summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCosimo Alfarano <cosimo.alfarano@collabora.co.uk>2010-03-03 14:30:00 +0000
committerCosimo Alfarano <cosimo.alfarano@collabora.co.uk>2010-03-12 14:07:43 +0100
commiteb8bd37327b668ff964cbcd8b5b4a8be645c8de1 (patch)
tree50e62bc5edb3a54ce2a85e014246fc2a0a2dcd50
parentb38e68a0472b570879a1492b68ab3b95b157dbd0 (diff)
downloadtelepathy-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.am7
-rw-r--r--telepathy-logger/channel-text.c31
-rw-r--r--telepathy-logger/log-entry-text.c33
-rw-r--r--telepathy-logger/log-entry-text.h7
-rw-r--r--telepathy-logger/log-entry.c147
-rw-r--r--telepathy-logger/log-entry.h20
-rw-r--r--telepathy-logger/log-manager.c1
-rw-r--r--telepathy-logger/log-store-default.c1275
-rw-r--r--telepathy-logger/log-store-default.h62
-rw-r--r--telepathy-logger/log-store-empathy.c261
-rw-r--r--telepathy-logger/util.c44
-rw-r--r--telepathy-logger/util.h4
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__