diff options
author | Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> | 2010-03-03 14:30:00 +0000 |
---|---|---|
committer | Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> | 2010-03-12 14:07:43 +0100 |
commit | eb8bd37327b668ff964cbcd8b5b4a8be645c8de1 (patch) | |
tree | 50e62bc5edb3a54ce2a85e014246fc2a0a2dcd50 | |
parent | b38e68a0472b570879a1492b68ab3b95b157dbd0 (diff) | |
download | telepathy-logger-eb8bd37327b668ff964cbcd8b5b4a8be645c8de1.tar.gz |
TplLogEntry/TplLogEntryText: added several fields/props
pending_msg_id: the entry's pending msd_id or -1 if sent/ack'd
channel_path: the channel path relative to the channel originating the entry
log_id: changed type, from guint to (gchar *), to represent a literal token,
thus the constructor for TplLogEntry changed to follow the log_id type change.
Due to the type change, a small amend has been done on TplLogStoreEmapthy.
TplLogStoreDefault has been created since with the introduction of log_id as a
literal token, TplLogStoreEmpathy won't be compatible anymore with the current TPL default LS.
A utility function has been added to util.c, returning a message_token which
TPL clients can trust to be unique within TPL, this until Bug#26838 will be solved.
TplLogManager has been updated with the new TPL_LOG_STORE_DEFAULT instance.
TpLChannelText has been updated with the TplLogStoreText constructor update.
-rw-r--r-- | telepathy-logger/Makefile.am | 7 | ||||
-rw-r--r-- | telepathy-logger/channel-text.c | 31 | ||||
-rw-r--r-- | telepathy-logger/log-entry-text.c | 33 | ||||
-rw-r--r-- | telepathy-logger/log-entry-text.h | 7 | ||||
-rw-r--r-- | telepathy-logger/log-entry.c | 147 | ||||
-rw-r--r-- | telepathy-logger/log-entry.h | 20 | ||||
-rw-r--r-- | telepathy-logger/log-manager.c | 1 | ||||
-rw-r--r-- | telepathy-logger/log-store-default.c | 1275 | ||||
-rw-r--r-- | telepathy-logger/log-store-default.h | 62 | ||||
-rw-r--r-- | telepathy-logger/log-store-empathy.c | 261 | ||||
-rw-r--r-- | telepathy-logger/util.c | 44 | ||||
-rw-r--r-- | telepathy-logger/util.h | 4 |
12 files changed, 1609 insertions, 283 deletions
diff --git a/telepathy-logger/Makefile.am b/telepathy-logger/Makefile.am index 0410fc3..c4a64af 100644 --- a/telepathy-logger/Makefile.am +++ b/telepathy-logger/Makefile.am @@ -34,8 +34,9 @@ LIBTPL_HEADERS = \ log-manager.h \ log-manager-priv.h \ log-store.h \ - log-store-sqlite.h \ + log-store-default.h \ log-store-empathy.h \ + log-store-sqlite.h \ log-store-factory.h \ observer.h \ util.h \ @@ -55,10 +56,12 @@ libtelepathy_logger_la_SOURCES = \ log-entry-text.c \ log-manager.c \ log-store.c \ - log-store-sqlite.c \ + log-store-default.c \ log-store-empathy.c \ + log-store-sqlite.c \ log-store-factory.c \ observer.c \ + util.c \ $(NULL) libtelepathy_logger_la_LIBADD = \ diff --git a/telepathy-logger/channel-text.c b/telepathy-logger/channel-text.c index 598b28d..6b7c3a4 100644 --- a/telepathy-logger/channel-text.c +++ b/telepathy-logger/channel-text.c @@ -33,6 +33,7 @@ #include <telepathy-logger/observer.h> #include <telepathy-logger/log-entry-text.h> #include <telepathy-logger/log-manager-priv.h> +#include <telepathy-logger/datetime.h> #include <telepathy-logger/util.h> @@ -846,8 +847,11 @@ on_sent_signal_cb (TpChannel *proxy, TplContact *tpl_contact_receiver = NULL; TplLogEntryText *log; TplLogManager *logmanager; - const gchar *account_path; const gchar *chat_id; + const gchar *account_path; + const gchar *channel_path = tp_proxy_get_object_path (TP_PROXY (tpl_text)); + gchar *log_id = create_message_token (channel_path, + tpl_time_to_string_local (arg_Timestamp, "%Y%m%d"), G_MAXUINT); g_return_if_fail (TPL_IS_CHANNEL_TEXT (tpl_text)); @@ -865,7 +869,8 @@ on_sent_signal_cb (TpChannel *proxy, tpl_contact_receiver = tpl_contact_from_tp_contact (remote); tpl_contact_set_contact_type (tpl_contact_receiver, TPL_CONTACT_USER); - DEBUG ("sent:\n\tto=\"%s (%s)\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + DEBUG ("sent:\n\tlog_id=\"%s\"\n\tto=\"%s (%s)\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + log_id, tpl_contact_get_identifier (tpl_contact_receiver), tpl_contact_get_alias (tpl_contact_receiver), tpl_contact_get_identifier (tpl_contact_sender), @@ -875,7 +880,8 @@ on_sent_signal_cb (TpChannel *proxy, } else { - DEBUG ("sent:\n\tto chatroom=\"%s\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + DEBUG ("sent:\n\tlog_id=\"%s\"\n\tto chatroom=\"%s\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + log_id, tpl_channel_text_get_chatroom_id (tpl_text), tpl_contact_get_identifier (tpl_contact_sender), tpl_contact_get_alias (tpl_contact_sender), @@ -891,9 +897,12 @@ on_sent_signal_cb (TpChannel *proxy, account_path = tp_proxy_get_object_path ( TP_PROXY (tpl_channel_get_account (TPL_CHANNEL (tpl_text)))); - log = tpl_log_entry_text_new (arg_Timestamp, account_path, + log = tpl_log_entry_text_new (log_id, account_path, TPL_LOG_ENTRY_DIRECTION_OUT); + tpl_log_entry_set_pending_msg_id (TPL_LOG_ENTRY (log), + TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED); + tpl_log_entry_set_channel_path (TPL_LOG_ENTRY (log), channel_path); tpl_log_entry_text_set_chat_id (log, chat_id); tpl_log_entry_text_set_timestamp (log, (time_t) arg_Timestamp); tpl_log_entry_text_set_signal_type (log, TPL_LOG_ENTRY_TEXT_SIGNAL_SENT); @@ -923,6 +932,8 @@ on_sent_signal_cb (TpChannel *proxy, g_object_unref (tpl_contact_sender); g_object_unref (logmanager); g_object_unref (log); + + g_free (log_id); } @@ -994,7 +1005,8 @@ keepon_on_receiving_signal (TplLogEntryText *log) tpl_contact_receiver = tpl_contact_from_tp_contact (local); - DEBUG ("recvd:\n\tto=\"%s (%s)\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + DEBUG ("recvd:\n\tlog_id=\"%s\"\n\tto=\"%s (%s)\"\n\tfrom=\"%s (%s)\"\n\tmsg=\"%s\"", + tpl_log_entry_get_log_id (TPL_LOG_ENTRY (log)), tpl_contact_get_identifier (tpl_contact_receiver), tpl_contact_get_alias (tpl_contact_receiver), tpl_contact_get_identifier (tpl_contact_sender), @@ -1046,6 +1058,9 @@ on_received_signal_cb (TpChannel *proxy, TplLogEntryText *log; TpAccount *account = tpl_channel_get_account (TPL_CHANNEL (tpl_text)); const gchar *account_path = tp_proxy_get_object_path (TP_PROXY (account)); + const gchar *channel_path = tp_proxy_get_object_path (TP_PROXY (tpl_text)); + gchar *log_id = create_message_token (channel_path, + tpl_time_to_string_local (arg_Timestamp, "%Y%m%d"), arg_ID); /* TODO use the Message iface to check the delivery notification and handle it correctly */ @@ -1058,9 +1073,11 @@ on_received_signal_cb (TpChannel *proxy, } /* Initialize TplLogEntryText (part 1) - chat_id still unknown */ - log = tpl_log_entry_text_new (arg_ID, account_path, + log = tpl_log_entry_text_new (log_id, account_path, TPL_LOG_ENTRY_DIRECTION_IN); + tpl_log_entry_set_channel_path (TPL_LOG_ENTRY (log), channel_path); + tpl_log_entry_set_pending_msg_id (TPL_LOG_ENTRY (log), arg_ID); tpl_log_entry_text_set_tpl_channel_text (log, tpl_text); tpl_log_entry_text_set_message (log, arg_Text); tpl_log_entry_text_set_message_type (log, arg_Type); @@ -1083,7 +1100,7 @@ on_received_signal_cb (TpChannel *proxy, keepon_on_receiving_signal (log); g_object_unref (tpl_contact_receiver); + g_free (log_id); } - /* End of Signal's Callbacks */ diff --git a/telepathy-logger/log-entry-text.c b/telepathy-logger/log-entry-text.c index 830ea94..669a2b7 100644 --- a/telepathy-logger/log-entry-text.c +++ b/telepathy-logger/log-entry-text.c @@ -177,7 +177,7 @@ tpl_log_entry_text_init (TplLogEntryText * self) TplLogEntryText * -tpl_log_entry_text_new (guint log_id, +tpl_log_entry_text_new (const gchar *log_id, const gchar *account_path, TplLogEntryDirection direction) { @@ -363,7 +363,7 @@ tpl_log_entry_text_get_signal_type (TplLogEntryText *self) } -guint +const gchar * tpl_log_entry_text_get_log_id (TplLogEntryText *self) { TplLogEntry *logentry = TPL_LOG_ENTRY (self); @@ -371,6 +371,21 @@ tpl_log_entry_text_get_log_id (TplLogEntryText *self) } +gint64 +tpl_log_entry_text_get_pending_msg_id (TplLogEntryText *self) +{ + TplLogEntry *logentry = TPL_LOG_ENTRY (self); + return TPL_LOG_ENTRY_GET_CLASS (self)->get_pending_msg_id (logentry); +} + + +gboolean +tpl_log_entry_text_is_pending (TplLogEntry *self) +{ + TplLogEntry *logentry = TPL_LOG_ENTRY (self); + return TPL_LOG_ENTRY_GET_CLASS (self)->is_pending (logentry); +} + const gchar * tpl_log_entry_text_get_chat_id (TplLogEntryText *self) { @@ -457,6 +472,15 @@ tpl_log_entry_text_set_receiver (TplLogEntryText *self, } +void +tpl_log_entry_text_set_pending_msg_id (TplLogEntryText *self, + gint64 data) +{ + TplLogEntry *logentry = TPL_LOG_ENTRY (self); + TPL_LOG_ENTRY_GET_CLASS (logentry)->set_pending_msg_id (logentry, data); +} + + gboolean tpl_log_entry_text_equal (TplLogEntry *message1, TplLogEntry *message2) @@ -470,7 +494,6 @@ tpl_log_entry_text_equal (TplLogEntry *message1, if (!tp_strdiff (priv1->entry.text->message, priv2->entry.text->message)) { } */ - DEBUG ("TODO: do a tpl_log_entry_equal rewrite!"); - return tpl_log_entry_text_get_log_id (TPL_LOG_ENTRY_TEXT (message1)) == - tpl_log_entry_text_get_log_id ( TPL_LOG_ENTRY_TEXT (message2)); + return !tp_strdiff (tpl_log_entry_get_log_id (message1), + tpl_log_entry_get_log_id (message2)); } diff --git a/telepathy-logger/log-entry-text.h b/telepathy-logger/log-entry-text.h index fe56a37..c987813 100644 --- a/telepathy-logger/log-entry-text.h +++ b/telepathy-logger/log-entry-text.h @@ -64,7 +64,7 @@ typedef struct GType tpl_log_entry_text_get_type (void); -TplLogEntryText *tpl_log_entry_text_new (guint log_id, +TplLogEntryText *tpl_log_entry_text_new (const gchar* log_id, const gchar *account_path, TplLogEntryDirection direction); TpChannelTextMessageType tpl_log_entry_text_message_type_from_str ( @@ -92,7 +92,9 @@ void tpl_log_entry_text_set_chatroom (TplLogEntryText *self, gboolean data); time_t tpl_log_entry_text_get_timestamp (TplLogEntryText *self); TplLogEntrySignalType tpl_log_entry_text_get_signal_type ( TplLogEntryText *self); -guint tpl_log_entry_text_get_log_id (TplLogEntryText *self); +const gchar *tpl_log_entry_text_get_log_id (TplLogEntryText *self); +gint64 tpl_log_entry_text_get_pending_msg_id (TplLogEntryText *self); +gboolean tpl_log_entry_text_is_pending (TplLogEntry *self); const gchar *tpl_log_entry_text_get_chat_id (TplLogEntryText *self); TplLogEntryDirection tpl_log_entry_text_get_direction (TplLogEntryText *self); TplContact *tpl_log_entry_text_get_sender (TplLogEntryText *self); @@ -108,6 +110,7 @@ void tpl_log_entry_text_set_chat_id (TplLogEntryText *self, 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_pending_msg_id (TplLogEntryText *self, gint64 data); gboolean tpl_log_entry_text_equal (TplLogEntry *message1, TplLogEntry *message2); /* Methods inherited by TplLogEntry */ diff --git a/telepathy-logger/log-entry.c b/telepathy-logger/log-entry.c index 88caee0..3b51464 100644 --- a/telepathy-logger/log-entry.c +++ b/telepathy-logger/log-entry.c @@ -43,19 +43,20 @@ G_DEFINE_ABSTRACT_TYPE (TplLogEntry, tpl_log_entry, G_TYPE_OBJECT) -static void tpl_log_entry_set_log_id (TplLogEntry *self, guint data); +static void tpl_log_entry_set_log_id (TplLogEntry *self, const gchar *data); static void tpl_log_entry_set_account_path (TplLogEntry *self, const gchar *data); - #define GET_PRIV(obj) TPL_GET_PRIV (obj, TplLogEntry) struct _TplLogEntryPriv { - guint log_id; + gchar *log_id; gint64 timestamp; TplLogEntrySignalType signal_type; gchar *chat_id; gchar *account_path; + gchar *channel_path; + gint64 pending_msg_id; /* incoming/outgoing */ TplLogEntryDirection direction; @@ -70,9 +71,11 @@ enum { PROP_TIMESTAMP = 1, PROP_SIGNAL_TYPE, PROP_LOG_ID, + PROP_PENDING_MSG_ID, PROP_DIRECTION, PROP_CHAT_ID, PROP_ACCOUNT_PATH, + PROP_CHANNEL_PATH, PROP_SENDER, PROP_RECEIVER }; @@ -129,8 +132,11 @@ tpl_log_entry_get_property (GObject *object, case PROP_SIGNAL_TYPE: g_value_set_uint (value, priv->signal_type); break; + case PROP_PENDING_MSG_ID: + g_value_set_uint (value, priv->pending_msg_id); + break; case PROP_LOG_ID: - g_value_set_uint (value, priv->log_id); + g_value_set_string (value, priv->log_id); break; case PROP_DIRECTION: g_value_set_uint (value, priv->direction); @@ -141,6 +147,9 @@ tpl_log_entry_get_property (GObject *object, case PROP_ACCOUNT_PATH: g_value_set_string (value, priv->account_path); break; + case PROP_CHANNEL_PATH: + g_value_set_string (value, priv->channel_path); + break; case PROP_SENDER: g_value_set_object (value, priv->sender); break; @@ -169,8 +178,11 @@ tpl_log_entry_set_property (GObject *object, case PROP_SIGNAL_TYPE: tpl_log_entry_set_signal_type (self, g_value_get_uint (value)); break; + case PROP_PENDING_MSG_ID: + tpl_log_entry_set_pending_msg_id (self, g_value_get_uint (value)); + break; case PROP_LOG_ID: - tpl_log_entry_set_log_id (self, g_value_get_uint (value)); + tpl_log_entry_set_log_id (self, g_value_get_string (value)); break; case PROP_DIRECTION: tpl_log_entry_set_direction (self, g_value_get_uint (value)); @@ -181,6 +193,9 @@ tpl_log_entry_set_property (GObject *object, case PROP_ACCOUNT_PATH: tpl_log_entry_set_account_path (self, g_value_get_string (value)); break; + case PROP_CHANNEL_PATH: + tpl_log_entry_set_channel_path (self, g_value_get_string (value)); + break; case PROP_SENDER: tpl_log_entry_set_sender (self, g_value_get_object (value)); break; @@ -213,6 +228,8 @@ tpl_log_entry_class_init (TplLogEntryClass *klass) klass->get_sender = tpl_log_entry_get_sender; klass->get_receiver = tpl_log_entry_get_receiver; klass->get_chat_id = tpl_log_entry_get_chat_id; + klass->get_pending_msg_id = tpl_log_entry_get_pending_msg_id; + klass->is_pending = tpl_log_entry_is_pending; klass->equal = NULL; klass->set_timestamp = tpl_log_entry_set_timestamp; @@ -221,6 +238,7 @@ tpl_log_entry_class_init (TplLogEntryClass *klass) klass->set_sender = tpl_log_entry_set_sender; klass->set_receiver = tpl_log_entry_set_receiver; klass->set_chat_id = tpl_log_entry_set_chat_id; + klass->set_pending_msg_id = tpl_log_entry_set_pending_msg_id; param_spec = g_param_spec_uint ("timestamp", "Timestamp", @@ -235,11 +253,35 @@ tpl_log_entry_class_init (TplLogEntryClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_SIGNAL_TYPE, param_spec); - param_spec = g_param_spec_uint ("log-id", + /** + * TplLogEntry::pending-msg-id: + * + * The pending message id for the current log entry. + * The construction-time value if #TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED, + * meaning that the log entry is considered already acknoledged. + * An object instantiating a TplLogEntry subclass should explicitly set ths + * + * The pending message id value is only meaningful when associated to the + * #TplLogEntry::channel-path property. + * The couple (channel-path, pending-msg-id) cannot be considered unique. + * Use #TplLogEntry::log-id for a TPL-unique identifier. + */ + param_spec = g_param_spec_int64 ("pending-msg-id", + "PendingMessageId", + "Pending Message ID, if set, the log entry is set as pending for ACK." + " Default to -1 meaning not pending.", + -1, G_MAXUINT32, TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_PENDING_MSG_ID, + param_spec); + + param_spec = g_param_spec_string ("log-id", "LogId", - "Log identification number: the triple LogId+AccountName+ChatId is unique", - 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); + "Log identification token, it's unique among existing LogEntry, if two " + "messages have the same token, they are the same entry (maybe logged " + "by two different TplLogStore)", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_LOG_ID, param_spec); param_spec = g_param_spec_uint ("direction", @@ -266,6 +308,13 @@ tpl_log_entry_class_init (TplLogEntryClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ACCOUNT_PATH, param_spec); + param_spec = g_param_spec_string ("channel-path", + "ChannelPath", + "The channel path of the TpChannel to which the log entry is related", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CHANNEL_PATH, param_spec); + param_spec = g_param_spec_object ("sender", "Sender", "TplContact instance who originated the log entry", @@ -303,6 +352,25 @@ tpl_log_entry_get_timestamp (TplLogEntry *self) } +gint64 +tpl_log_entry_get_pending_msg_id (TplLogEntry *self) +{ + TplLogEntryPriv *priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), -1); + + priv = GET_PRIV (self); + return priv->pending_msg_id; +} + + +gboolean +tpl_log_entry_is_pending (TplLogEntry *self) +{ + return (tpl_log_entry_get_pending_msg_id (self) != + TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED); +} + TplLogEntrySignalType tpl_log_entry_get_signal_type (TplLogEntry *self) { @@ -315,7 +383,7 @@ tpl_log_entry_get_signal_type (TplLogEntry *self) } -guint +const gchar * tpl_log_entry_get_log_id (TplLogEntry *self) { TplLogEntryPriv *priv; @@ -388,6 +456,18 @@ tpl_log_entry_get_account_path (TplLogEntry *self) } +const gchar * +tpl_log_entry_get_channel_path (TplLogEntry *self) +{ + TplLogEntryPriv *priv; + + g_return_val_if_fail (TPL_IS_LOG_ENTRY (self), NULL); + + priv = GET_PRIV (self); + return priv->channel_path; +} + + void tpl_log_entry_set_timestamp (TplLogEntry *self, gint64 data) @@ -415,17 +495,42 @@ tpl_log_entry_set_signal_type (TplLogEntry *self, g_object_notify (G_OBJECT (self), "signal-type"); } +/** + * tpl_log_entry_set_pending_msg_id: + * @self: TplLogentry instance + * @data: the pending message ID + * + * Sets the value of the pending message id, or + * #TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED to set @self as aknowledged. + */ +void +tpl_log_entry_set_pending_msg_id (TplLogEntry *self, + gint64 data) +{ + TplLogEntryPriv *priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_ENTRY (self)); + + priv = GET_PRIV (self); + priv->pending_msg_id = data; + g_object_notify (G_OBJECT (self), "pending-msg-id"); +} + + /* set just on construction time */ static void tpl_log_entry_set_log_id (TplLogEntry *self, - guint data) + const gchar* data) { TplLogEntryPriv *priv; + priv = GET_PRIV (self); + g_return_if_fail (TPL_IS_LOG_ENTRY (self)); + g_return_if_fail (!TPL_STR_EMPTY (data)); + g_return_if_fail (priv->log_id == NULL); - priv = GET_PRIV (self); - priv->log_id = data; + priv->log_id = g_strdup (data); g_object_notify (G_OBJECT (self), "log-id"); } @@ -513,6 +618,22 @@ tpl_log_entry_set_account_path (TplLogEntry *self, } +void +tpl_log_entry_set_channel_path (TplLogEntry *self, + const gchar *data) +{ + TplLogEntryPriv *priv; + + priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_ENTRY (self)); + g_return_if_fail (!TPL_STR_EMPTY (data)); + g_return_if_fail (priv->channel_path == NULL); + + priv->channel_path = g_strdup (data); + g_object_notify (G_OBJECT (self), "channel-path"); +} + /** * log_entry: * @self: TplLogEntry subclass instance diff --git a/telepathy-logger/log-entry.h b/telepathy-logger/log-entry.h index 187cc89..9a4f5b7 100644 --- a/telepathy-logger/log-entry.h +++ b/telepathy-logger/log-entry.h @@ -34,6 +34,9 @@ G_BEGIN_DECLS #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)) +/* value used to identify a ack'd message with the 'pending-msg-id' prop */ +#define TPL_LOG_ENTRY_MSG_ID_ACKNOWLEDGED -1 + typedef enum { TPL_LOG_ENTRY_DIRECTION_NONE = 0, @@ -80,46 +83,53 @@ typedef struct void (*finalize) (GObject *obj); gint64 (*get_timestamp) (TplLogEntry *self); + gint64 (*get_pending_msg_id) (TplLogEntry *self); + gboolean (*is_pending) (TplLogEntry *self); TplLogEntrySignalType (*get_signal_type) (TplLogEntry *self); - guint (*get_log_id) (TplLogEntry *self); + const gchar* (*get_log_id) (TplLogEntry *self); TplLogEntryDirection (*get_direction) (TplLogEntry *self); TplContact * (*get_sender) (TplLogEntry *self); TplContact * (*get_receiver) (TplLogEntry *self); const gchar * (*get_chat_id) (TplLogEntry *self); const gchar * (*get_account_path) (TplLogEntry *self); + const gchar * (*get_channel_path) (TplLogEntry *self); void (*set_timestamp) (TplLogEntry *self, gint64 data); + void (*set_pending_msg_id) (TplLogEntry *self, gint64 data); void (*set_signal_type) (TplLogEntry *self, TplLogEntrySignalType data); void (*set_log_id) (TplLogEntry *self, guint data); void (*set_direction) (TplLogEntry *self, TplLogEntryDirection data); void (*set_sender) (TplLogEntry *self, TplContact *data); void (*set_receiver) (TplLogEntry *self, TplContact *data); void (*set_chat_id) (TplLogEntry *self, const gchar *data); + void (*set_channel_path) (TplLogEntry *self, const gchar *data); /* to be implemented only by subclasses */ gboolean (*equal) (TplLogEntry *entry1, TplLogEntry *entry2); } TplLogEntryClass; GType tpl_log_entry_get_type (void); -TplLogEntry *tpl_log_entry_new (guint log_id, const gchar *chat_id, - TplLogEntryDirection direction); - gint64 tpl_log_entry_get_timestamp (TplLogEntry *self); +gint64 tpl_log_entry_get_pending_msg_id (TplLogEntry *self); +gboolean tpl_log_entry_is_pending (TplLogEntry *self); TplLogEntrySignalType tpl_log_entry_get_signal_type (TplLogEntry *self); -guint tpl_log_entry_get_log_id (TplLogEntry *self); +const gchar* tpl_log_entry_get_log_id (TplLogEntry *self); const gchar *tpl_log_entry_get_chat_id (TplLogEntry * self); const gchar *tpl_log_entry_get_account_path (TplLogEntry *self); +const gchar *tpl_log_entry_get_channel_path (TplLogEntry *self); TplLogEntryDirection tpl_log_entry_get_direction (TplLogEntry *self); TplContact *tpl_log_entry_get_sender (TplLogEntry *self); TplContact *tpl_log_entry_get_receiver (TplLogEntry *self); void tpl_log_entry_set_timestamp (TplLogEntry *self, gint64 data); +void tpl_log_entry_set_pending_msg_id (TplLogEntry *self, gint64 data); void tpl_log_entry_set_signal_type (TplLogEntry *self, TplLogEntrySignalType data); void tpl_log_entry_set_direction (TplLogEntry *self, TplLogEntryDirection data); void tpl_log_entry_set_chat_id (TplLogEntry *self, const gchar *data); +void tpl_log_entry_set_channel_path (TplLogEntry *self, const gchar *data); void tpl_log_entry_set_sender (TplLogEntry *self, TplContact *data); void tpl_log_entry_set_receiver (TplLogEntry *self, TplContact *data); diff --git a/telepathy-logger/log-manager.c b/telepathy-logger/log-manager.c index ab34c77..f989ba0 100644 --- a/telepathy-logger/log-manager.c +++ b/telepathy-logger/log-manager.c @@ -37,6 +37,7 @@ #include <telepathy-logger/log-entry.h> #include <telepathy-logger/log-store.h> +#include <telepathy-logger/log-store-default.h> #include <telepathy-logger/log-store-empathy.h> #include <telepathy-logger/log-store-sqlite.h> #include <telepathy-logger/datetime.h> diff --git a/telepathy-logger/log-store-default.c b/telepathy-logger/log-store-default.c new file mode 100644 index 0000000..393f3ab --- /dev/null +++ b/telepathy-logger/log-store-default.c @@ -0,0 +1,1275 @@ +/* -*- 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> + * Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + */ + +#include "config.h" +#include "log-store-default.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/dbus.h> +#include <telepathy-glib/defs.h> +#include <telepathy-glib/util.h> + +#include <telepathy-logger/contact.h> +#include <telepathy-logger/log-entry-text.h> +#include <telepathy-logger/log-manager.h> +#include <telepathy-logger/log-store.h> +#include <telepathy-logger/datetime.h> +#include <telepathy-logger/util.h> + +#define DEBUG_FLAG TPL_DEBUG_LOG_STORE +#include <telepathy-logger/debug.h> + +#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=\"default-log.xsl\"?>\n" \ + "<log>\n" + +#define LOG_FOOTER \ + "</log>\n" + + +#define GET_PRIV(obj) TPL_GET_PRIV (obj, TplLogStoreDefault) +typedef struct +{ + gchar *basedir; + gchar *name; + gboolean readable; + gboolean writable; + TpAccountManager *account_manager; +} TplLogStoreDefaultPriv; + +enum { + PROP_0, + PROP_NAME, + PROP_READABLE, + PROP_WRITABLE, + PROP_BASEDIR +}; + +static void log_store_iface_init (gpointer g_iface, gpointer iface_data); +static void tpl_log_store_get_property (GObject *object, guint param_id, GValue *value, + GParamSpec *pspec); +static void tpl_log_store_set_property (GObject *object, guint param_id, const GValue *value, + GParamSpec *pspec); +static const gchar *log_store_default_get_name (TplLogStore *self); +static void log_store_default_set_name (TplLogStore *self, const gchar *data); +static const gchar *log_store_default_get_basedir (TplLogStore *self); +static void log_store_default_set_basedir (TplLogStore *self, + const gchar *data); +static void log_store_default_set_writable (TplLogStore *self, gboolean data); +static void log_store_default_set_readable (TplLogStore *self, gboolean data); + + +G_DEFINE_TYPE_WITH_CODE (TplLogStoreDefault, tpl_log_store_default, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TPL_TYPE_LOG_STORE, log_store_iface_init)) + +static void +log_store_default_dispose (GObject *object) +{ + TplLogStoreDefault *self = TPL_LOG_STORE_DEFAULT (object); + TplLogStoreDefaultPriv *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 */ + if (priv->account_manager != NULL) + { + g_object_unref (priv->account_manager); + priv->account_manager = NULL; + } +} + + +static void +log_store_default_finalize (GObject *object) +{ + TplLogStoreDefault *self = TPL_LOG_STORE_DEFAULT (object); + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + if (priv->basedir != NULL) + { + g_free (priv->basedir); + priv->basedir = NULL; + } + if (priv->name != NULL) + { + g_free (priv->name); + priv->name = NULL; + } +} + + +static void +tpl_log_store_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (object); + + switch (param_id) + { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_WRITABLE: + g_value_set_boolean (value, priv->writable); + break; + case PROP_READABLE: + g_value_set_boolean (value, priv->readable); + break; + case PROP_BASEDIR: + g_value_set_string (value, priv->basedir); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + + +static void +tpl_log_store_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + TplLogStore *self = TPL_LOG_STORE (object); + + switch (param_id) + { + case PROP_NAME: + log_store_default_set_name (self, g_value_get_string (value)); + break; + case PROP_READABLE: + log_store_default_set_readable (self, g_value_get_boolean (value)); + break; + case PROP_WRITABLE: + log_store_default_set_writable (self, g_value_get_boolean (value)); + break; + case PROP_BASEDIR: + log_store_default_set_basedir (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + + +static void +tpl_log_store_default_class_init (TplLogStoreDefaultClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *param_spec; + + object_class->finalize = log_store_default_finalize; + object_class->dispose = log_store_default_dispose; + object_class->get_property = tpl_log_store_get_property; + object_class->set_property = tpl_log_store_set_property; + + /** + * TplLogStoreDefault:name: + * + * The log store's name. No default available, it has to be passed at object + * creation. + * As defined in #TplLogStore. + */ + param_spec = g_param_spec_string ("name", + "Name", + "The TplLogStore implementation's name", + NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_NAME, param_spec); + + /* the default value for the basedir prop is composed by user_data_dir () + + * prop "name" value, it's not possible to know it at param_spec time, so + * it's set to NULL and let to get_basedir to set it to its default if + * priv->basedir == NULL + */ + + /** + * TplLogStoreDefault:basedir: + * + * The log store's basedir. + */ + param_spec = g_param_spec_string ("basedir", + "Basedir", + "The TplLogStore implementation's name", + NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_BASEDIR, param_spec); + + /** + * TplLogStoreDefault:readable: + * + * Wether the log store is readable. + * Default: %TRUE + * + * As defined in #TplLogStore. + */ + param_spec = g_param_spec_boolean ("readable", + "Readable", + "Defines wether the LogStore is readable or not, allowing searching " + "into this instance", + TRUE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_READABLE, param_spec); + + /** + * TplLogStoreDefault:writable: + * + * Wether the log store is writable. + * Default: %FALSE. + * Setting a LogStore to %TRUE might result in duplicate entries among logs. + * + * As defined in #TplLogStore. + */ + param_spec = g_param_spec_boolean ("writable", + "Writable", + "Defines wether the LogStore is writable or not, allowing message " + "to be stored into this instance", + FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_WRITABLE, param_spec); + + g_type_class_add_private (object_class, sizeof (TplLogStoreDefaultPriv)); +} + + +static void +tpl_log_store_default_init (TplLogStoreDefault *self) +{ + TplLogStoreDefaultPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TPL_TYPE_LOG_STORE_DEFAULT, TplLogStoreDefaultPriv); + + self->priv = priv; + 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), "/", '_'); +} + +/* chat_id can be NULL, but if present have to be a non zero-lenght string. + * If NULL, the returned dir will be composed until the account part. + * If non-NULL, the returned dir will be composed until the chat_id part */ +static gchar * +log_store_default_get_dir (TplLogStore *self, + TpAccount *account, + const gchar *chat_id, + gboolean chatroom) +{ + gchar *basedir; + gchar *escaped; + TplLogStoreDefaultPriv *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, but not empthy string if not-NULL */ + g_return_val_if_fail ((chat_id == NULL) || (*chat_id != '\0'), NULL); + + priv = GET_PRIV (self); + + escaped = log_store_account_to_dirname (account); + + if (chatroom) + basedir = g_build_path (G_DIR_SEPARATOR_S, + log_store_default_get_basedir (self), escaped, LOG_DIR_CHATROOMS, + chat_id, NULL); + else + basedir = g_build_path (G_DIR_SEPARATOR_S, + log_store_default_get_basedir (self), escaped, chat_id, NULL); + + g_free (escaped); + + return basedir; +} + + +static gchar * +log_store_default_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_default_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_default_get_filename (TplLogStore *self, + TpAccount *account, + const gchar *chat_id, + gboolean chatroom) +{ + gchar *chatid_dir; + gchar *timestamp; + gchar *filename; + gchar *esc_chat_id; + + /* avoid that 1-1 conversation generated from a chatroom, having id similar + * to room@conference.domain/My_Alias (in XMPP) are threated as a directory + * path, creating My_Alias as a subdirectory of room@conference.domain */ + esc_chat_id = g_strdelimit (g_strdup (chat_id), "/", '_'); + chatid_dir = log_store_default_get_dir (self, account, esc_chat_id, + chatroom); + timestamp = log_store_default_get_timestamp_filename (); + filename = g_build_filename (chatid_dir, timestamp, NULL); + + g_free (esc_chat_id); + g_free (chatid_dir); + g_free (timestamp); + + return filename; +} + + +/* this is a method used at the end of the add_message process, used by any + * LogEntry<Type> instance. it should the only method allowed to write to the + * store */ +static gboolean +_log_store_default_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_default_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); + + 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, "%s", entry); + /*DEBUG ("%s: written: %s", filename, entry);*/ + + fclose (file); + g_free (filename); + return TRUE; +} + + +static gboolean +add_message_text_chat (TplLogStore *self, + TplLogEntryText *message, + GError **error) +{ + gboolean ret = FALSE; + TpDBusDaemon *bus_daemon; + TpAccount *account; + TplContact *sender; + const gchar *body_str; + gchar *avatar_token = NULL; + gchar *body; + gchar *timestamp; + gchar *contact_name; + gchar *contact_id; + gchar *entry; + TpChannelTextMessageType msg_type; + + bus_daemon = tp_dbus_daemon_dup (error); + if (bus_daemon == NULL) + { + DEBUG ("Error acquiring bus daemon: %s", (*error)->message); + goto out; + } + + account = tp_account_new (bus_daemon, + tpl_log_entry_get_account_path (TPL_LOG_ENTRY (message)), error); + if (account == NULL) + { + DEBUG ("Error acquiring TpAccount proxy: %s", (*error)->message); + goto out; + } + + body_str = tpl_log_entry_text_get_message (message); + if (TPL_STR_EMPTY (body_str)) + goto out; + + body = g_markup_escape_text (body_str, -1); + msg_type = tpl_log_entry_text_get_message_type (message); + timestamp = log_store_default_get_timestamp_from_message ( + TPL_LOG_ENTRY (message)); + + sender = tpl_log_entry_get_sender (TPL_LOG_ENTRY (message)); + contact_name = g_markup_escape_text (tpl_contact_get_alias (sender), -1); + contact_id = g_markup_escape_text (tpl_contact_get_identifier (sender), -1); + avatar_token = g_markup_escape_text (tpl_contact_get_avatar_token (sender), -1); + + entry = g_strdup_printf ("<message time='%s' cm_id='%s' id='%s' name='%s' " + "token='%s' isuser='%s' type='%s'>" + "%s</message>\n" LOG_FOOTER, timestamp, + tpl_log_entry_get_log_id (TPL_LOG_ENTRY (message)), + contact_id, contact_name, + avatar_token ? avatar_token : "", + tpl_contact_get_contact_type (sender) == + TPL_CONTACT_USER ? "true" : "false", + tpl_log_entry_text_message_type_to_str (msg_type), + body); + + DEBUG ("writing %s from %s (ts %s)", + tpl_log_entry_get_log_id (TPL_LOG_ENTRY (message)), + contact_id, timestamp); + + ret = _log_store_default_write_to_store (self, account, + tpl_log_entry_get_chat_id (TPL_LOG_ENTRY (message)), + tpl_log_entry_text_is_chatroom (message), + entry, error); + +out: + g_free (contact_id); + g_free (contact_name); + g_free (timestamp); + g_free (body); + g_free (entry); + g_free (avatar_token); + + if (bus_daemon != NULL) + g_object_unref (bus_daemon); + if (account != NULL) + g_object_unref (account); + + return ret; +} + + +static gboolean +add_message_text (TplLogStore *self, + TplLogEntryText *message, + GError **error) +{ + TplLogEntryTextSignalType signal_type; + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), FALSE); + g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (message), FALSE); + + signal_type = tpl_log_entry_get_signal_type (TPL_LOG_ENTRY (message)); + + switch (signal_type) + { + case TPL_LOG_ENTRY_TEXT_SIGNAL_SENT: + case TPL_LOG_ENTRY_TEXT_SIGNAL_RECEIVED: + return add_message_text_chat (self, message, error); + break; + case TPL_LOG_ENTRY_TEXT_SIGNAL_CHAT_STATUS_CHANGED: + g_warning ("STATUS_CHANGED log entry not currently handled"); + return FALSE; + 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_default_add_message (TplLogStore *self, + TplLogEntry *message, + GError **error) +{ + g_return_val_if_fail (TPL_IS_LOG_ENTRY (message), FALSE); + + switch (tpl_log_entry_get_signal_type (TPL_LOG_ENTRY (message))) + { + case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_SENT: + case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_RECEIVED: + case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_SEND_ERROR: + case TPL_LOG_ENTRY_CHANELL_TEXT_SIGNAL_LOST_MESSAGE: + case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_CHAT_STATUS_CHANGED: + return add_message_text (self, TPL_LOG_ENTRY_TEXT (message), error); + default: + g_set_error (error, TPL_LOG_STORE_ERROR, + TPL_LOG_STORE_ERROR_ADD_MESSAGE, + "LogEntrySignalType not handled by this LogStore (%s)", + log_store_default_get_name (self)); + return FALSE; + } +} + + +static gboolean +log_store_default_exists (TplLogStore *self, + TpAccount *account, + const gchar *chat_id, + gboolean chatroom) +{ + gchar *dir; + gboolean exists; + + g_return_val_if_fail (TPL_IS_LOG_STORE (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_default_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_default_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_default_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_default_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_default_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_default_search_hit_new (TplLogStore *self, + const gchar *filename) +{ + TplLogStoreDefaultPriv *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 = g_object_ref (account); + } + g_free (name); + } + g_list_free (accounts); + + hit->filename = g_strdup (filename); + + g_strfreev (strv); + + return hit; +} + +/* returns a Glist of TplLogEntryText instances */ +static GList * +log_store_default_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 (TP_IS_ACCOUNT (account), 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) + { + TplLogEntryText *message; + 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 = (!tp_strdiff (is_user_str, "true")); + + 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); + else + cm_id = 0; + + t = tpl_time_parse (time_); + + sender = tpl_contact_new (sender_id); + tpl_contact_set_account (sender, account); + tpl_contact_set_alias (sender, sender_name); + tpl_contact_set_avatar_token (sender, sender_avatar_token); + + message = tpl_log_entry_text_new (cm_id_str, + tp_proxy_get_object_path (account), TPL_LOG_ENTRY_DIRECTION_NONE); + tpl_log_entry_set_sender (TPL_LOG_ENTRY (message), sender); + tpl_log_entry_set_timestamp (TPL_LOG_ENTRY (message), t); + tpl_log_entry_text_set_message (message, body); + tpl_log_entry_text_set_message_type (message, msg_type); + /* TODO uderstand if useful + tpl_log_entry_text_set_is_backlog (message, TRUE); + */ + + 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; +} + + +/* If dir is NULL, basedir will be used instead. + * Used to make possible the full search vs. specific subtrees search */ +static GList * +log_store_default_get_all_files (TplLogStore *self, + const gchar *dir) +{ + GDir *gdir; + GList *files = NULL; + const gchar *name; + const gchar *basedir; + TplLogStoreDefaultPriv *priv; + + priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_STORE (self), NULL); + /* dir can be NULL, do not check :-) */ + + basedir = (dir != NULL) ? dir : log_store_default_get_basedir (self); + + 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_default_get_all_files (self, + filename)); + } + + g_free (filename); + } + + g_dir_close (gdir); + + return files; +} + + +static GList * +_log_store_default_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_default_search_hit_new (self, filename); + if (hit != NULL) + { + 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_default_search_in_identifier_chats_new (TplLogStore *self, + TpAccount *account, + gchar const *identifier, + const gchar *text) +{ + GList *files; + gchar *dir, *account_dir; + + 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, log_store_default_get_basedir (self), + account_dir, identifier, NULL); + + files = log_store_default_get_all_files (self, dir); + DEBUG ("Found %d log files in total", g_list_length (files)); + + return _log_store_default_search_in_files (self, text, files); +} + + + +static GList * +log_store_default_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_default_get_all_files (self, NULL); + DEBUG ("Found %d log files in total", g_list_length (files)); + + return _log_store_default_search_in_files (self, text, files); +} + +/* Returns: (GList *) of (TplLogSearchHit *) */ +static GList * +log_store_default_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_default_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; +} + + +/* returns a Glist of TplLogEntryText instances */ +static GList * +log_store_default_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_default_get_filename_for_date (self, account, chat_id, + chatroom, date); + messages = log_store_default_get_messages_for_file (self, account, + filename); + g_free (filename); + + return messages; +} + + +static GList * +log_store_default_get_chats (TplLogStore *self, + TpAccount *account) +{ + gchar *dir; + GList *hits; + TplLogStoreDefaultPriv *priv; + + priv = GET_PRIV (self); + + dir = log_store_default_get_dir (self, account, NULL, FALSE); + hits = log_store_default_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_default_get_name (TplLogStore *self) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_STORE_DEFAULT (self), NULL); + + return priv->name; +} + + +/* returns am absolute path for the base directory of LogStore */ +static const gchar * +log_store_default_get_basedir (TplLogStore *self) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_val_if_fail (TPL_IS_LOG_STORE_DEFAULT (self), NULL); + + /* set default based on name if NULL, see prop's comment about it in + * class_init method */ + if (priv->basedir == NULL) + { + gchar *dir; + + dir = g_build_path (G_DIR_SEPARATOR_S, g_get_user_data_dir (), + log_store_default_get_name (self), "logs", NULL); + log_store_default_set_basedir (self, dir); + g_free (dir); + } + + return priv->basedir; +} + + +static void +log_store_default_set_name (TplLogStore *self, + const gchar *data) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_STORE_DEFAULT (self)); + g_return_if_fail (!TPL_STR_EMPTY (data)); + g_return_if_fail (priv->name == NULL); + + priv->name = g_strdup (data); +} + +static void +log_store_default_set_basedir (TplLogStore *self, + const gchar *data) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_STORE_DEFAULT (self)); + g_return_if_fail (priv->basedir == NULL); + /* data may be NULL when the class is initialized and the default value is + * set */ + + priv->basedir = g_strdup (data); + + /* at install_spec time, default value is set to NULL, ignore it */ + if (priv->basedir != NULL) + DEBUG ("logstore set to dir: %s", data); +} + + +static void +log_store_default_set_readable (TplLogStore *self, + gboolean data) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_STORE_DEFAULT (self)); + + priv->readable = data; +} + + +static void +log_store_default_set_writable (TplLogStore *self, + gboolean data) +{ + TplLogStoreDefaultPriv *priv = GET_PRIV (self); + + g_return_if_fail (TPL_IS_LOG_STORE_DEFAULT (self)); + + priv->writable = data; +} + + +static GList * +log_store_default_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_default_get_dates (self, account, chat_id, chatroom); + + for (l = g_list_last (dates); l != NULL && 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_default_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_default_get_name; + iface->exists = log_store_default_exists; + iface->add_message = log_store_default_add_message; + iface->get_dates = log_store_default_get_dates; + iface->get_messages_for_date = log_store_default_get_messages_for_date; + iface->get_chats = log_store_default_get_chats; + iface->search_in_identifier_chats_new = + log_store_default_search_in_identifier_chats_new; + iface->search_new = log_store_default_search_new; + iface->get_filtered_messages = log_store_default_get_filtered_messages; +} diff --git a/telepathy-logger/log-store-default.h b/telepathy-logger/log-store-default.h new file mode 100644 index 0000000..e7029fa --- /dev/null +++ b/telepathy-logger/log-store-default.h @@ -0,0 +1,62 @@ +/* -*- 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_DEFAULT_H__ +#define __TPL_LOG_STORE_DEFAULT_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS +#define TPL_TYPE_LOG_STORE_DEFAULT \ + (tpl_log_store_default_get_type ()) +#define TPL_LOG_STORE_DEFAULT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPL_TYPE_LOG_STORE_DEFAULT, \ + TplLogStoreDefault)) +#define TPL_LOG_STORE_DEFAULT_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_CAST ((vtable), TPL_TYPE_LOG_STORE_DEFAULT, \ + TplLogStoreDefaultClass)) +#define TPL_IS_LOG_STORE_DEFAULT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPL_TYPE_LOG_STORE_DEFAULT)) +#define TPL_IS_LOG_STORE_DEFAULT_CLASS(vtable) \ + (G_TYPE_CHECK_CLASS_TYPE ((vtable), TPL_TYPE_LOG_STORE_DEFAULT)) +#define TPL_LOG_STORE_DEFAULT_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_CLASS ((inst), TPL_TYPE_LOG_STORE_DEFAULT, \ + TplLogStoreDefaultClass)) + +typedef struct TplLogStoreDefault +{ + GObject parent; + gpointer priv; +} TplLogStoreDefault; + +typedef struct +{ + GObjectClass parent; +} TplLogStoreDefaultClass; + +GType tpl_log_store_default_get_type (void); + +G_END_DECLS +#endif /* __TPL_LOG_STORE_DEFAULT_H__ */ diff --git a/telepathy-logger/log-store-empathy.c b/telepathy-logger/log-store-empathy.c index 0c0b89e..59df2b6 100644 --- a/telepathy-logger/log-store-empathy.c +++ b/telepathy-logger/log-store-empathy.c @@ -292,252 +292,6 @@ log_store_empathy_get_dir (TplLogStore *self, } -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 *chatid_dir; - gchar *timestamp; - gchar *filename; - gchar *esc_chat_id; - - /* avoid that 1-1 conversation generated from a chatroom, having id similar - * to room@conference.domain/My_Alias (in XMPP) are threated as a directory - * path, creating My_Alias as a subdirectory of room@conference.domain */ - esc_chat_id = g_strdelimit (g_strdup (chat_id), "/", '_'); - chatid_dir = log_store_empathy_get_dir (self, account, esc_chat_id, - chatroom); - timestamp = log_store_empathy_get_timestamp_filename (); - filename = g_build_filename (chatid_dir, timestamp, NULL); - - g_free (esc_chat_id); - g_free (chatid_dir); - g_free (timestamp); - - return filename; -} - - -/* this is a method used at the end of the add_message process, used by any - * LogEntry<Type> instance. it should the only method allowed to write to the - * store */ -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); - - 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, "%s", entry); - DEBUG ("%s: written: %s", filename, entry); - - fclose (file); - g_free (filename); - return TRUE; -} - - -static gboolean -add_message_text_chat (TplLogStore *self, - TplLogEntryText *message, - GError **error) -{ - gboolean ret = FALSE; - TpDBusDaemon *bus_daemon; - TpAccount *account; - TplContact *sender; - const gchar *body_str; - gchar *avatar_token = NULL; - gchar *body; - gchar *timestamp; - gchar *contact_name; - gchar *contact_id; - gchar *entry; - TpChannelTextMessageType msg_type; - - bus_daemon = tp_dbus_daemon_dup (error); - if (bus_daemon == NULL) - { - DEBUG ("Error acquiring bus daemon: %s", (*error)->message); - goto out; - } - - account = tp_account_new (bus_daemon, - tpl_log_entry_get_account_path (TPL_LOG_ENTRY (message)), error); - if (account == NULL) - { - DEBUG ("Error acquiring TpAccount proxy: %s", (*error)->message); - goto out; - } - - body_str = tpl_log_entry_text_get_message (message); - if (TPL_STR_EMPTY (body_str)) - goto out; - - body = g_markup_escape_text (body_str, -1); - msg_type = tpl_log_entry_text_get_message_type (message); - timestamp = log_store_empathy_get_timestamp_from_message ( - TPL_LOG_ENTRY (message)); - - sender = tpl_log_entry_get_sender (TPL_LOG_ENTRY (message)); - contact_name = g_markup_escape_text (tpl_contact_get_alias (sender), -1); - contact_id = g_markup_escape_text (tpl_contact_get_identifier (sender), -1); - avatar_token = g_markup_escape_text (tpl_contact_get_avatar_token (sender), -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_get_log_id (TPL_LOG_ENTRY (message)), - contact_id, contact_name, - avatar_token ? avatar_token : "", - 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, - tpl_log_entry_get_chat_id (TPL_LOG_ENTRY (message)), - tpl_log_entry_text_is_chatroom (message), - entry, error); - -out: - g_free (contact_id); - g_free (contact_name); - g_free (timestamp); - g_free (body); - g_free (entry); - g_free (avatar_token); - - if (bus_daemon != NULL) - g_object_unref (bus_daemon); - if (account != NULL) - g_object_unref (account); - - return ret; -} - - -static gboolean -add_message_text (TplLogStore *self, - TplLogEntryText *message, - GError **error) -{ - TplLogEntryTextSignalType signal_type; - - g_return_val_if_fail (TPL_IS_LOG_STORE (self), FALSE); - g_return_val_if_fail (TPL_IS_LOG_ENTRY_TEXT (message), FALSE); - - signal_type = tpl_log_entry_get_signal_type (TPL_LOG_ENTRY (message)); - - switch (signal_type) - { - case TPL_LOG_ENTRY_TEXT_SIGNAL_SENT: - case TPL_LOG_ENTRY_TEXT_SIGNAL_RECEIVED: - return add_message_text_chat (self, message, error); - break; - case TPL_LOG_ENTRY_TEXT_SIGNAL_CHAT_STATUS_CHANGED: - g_warning ("STATUS_CHANGED log entry not currently handled"); - return FALSE; - 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, - TplLogEntry *message, - GError **error) -{ - g_return_val_if_fail (TPL_IS_LOG_ENTRY (message), FALSE); - - switch (tpl_log_entry_get_signal_type (TPL_LOG_ENTRY (message))) - { - case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_SENT: - case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_RECEIVED: - case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_SEND_ERROR: - case TPL_LOG_ENTRY_CHANELL_TEXT_SIGNAL_LOST_MESSAGE: - case TPL_LOG_ENTRY_CHANNEL_TEXT_SIGNAL_CHAT_STATUS_CHANGED: - return add_message_text (self, TPL_LOG_ENTRY_TEXT (message), error); - default: - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_MESSAGE, - "LogEntrySignalType not handled by this LogStore (%s)", - log_store_empathy_get_name (self)); - return FALSE; - } -} - - static gboolean log_store_empathy_exists (TplLogStore *self, TpAccount *account, @@ -758,7 +512,9 @@ log_store_empathy_get_messages_for_file (TplLogStore *self, gboolean is_user = FALSE; gchar *msg_type_str; gchar *cm_id_str; - guint cm_id; + gint64 cm_id; + gchar *instead_of_channel_path; + gchar *log_id; TpChannelTextMessageType msg_type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; if (strcmp ((const gchar *) node->name, "message") != 0) @@ -792,8 +548,15 @@ log_store_empathy_get_messages_for_file (TplLogStore *self, tpl_contact_set_alias (sender, sender_name); tpl_contact_set_avatar_token (sender, sender_avatar_token); - message = tpl_log_entry_text_new (cm_id, + /* in legacy Empathy LogStore there is no concept of log-id as a unique + * token, so I'll create, just for it to be present an ad hoc unique + * token */ + instead_of_channel_path = g_strconcat ( + tp_proxy_get_object_path (account), sender_id, NULL); + log_id = create_message_token (instead_of_channel_path, time_, cm_id); + message = tpl_log_entry_text_new (log_id, tp_proxy_get_object_path (account), TPL_LOG_ENTRY_DIRECTION_NONE); + tpl_log_entry_set_pending_msg_id (TPL_LOG_ENTRY (message), cm_id); tpl_log_entry_set_sender (TPL_LOG_ENTRY (message), sender); tpl_log_entry_set_timestamp (TPL_LOG_ENTRY (message), t); tpl_log_entry_text_set_message (message, body); @@ -1211,9 +974,9 @@ log_store_iface_init (gpointer g_iface, { TplLogStoreInterface *iface = (TplLogStoreInterface *) g_iface; + iface->add_message = NULL; /* read_only LogStore */ 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; diff --git a/telepathy-logger/util.c b/telepathy-logger/util.c new file mode 100644 index 0000000..8193369 --- /dev/null +++ b/telepathy-logger/util.c @@ -0,0 +1,44 @@ +/* -*- 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 "util.h" + +/* Bug#26838 prevents us to trust Messages' iface message-token + * header, so I need to create a token which TPL can trust to be unique + * within itself */ +gchar * +create_message_token (const gchar *channel, + const gchar *date, + guint msgid) +{ + GChecksum *log_id = g_checksum_new (G_CHECKSUM_SHA1); + gchar *retval; + + g_checksum_update (log_id, (guchar *) channel, -1); + g_checksum_update (log_id, (guchar *) date, -1); + g_checksum_update (log_id, (guchar *) &msgid, sizeof (unsigned int)); + + retval = g_strdup (g_checksum_get_string (log_id)); + + g_checksum_free (log_id); + + return retval; +} diff --git a/telepathy-logger/util.h b/telepathy-logger/util.h index 0db8623..18df252 100644 --- a/telepathy-logger/util.h +++ b/telepathy-logger/util.h @@ -42,4 +42,8 @@ return; \ } +gchar *create_message_token (const gchar *channel, const gchar *date, + guint msgid); + + #endif // __TPL_UTIL_H__ |