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>
26 #include "empathy-message.h"
28 #include <glib/gi18n-lib.h>
30 #include "empathy-client-factory.h"
31 #include "empathy-time.h"
32 #include "empathy-utils.h"
33 #include "empathy-enum-types.h"
35 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
37 TpMessage *tp_message;
38 TpChannelTextMessageType type;
39 EmpathyContact *sender;
40 EmpathyContact *receiver;
45 gint64 original_timestamp;
49 TpChannelTextMessageFlags flags;
52 static void empathy_message_finalize (GObject *object);
53 static void message_get_property (GObject *object,
57 static void message_set_property (GObject *object,
62 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
73 PROP_ORIGINAL_TIMESTAMP,
81 empathy_message_class_init (EmpathyMessageClass *class)
83 GObjectClass *object_class;
85 object_class = G_OBJECT_CLASS (class);
86 object_class->finalize = empathy_message_finalize;
87 object_class->get_property = message_get_property;
88 object_class->set_property = message_set_property;
90 g_object_class_install_property (object_class,
92 g_param_spec_uint ("type",
94 "The type of message",
95 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
96 TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
97 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
98 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
99 G_PARAM_CONSTRUCT_ONLY));
100 g_object_class_install_property (object_class,
102 g_param_spec_object ("sender",
104 "The sender of the message",
105 EMPATHY_TYPE_CONTACT,
106 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
107 g_object_class_install_property (object_class,
109 g_param_spec_object ("receiver",
111 "The receiver of the message",
112 EMPATHY_TYPE_CONTACT,
113 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
114 g_object_class_install_property (object_class,
116 g_param_spec_string ("token",
120 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
121 g_object_class_install_property (object_class,
123 g_param_spec_string ("supersedes",
125 "The message-token this message supersedes",
127 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
128 g_object_class_install_property (object_class,
130 g_param_spec_string ("body",
132 "The content of the message",
134 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
135 G_PARAM_CONSTRUCT_ONLY));
136 g_object_class_install_property (object_class,
138 g_param_spec_int64 ("timestamp",
141 G_MININT64, G_MAXINT64, 0,
142 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
143 G_PARAM_CONSTRUCT_ONLY));
144 g_object_class_install_property (object_class,
145 PROP_ORIGINAL_TIMESTAMP,
146 g_param_spec_int64 ("original-timestamp",
147 "Original Timestamp",
148 "Timestamp of the original message",
149 G_MININT64, G_MAXINT64, 0,
150 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
151 g_object_class_install_property (object_class,
153 g_param_spec_boolean ("is-backlog",
155 "If the message belongs to history",
157 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
158 G_PARAM_CONSTRUCT_ONLY));
161 g_object_class_install_property (object_class,
163 g_param_spec_boolean ("incoming",
165 "If this is an incoming (as opposed to sent) message",
167 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
168 G_PARAM_CONSTRUCT_ONLY));
170 g_object_class_install_property (object_class,
172 g_param_spec_uint ("flags",
174 "The TpChannelTextMessageFlags of this message",
176 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
177 G_PARAM_CONSTRUCT_ONLY));
179 g_object_class_install_property (object_class,
181 g_param_spec_object ("tp-message",
183 "The TpMessage of this message",
185 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
186 G_PARAM_CONSTRUCT_ONLY));
188 g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
193 empathy_message_init (EmpathyMessage *message)
195 EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
196 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
198 message->priv = priv;
199 priv->timestamp = empathy_time_get_current ();
203 empathy_message_finalize (GObject *object)
205 EmpathyMessagePriv *priv;
207 priv = GET_PRIV (object);
210 g_object_unref (priv->sender);
212 if (priv->receiver) {
213 g_object_unref (priv->receiver);
216 if (priv->tp_message) {
217 g_object_unref (priv->tp_message);
220 g_free (priv->token);
221 g_free (priv->supersedes);
224 G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
228 message_get_property (GObject *object,
233 EmpathyMessagePriv *priv;
235 priv = GET_PRIV (object);
239 g_value_set_uint (value, priv->type);
242 g_value_set_object (value, priv->sender);
245 g_value_set_object (value, priv->receiver);
248 g_value_set_string (value, priv->token);
250 case PROP_SUPERSEDES:
251 g_value_set_string (value, priv->supersedes);
254 g_value_set_string (value, priv->body);
257 g_value_set_int64 (value, priv->timestamp);
259 case PROP_ORIGINAL_TIMESTAMP:
260 g_value_set_int64 (value, priv->original_timestamp);
262 case PROP_IS_BACKLOG:
263 g_value_set_boolean (value, priv->is_backlog);
266 g_value_set_boolean (value, priv->incoming);
269 g_value_set_uint (value, priv->flags);
271 case PROP_TP_MESSAGE:
272 g_value_set_object (value, priv->tp_message);
275 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
281 message_set_property (GObject *object,
286 EmpathyMessagePriv *priv;
288 priv = GET_PRIV (object);
292 priv->type = g_value_get_uint (value);
295 empathy_message_set_sender (EMPATHY_MESSAGE (object),
296 EMPATHY_CONTACT (g_value_get_object (value)));
299 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
300 EMPATHY_CONTACT (g_value_get_object (value)));
303 g_assert (priv->token == NULL); /* construct only */
304 priv->token = g_value_dup_string (value);
306 case PROP_SUPERSEDES:
307 g_assert (priv->supersedes == NULL); /* construct only */
308 priv->supersedes = g_value_dup_string (value);
311 g_assert (priv->body == NULL); /* construct only */
312 priv->body = g_value_dup_string (value);
315 priv->timestamp = g_value_get_int64 (value);
316 if (priv->timestamp <= 0)
317 priv->timestamp = empathy_time_get_current ();
319 case PROP_ORIGINAL_TIMESTAMP:
320 priv->original_timestamp = g_value_get_int64 (value);
322 case PROP_IS_BACKLOG:
323 priv->is_backlog = g_value_get_boolean (value);
326 priv->incoming = g_value_get_boolean (value);
329 priv->flags = g_value_get_uint (value);
331 case PROP_TP_MESSAGE:
332 priv->tp_message = g_value_dup_object (value);
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
341 empathy_message_from_tpl_log_event (TplEvent *logevent)
343 EmpathyMessage *retval = NULL;
344 EmpathyClientFactory *factory;
345 TpAccount *account = NULL;
346 TplEntity *receiver = NULL;
347 TplEntity *sender = NULL;
349 const gchar *token = NULL, *supersedes = NULL;
350 EmpathyContact *contact;
351 TpChannelTextMessageType type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
352 gint64 timestamp, original_timestamp = 0;
354 g_return_val_if_fail (TPL_IS_EVENT (logevent), NULL);
356 factory = empathy_client_factory_dup ();
357 /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
358 * won't be selected any non-existing (ie removed) account.
359 * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
360 * might add a not existing account to the AM. tp_account_new () probably
361 * will be the best way to handle it.
362 * Note: When creating an EmpathyContact from a TplEntity instance, the
363 * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
364 * avatar (contact_get_avatar_filename () need a TpAccount).
365 * If the way EmpathyContact stores the avatar is changes, it might not be
366 * needed anymore any TpAccount passing and the following call will be
368 account = tp_simple_client_factory_ensure_account (
369 TP_SIMPLE_CLIENT_FACTORY (factory),
370 tpl_event_get_account_path (logevent), NULL, NULL);
371 g_object_unref (factory);
373 if (TPL_IS_TEXT_EVENT (logevent)) {
374 TplTextEvent *textevent = TPL_TEXT_EVENT (logevent);
376 supersedes = tpl_text_event_get_supersedes_token (textevent);
378 /* tp-logger is kind of messy in that instead of having
379 * timestamp and original-timestamp like Telepathy it has
380 * timestamp (which is the original) and edited-timestamp,
381 * (which is when the message was edited) */
382 if (tp_str_empty (supersedes)) {
383 /* not an edited message */
384 timestamp = tpl_event_get_timestamp (logevent);
386 /* this is an edited event */
387 original_timestamp = tpl_event_get_timestamp (logevent);
388 timestamp = tpl_text_event_get_edit_timestamp (textevent);
391 body = g_strdup (tpl_text_event_get_message (textevent));
393 type = tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent));
394 token = tpl_text_event_get_message_token (textevent);
396 else if (TPL_IS_CALL_EVENT (logevent)) {
397 TplCallEvent *call = TPL_CALL_EVENT (logevent);
399 timestamp = tpl_event_get_timestamp (logevent);
401 if (tpl_call_event_get_end_reason (call) == TP_CALL_STATE_CHANGE_REASON_NO_ANSWER)
402 body = g_strdup_printf (_("Missed call from %s"),
403 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
404 else if (tpl_entity_get_entity_type (tpl_event_get_sender (logevent)) == TPL_ENTITY_SELF)
405 /* Translators: this is an outgoing call, e.g. 'Called Alice' */
406 body = g_strdup_printf (_("Called %s"),
407 tpl_entity_get_alias (tpl_event_get_receiver (logevent)));
409 body = g_strdup_printf (_("Call from %s"),
410 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
413 /* Unknown event type */
417 receiver = tpl_event_get_receiver (logevent);
418 sender = tpl_event_get_sender (logevent);
420 retval = g_object_new (EMPATHY_TYPE_MESSAGE,
423 "supersedes", supersedes,
426 "timestamp", timestamp,
427 "original-timestamp", original_timestamp,
430 if (receiver != NULL) {
431 contact = empathy_contact_from_tpl_contact (account, receiver);
432 empathy_message_set_receiver (retval, contact);
433 g_object_unref (contact);
436 if (sender != NULL) {
437 contact = empathy_contact_from_tpl_contact (account, sender);
438 empathy_message_set_sender (retval, contact);
439 g_object_unref (contact);
448 empathy_message_get_tp_message (EmpathyMessage *message)
450 EmpathyMessagePriv *priv;
452 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
454 priv = GET_PRIV (message);
456 return priv->tp_message;
459 TpChannelTextMessageType
460 empathy_message_get_tptype (EmpathyMessage *message)
462 EmpathyMessagePriv *priv;
464 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
465 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
467 priv = GET_PRIV (message);
473 empathy_message_get_sender (EmpathyMessage *message)
475 EmpathyMessagePriv *priv;
477 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
479 priv = GET_PRIV (message);
485 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
487 EmpathyMessagePriv *priv;
488 EmpathyContact *old_sender;
490 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
491 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
493 priv = GET_PRIV (message);
495 old_sender = priv->sender;
496 priv->sender = g_object_ref (contact);
499 g_object_unref (old_sender);
502 g_object_notify (G_OBJECT (message), "sender");
506 empathy_message_get_receiver (EmpathyMessage *message)
508 EmpathyMessagePriv *priv;
510 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
512 priv = GET_PRIV (message);
514 return priv->receiver;
518 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
520 EmpathyMessagePriv *priv;
521 EmpathyContact *old_receiver;
523 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
524 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
526 priv = GET_PRIV (message);
528 old_receiver = priv->receiver;
529 priv->receiver = g_object_ref (contact);
532 g_object_unref (old_receiver);
535 g_object_notify (G_OBJECT (message), "receiver");
539 empathy_message_get_token (EmpathyMessage *message)
541 EmpathyMessagePriv *priv;
543 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
545 priv = GET_PRIV (message);
551 empathy_message_get_supersedes (EmpathyMessage *message)
553 EmpathyMessagePriv *priv;
555 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
557 priv = GET_PRIV (message);
559 return priv->supersedes;
563 empathy_message_is_edit (EmpathyMessage *message)
565 EmpathyMessagePriv *priv;
567 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
569 priv = GET_PRIV (message);
571 return !tp_str_empty (priv->supersedes);
575 empathy_message_get_body (EmpathyMessage *message)
577 EmpathyMessagePriv *priv;
579 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
581 priv = GET_PRIV (message);
587 empathy_message_get_timestamp (EmpathyMessage *message)
589 EmpathyMessagePriv *priv;
591 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
593 priv = GET_PRIV (message);
595 return priv->timestamp;
599 empathy_message_get_original_timestamp (EmpathyMessage *message)
601 EmpathyMessagePriv *priv;
603 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
605 priv = GET_PRIV (message);
607 return priv->original_timestamp;
611 empathy_message_is_backlog (EmpathyMessage *message)
613 EmpathyMessagePriv *priv;
615 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
617 priv = GET_PRIV (message);
619 return priv->is_backlog;
622 TpChannelTextMessageType
623 empathy_message_type_from_str (const gchar *type_str)
625 if (strcmp (type_str, "normal") == 0) {
626 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
628 if (strcmp (type_str, "action") == 0) {
629 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
631 else if (strcmp (type_str, "notice") == 0) {
632 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
634 else if (strcmp (type_str, "auto-reply") == 0) {
635 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
638 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
642 empathy_message_type_to_str (TpChannelTextMessageType type)
645 case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
647 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
649 case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
651 case TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT:
652 return "delivery-report";
653 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
660 empathy_message_is_incoming (EmpathyMessage *message)
662 EmpathyMessagePriv *priv = GET_PRIV (message);
664 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
666 return priv->incoming;
670 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
672 EmpathyMessagePriv *priv1;
673 EmpathyMessagePriv *priv2;
675 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
676 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
678 priv1 = GET_PRIV (message1);
679 priv2 = GET_PRIV (message2);
681 if (priv1->timestamp == priv2->timestamp &&
682 !tp_strdiff (priv1->body, priv2->body)) {
689 TpChannelTextMessageFlags
690 empathy_message_get_flags (EmpathyMessage *self)
692 EmpathyMessagePriv *priv = GET_PRIV (self);
694 g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
700 empathy_message_new_from_tp_message (TpMessage *tp_msg,
703 EmpathyMessage *message;
705 TpChannelTextMessageFlags flags;
707 gint64 original_timestamp;
708 const GHashTable *part = tp_message_peek (tp_msg, 0);
711 g_return_val_if_fail (TP_IS_MESSAGE (tp_msg), NULL);
713 body = tp_message_to_text (tp_msg, &flags);
715 timestamp = tp_message_get_sent_timestamp (tp_msg);
717 timestamp = tp_message_get_received_timestamp (tp_msg);
719 original_timestamp = tp_asv_get_int64 (part,
720 "original-message-received", NULL);
722 is_backlog = (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) ==
723 TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK;
725 message = g_object_new (EMPATHY_TYPE_MESSAGE,
727 "token", tp_message_get_token (tp_msg),
728 "supersedes", tp_message_get_supersedes (tp_msg),
729 "type", tp_message_get_message_type (tp_msg),
730 "timestamp", timestamp,
731 "original-timestamp", original_timestamp,
733 "is-backlog", is_backlog,
734 "incoming", incoming,
735 "tp-message", tp_msg,