1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2004-2007 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Xavier Claessens <xclaesse@gmail.com>
29 #include <glib/gi18n-lib.h>
31 #include <telepathy-glib/util.h>
32 #include <telepathy-glib/account.h>
33 #include <telepathy-glib/account-manager.h>
35 #include <telepathy-logger/entity.h>
36 #include <telepathy-logger/event.h>
37 #include <telepathy-logger/text-event.h>
39 # include <telepathy-logger/call-event.h>
42 #include "empathy-client-factory.h"
43 #include "empathy-message.h"
44 #include "empathy-utils.h"
45 #include "empathy-enum-types.h"
47 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
49 TpMessage *tp_message;
50 TpChannelTextMessageType type;
51 EmpathyContact *sender;
52 EmpathyContact *receiver;
57 gint64 original_timestamp;
61 TpChannelTextMessageFlags flags;
64 static void empathy_message_finalize (GObject *object);
65 static void message_get_property (GObject *object,
69 static void message_set_property (GObject *object,
74 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
85 PROP_ORIGINAL_TIMESTAMP,
93 empathy_message_class_init (EmpathyMessageClass *class)
95 GObjectClass *object_class;
97 object_class = G_OBJECT_CLASS (class);
98 object_class->finalize = empathy_message_finalize;
99 object_class->get_property = message_get_property;
100 object_class->set_property = message_set_property;
102 g_object_class_install_property (object_class,
104 g_param_spec_uint ("type",
106 "The type of message",
107 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
108 TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
109 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
110 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
111 G_PARAM_CONSTRUCT_ONLY));
112 g_object_class_install_property (object_class,
114 g_param_spec_object ("sender",
116 "The sender of the message",
117 EMPATHY_TYPE_CONTACT,
118 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
119 g_object_class_install_property (object_class,
121 g_param_spec_object ("receiver",
123 "The receiver of the message",
124 EMPATHY_TYPE_CONTACT,
125 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
126 g_object_class_install_property (object_class,
128 g_param_spec_string ("token",
132 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
133 g_object_class_install_property (object_class,
135 g_param_spec_string ("supersedes",
137 "The message-token this message supersedes",
139 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
140 g_object_class_install_property (object_class,
142 g_param_spec_string ("body",
144 "The content of the message",
146 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
147 G_PARAM_CONSTRUCT_ONLY));
148 g_object_class_install_property (object_class,
150 g_param_spec_int64 ("timestamp",
153 G_MININT64, G_MAXINT64, 0,
154 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
155 G_PARAM_CONSTRUCT_ONLY));
156 g_object_class_install_property (object_class,
157 PROP_ORIGINAL_TIMESTAMP,
158 g_param_spec_int64 ("original-timestamp",
159 "Original Timestamp",
160 "Timestamp of the original message",
161 G_MININT64, G_MAXINT64, 0,
162 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
163 g_object_class_install_property (object_class,
165 g_param_spec_boolean ("is-backlog",
167 "If the message belongs to history",
169 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
170 G_PARAM_CONSTRUCT_ONLY));
173 g_object_class_install_property (object_class,
175 g_param_spec_boolean ("incoming",
177 "If this is an incoming (as opposed to sent) message",
179 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
180 G_PARAM_CONSTRUCT_ONLY));
182 g_object_class_install_property (object_class,
184 g_param_spec_uint ("flags",
186 "The TpChannelTextMessageFlags of this message",
188 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
189 G_PARAM_CONSTRUCT_ONLY));
191 g_object_class_install_property (object_class,
193 g_param_spec_object ("tp-message",
195 "The TpMessage of this message",
197 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
198 G_PARAM_CONSTRUCT_ONLY));
200 g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
205 empathy_message_init (EmpathyMessage *message)
207 EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
208 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
210 message->priv = priv;
211 priv->timestamp = empathy_time_get_current ();
215 empathy_message_finalize (GObject *object)
217 EmpathyMessagePriv *priv;
219 priv = GET_PRIV (object);
222 g_object_unref (priv->sender);
224 if (priv->receiver) {
225 g_object_unref (priv->receiver);
228 if (priv->tp_message) {
229 g_object_unref (priv->tp_message);
232 g_free (priv->token);
233 g_free (priv->supersedes);
236 G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
240 message_get_property (GObject *object,
245 EmpathyMessagePriv *priv;
247 priv = GET_PRIV (object);
251 g_value_set_uint (value, priv->type);
254 g_value_set_object (value, priv->sender);
257 g_value_set_object (value, priv->receiver);
260 g_value_set_string (value, priv->token);
262 case PROP_SUPERSEDES:
263 g_value_set_string (value, priv->supersedes);
266 g_value_set_string (value, priv->body);
269 g_value_set_int64 (value, priv->timestamp);
271 case PROP_ORIGINAL_TIMESTAMP:
272 g_value_set_int64 (value, priv->original_timestamp);
274 case PROP_IS_BACKLOG:
275 g_value_set_boolean (value, priv->is_backlog);
278 g_value_set_boolean (value, priv->incoming);
281 g_value_set_uint (value, priv->flags);
283 case PROP_TP_MESSAGE:
284 g_value_set_object (value, priv->tp_message);
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
293 message_set_property (GObject *object,
298 EmpathyMessagePriv *priv;
300 priv = GET_PRIV (object);
304 priv->type = g_value_get_uint (value);
307 empathy_message_set_sender (EMPATHY_MESSAGE (object),
308 EMPATHY_CONTACT (g_value_get_object (value)));
311 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
312 EMPATHY_CONTACT (g_value_get_object (value)));
315 g_assert (priv->token == NULL); /* construct only */
316 priv->token = g_value_dup_string (value);
318 case PROP_SUPERSEDES:
319 g_assert (priv->supersedes == NULL); /* construct only */
320 priv->supersedes = g_value_dup_string (value);
323 g_assert (priv->body == NULL); /* construct only */
324 priv->body = g_value_dup_string (value);
327 priv->timestamp = g_value_get_int64 (value);
328 if (priv->timestamp <= 0)
329 priv->timestamp = empathy_time_get_current ();
331 case PROP_ORIGINAL_TIMESTAMP:
332 priv->original_timestamp = g_value_get_int64 (value);
334 case PROP_IS_BACKLOG:
335 priv->is_backlog = g_value_get_boolean (value);
338 priv->incoming = g_value_get_boolean (value);
341 priv->flags = g_value_get_uint (value);
343 case PROP_TP_MESSAGE:
344 priv->tp_message = g_value_dup_object (value);
347 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
353 empathy_message_from_tpl_log_event (TplEvent *logevent)
355 EmpathyMessage *retval = NULL;
356 EmpathyClientFactory *factory;
357 TpAccount *account = NULL;
358 TplEntity *receiver = NULL;
359 TplEntity *sender = NULL;
361 const gchar *token = NULL, *supersedes = NULL;
362 EmpathyContact *contact;
363 TpChannelTextMessageType type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
364 gint64 timestamp, original_timestamp = 0;
366 g_return_val_if_fail (TPL_IS_EVENT (logevent), NULL);
368 factory = empathy_client_factory_dup ();
369 /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
370 * won't be selected any non-existing (ie removed) account.
371 * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
372 * might add a not existing account to the AM. tp_account_new () probably
373 * will be the best way to handle it.
374 * Note: When creating an EmpathyContact from a TplEntity instance, the
375 * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
376 * avatar (contact_get_avatar_filename () need a TpAccount).
377 * If the way EmpathyContact stores the avatar is changes, it might not be
378 * needed anymore any TpAccount passing and the following call will be
380 account = tp_simple_client_factory_ensure_account (
381 TP_SIMPLE_CLIENT_FACTORY (factory),
382 tpl_event_get_account_path (logevent), NULL, NULL);
383 g_object_unref (factory);
385 if (TPL_IS_TEXT_EVENT (logevent)) {
386 TplTextEvent *textevent = TPL_TEXT_EVENT (logevent);
388 supersedes = tpl_text_event_get_supersedes_token (textevent);
390 /* tp-logger is kind of messy in that instead of having
391 * timestamp and original-timestamp like Telepathy it has
392 * timestamp (which is the original) and edited-timestamp,
393 * (which is when the message was edited) */
394 if (tp_str_empty (supersedes)) {
395 /* not an edited message */
396 timestamp = tpl_event_get_timestamp (logevent);
398 /* this is an edited event */
399 original_timestamp = tpl_event_get_timestamp (logevent);
400 timestamp = tpl_text_event_get_edit_timestamp (textevent);
403 body = g_strdup (tpl_text_event_get_message (textevent));
405 type = tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent));
406 token = tpl_text_event_get_message_token (textevent);
408 #ifdef HAVE_CALL_LOGS
409 else if (TPL_IS_CALL_EVENT (logevent)) {
410 TplCallEvent *call = TPL_CALL_EVENT (logevent);
412 timestamp = tpl_event_get_timestamp (logevent);
414 if (tpl_call_event_get_end_reason (call) == TPL_CALL_END_REASON_NO_ANSWER)
415 body = g_strdup_printf (_("Missed call from %s"),
416 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
417 else if (tpl_entity_get_entity_type (tpl_event_get_sender (logevent)) == TPL_ENTITY_SELF)
418 /* Translators: this is an outgoing call, e.g. 'Called Alice' */
419 body = g_strdup_printf (_("Called %s"),
420 tpl_entity_get_alias (tpl_event_get_receiver (logevent)));
422 body = g_strdup_printf (_("Call from %s"),
423 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
427 /* Unknown event type */
431 receiver = tpl_event_get_receiver (logevent);
432 sender = tpl_event_get_sender (logevent);
434 retval = g_object_new (EMPATHY_TYPE_MESSAGE,
437 "supersedes", supersedes,
440 "timestamp", timestamp,
441 "original-timestamp", original_timestamp,
444 if (receiver != NULL) {
445 contact = empathy_contact_from_tpl_contact (account, receiver);
446 empathy_message_set_receiver (retval, contact);
447 g_object_unref (contact);
450 if (sender != NULL) {
451 contact = empathy_contact_from_tpl_contact (account, sender);
452 empathy_message_set_sender (retval, contact);
453 g_object_unref (contact);
462 empathy_message_get_tp_message (EmpathyMessage *message)
464 EmpathyMessagePriv *priv;
466 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
468 priv = GET_PRIV (message);
470 return priv->tp_message;
473 TpChannelTextMessageType
474 empathy_message_get_tptype (EmpathyMessage *message)
476 EmpathyMessagePriv *priv;
478 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
479 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
481 priv = GET_PRIV (message);
487 empathy_message_get_sender (EmpathyMessage *message)
489 EmpathyMessagePriv *priv;
491 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
493 priv = GET_PRIV (message);
499 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
501 EmpathyMessagePriv *priv;
502 EmpathyContact *old_sender;
504 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
505 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
507 priv = GET_PRIV (message);
509 old_sender = priv->sender;
510 priv->sender = g_object_ref (contact);
513 g_object_unref (old_sender);
516 g_object_notify (G_OBJECT (message), "sender");
520 empathy_message_get_receiver (EmpathyMessage *message)
522 EmpathyMessagePriv *priv;
524 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
526 priv = GET_PRIV (message);
528 return priv->receiver;
532 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
534 EmpathyMessagePriv *priv;
535 EmpathyContact *old_receiver;
537 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
538 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
540 priv = GET_PRIV (message);
542 old_receiver = priv->receiver;
543 priv->receiver = g_object_ref (contact);
546 g_object_unref (old_receiver);
549 g_object_notify (G_OBJECT (message), "receiver");
553 empathy_message_get_token (EmpathyMessage *message)
555 EmpathyMessagePriv *priv;
557 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
559 priv = GET_PRIV (message);
565 empathy_message_get_supersedes (EmpathyMessage *message)
567 EmpathyMessagePriv *priv;
569 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
571 priv = GET_PRIV (message);
573 return priv->supersedes;
577 empathy_message_is_edit (EmpathyMessage *message)
579 EmpathyMessagePriv *priv;
581 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
583 priv = GET_PRIV (message);
585 return !tp_str_empty (priv->supersedes);
589 empathy_message_get_body (EmpathyMessage *message)
591 EmpathyMessagePriv *priv;
593 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
595 priv = GET_PRIV (message);
601 empathy_message_get_timestamp (EmpathyMessage *message)
603 EmpathyMessagePriv *priv;
605 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
607 priv = GET_PRIV (message);
609 return priv->timestamp;
613 empathy_message_get_original_timestamp (EmpathyMessage *message)
615 EmpathyMessagePriv *priv;
617 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
619 priv = GET_PRIV (message);
621 return priv->original_timestamp;
625 empathy_message_is_backlog (EmpathyMessage *message)
627 EmpathyMessagePriv *priv;
629 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
631 priv = GET_PRIV (message);
633 return priv->is_backlog;
636 TpChannelTextMessageType
637 empathy_message_type_from_str (const gchar *type_str)
639 if (strcmp (type_str, "normal") == 0) {
640 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
642 if (strcmp (type_str, "action") == 0) {
643 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
645 else if (strcmp (type_str, "notice") == 0) {
646 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
648 else if (strcmp (type_str, "auto-reply") == 0) {
649 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
652 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
656 empathy_message_type_to_str (TpChannelTextMessageType type)
659 case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
661 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
663 case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
665 case TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT:
666 return "delivery-report";
667 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
674 empathy_message_is_incoming (EmpathyMessage *message)
676 EmpathyMessagePriv *priv = GET_PRIV (message);
678 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
680 return priv->incoming;
684 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
686 EmpathyMessagePriv *priv1;
687 EmpathyMessagePriv *priv2;
689 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
690 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
692 priv1 = GET_PRIV (message1);
693 priv2 = GET_PRIV (message2);
695 if (priv1->timestamp == priv2->timestamp &&
696 !tp_strdiff (priv1->body, priv2->body)) {
703 TpChannelTextMessageFlags
704 empathy_message_get_flags (EmpathyMessage *self)
706 EmpathyMessagePriv *priv = GET_PRIV (self);
708 g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
714 empathy_message_new_from_tp_message (TpMessage *tp_msg,
717 EmpathyMessage *message;
719 TpChannelTextMessageFlags flags;
721 gint64 original_timestamp;
722 const GHashTable *part = tp_message_peek (tp_msg, 0);
725 g_return_val_if_fail (TP_IS_MESSAGE (tp_msg), NULL);
727 body = tp_message_to_text (tp_msg, &flags);
729 timestamp = tp_message_get_sent_timestamp (tp_msg);
731 timestamp = tp_message_get_received_timestamp (tp_msg);
733 original_timestamp = tp_asv_get_int64 (part,
734 "original-message-received", NULL);
736 is_backlog = (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) ==
737 TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK;
739 message = g_object_new (EMPATHY_TYPE_MESSAGE,
741 "token", tp_message_get_token (tp_msg),
742 "supersedes", tp_message_get_supersedes (tp_msg),
743 "type", tp_message_get_message_type (tp_msg),
744 "timestamp", timestamp,
745 "original-timestamp", original_timestamp,
747 "is-backlog", is_backlog,
748 "incoming", incoming,
749 "tp-message", tp_msg,