]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
Updated Dutch translation master
[empathy.git] / libempathy / empathy-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
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.
10  *
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.
15  *
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
20  *
21  * Authors: Mikael Hallendal <micke@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26 #include "empathy-message.h"
27
28 #include <glib/gi18n-lib.h>
29 #include <tp-account-widgets/tpaw-time.h>
30
31 #include "empathy-client-factory.h"
32 #include "empathy-utils.h"
33 #include "empathy-enum-types.h"
34
35 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
36 typedef struct {
37         TpMessage *tp_message;
38         TpChannelTextMessageType  type;
39         EmpathyContact           *sender;
40         EmpathyContact           *receiver;
41         gchar                    *token;
42         gchar                    *supersedes;
43         gchar                    *body;
44         gint64                    timestamp;
45         gint64                    original_timestamp;
46         gboolean                  is_backlog;
47         guint                     id;
48         gboolean                  incoming;
49 } EmpathyMessagePriv;
50
51 static void empathy_message_finalize   (GObject            *object);
52 static void message_get_property      (GObject            *object,
53                                        guint               param_id,
54                                        GValue             *value,
55                                        GParamSpec         *pspec);
56 static void message_set_property      (GObject            *object,
57                                        guint               param_id,
58                                        const GValue       *value,
59                                        GParamSpec         *pspec);
60
61 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
62
63 enum {
64         PROP_0,
65         PROP_TYPE,
66         PROP_SENDER,
67         PROP_RECEIVER,
68         PROP_TOKEN,
69         PROP_SUPERSEDES,
70         PROP_BODY,
71         PROP_TIMESTAMP,
72         PROP_ORIGINAL_TIMESTAMP,
73         PROP_IS_BACKLOG,
74         PROP_INCOMING,
75         PROP_TP_MESSAGE,
76 };
77
78 static void
79 empathy_message_class_init (EmpathyMessageClass *class)
80 {
81         GObjectClass *object_class;
82
83         object_class = G_OBJECT_CLASS (class);
84         object_class->finalize     = empathy_message_finalize;
85         object_class->get_property = message_get_property;
86         object_class->set_property = message_set_property;
87
88         g_object_class_install_property (object_class,
89                                          PROP_TYPE,
90                                          g_param_spec_uint ("type",
91                                                             "Message Type",
92                                                             "The type of message",
93                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
94                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
95                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
96                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
97                                                             G_PARAM_CONSTRUCT_ONLY));
98         g_object_class_install_property (object_class,
99                                          PROP_SENDER,
100                                          g_param_spec_object ("sender",
101                                                               "Message Sender",
102                                                               "The sender of the message",
103                                                               EMPATHY_TYPE_CONTACT,
104                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
105         g_object_class_install_property (object_class,
106                                          PROP_RECEIVER,
107                                          g_param_spec_object ("receiver",
108                                                               "Message Receiver",
109                                                               "The receiver of the message",
110                                                               EMPATHY_TYPE_CONTACT,
111                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
112         g_object_class_install_property (object_class,
113                                          PROP_TOKEN,
114                                          g_param_spec_string ("token",
115                                                               "Message Token",
116                                                               "The message-token",
117                                                               NULL,
118                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
119         g_object_class_install_property (object_class,
120                                          PROP_SUPERSEDES,
121                                          g_param_spec_string ("supersedes",
122                                                               "Supersedes Token",
123                                                               "The message-token this message supersedes",
124                                                               NULL,
125                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
126         g_object_class_install_property (object_class,
127                                          PROP_BODY,
128                                          g_param_spec_string ("body",
129                                                               "Message Body",
130                                                               "The content of the message",
131                                                               NULL,
132                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
133                                                               G_PARAM_CONSTRUCT_ONLY));
134         g_object_class_install_property (object_class,
135                                          PROP_TIMESTAMP,
136                                          g_param_spec_int64 ("timestamp",
137                                                             "timestamp",
138                                                             "timestamp",
139                                                             G_MININT64, G_MAXINT64, 0,
140                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
141                                                             G_PARAM_CONSTRUCT_ONLY));
142         g_object_class_install_property (object_class,
143                                          PROP_ORIGINAL_TIMESTAMP,
144                                          g_param_spec_int64 ("original-timestamp",
145                                                              "Original Timestamp",
146                                                              "Timestamp of the original message",
147                                                              G_MININT64, G_MAXINT64, 0,
148                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
149         g_object_class_install_property (object_class,
150                                          PROP_IS_BACKLOG,
151                                          g_param_spec_boolean ("is-backlog",
152                                                                "History message",
153                                                                "If the message belongs to history",
154                                                                FALSE,
155                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
156                                                                G_PARAM_CONSTRUCT_ONLY));
157
158
159         g_object_class_install_property (object_class,
160                                          PROP_INCOMING,
161                                          g_param_spec_boolean ("incoming",
162                                                                "Incoming",
163                                                                "If this is an incoming (as opposed to sent) message",
164                                                                FALSE,
165                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
166                                                                G_PARAM_CONSTRUCT_ONLY));
167
168         g_object_class_install_property (object_class,
169                                          PROP_TP_MESSAGE,
170                                          g_param_spec_object ("tp-message",
171                                                                "TpMessage",
172                                                                "The TpMessage of this message",
173                                                                TP_TYPE_MESSAGE,
174                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
175                                                                G_PARAM_CONSTRUCT_ONLY));
176
177         g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
178
179 }
180
181 static void
182 empathy_message_init (EmpathyMessage *message)
183 {
184         EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
185                 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
186
187         message->priv = priv;
188         priv->timestamp = tpaw_time_get_current ();
189 }
190
191 static void
192 empathy_message_finalize (GObject *object)
193 {
194         EmpathyMessagePriv *priv;
195
196         priv = GET_PRIV (object);
197
198         if (priv->sender) {
199                 g_object_unref (priv->sender);
200         }
201         if (priv->receiver) {
202                 g_object_unref (priv->receiver);
203         }
204
205         if (priv->tp_message) {
206                 g_object_unref (priv->tp_message);
207         }
208
209         g_free (priv->token);
210         g_free (priv->supersedes);
211         g_free (priv->body);
212
213         G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
214 }
215
216 static void
217 message_get_property (GObject    *object,
218                       guint       param_id,
219                       GValue     *value,
220                       GParamSpec *pspec)
221 {
222         EmpathyMessagePriv *priv;
223
224         priv = GET_PRIV (object);
225
226         switch (param_id) {
227         case PROP_TYPE:
228                 g_value_set_uint (value, priv->type);
229                 break;
230         case PROP_SENDER:
231                 g_value_set_object (value, priv->sender);
232                 break;
233         case PROP_RECEIVER:
234                 g_value_set_object (value, priv->receiver);
235                 break;
236         case PROP_TOKEN:
237                 g_value_set_string (value, priv->token);
238                 break;
239         case PROP_SUPERSEDES:
240                 g_value_set_string (value, priv->supersedes);
241                 break;
242         case PROP_BODY:
243                 g_value_set_string (value, priv->body);
244                 break;
245         case PROP_TIMESTAMP:
246                 g_value_set_int64 (value, priv->timestamp);
247                 break;
248         case PROP_ORIGINAL_TIMESTAMP:
249                 g_value_set_int64 (value, priv->original_timestamp);
250                 break;
251         case PROP_IS_BACKLOG:
252                 g_value_set_boolean (value, priv->is_backlog);
253                 break;
254         case PROP_INCOMING:
255                 g_value_set_boolean (value, priv->incoming);
256                 break;
257         case PROP_TP_MESSAGE:
258                 g_value_set_object (value, priv->tp_message);
259                 break;
260         default:
261                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
262                 break;
263         };
264 }
265
266 static void
267 message_set_property (GObject      *object,
268                       guint         param_id,
269                       const GValue *value,
270                       GParamSpec   *pspec)
271 {
272         EmpathyMessagePriv *priv;
273
274         priv = GET_PRIV (object);
275
276         switch (param_id) {
277         case PROP_TYPE:
278                 priv->type = g_value_get_uint (value);
279                 break;
280         case PROP_SENDER:
281                 empathy_message_set_sender (EMPATHY_MESSAGE (object),
282                                            EMPATHY_CONTACT (g_value_get_object (value)));
283                 break;
284         case PROP_RECEIVER:
285                 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
286                                              EMPATHY_CONTACT (g_value_get_object (value)));
287                 break;
288         case PROP_TOKEN:
289                 g_assert (priv->token == NULL); /* construct only */
290                 priv->token = g_value_dup_string (value);
291                 break;
292         case PROP_SUPERSEDES:
293                 g_assert (priv->supersedes == NULL); /* construct only */
294                 priv->supersedes = g_value_dup_string (value);
295                 break;
296         case PROP_BODY:
297                 g_assert (priv->body == NULL); /* construct only */
298                 priv->body = g_value_dup_string (value);
299                 break;
300         case PROP_TIMESTAMP:
301                 priv->timestamp = g_value_get_int64 (value);
302                 if (priv->timestamp <= 0)
303                         priv->timestamp = tpaw_time_get_current ();
304                 break;
305         case PROP_ORIGINAL_TIMESTAMP:
306                 priv->original_timestamp = g_value_get_int64 (value);
307                 break;
308         case PROP_IS_BACKLOG:
309                 priv->is_backlog = g_value_get_boolean (value);
310                 break;
311         case PROP_INCOMING:
312                 priv->incoming = g_value_get_boolean (value);
313                 break;
314         case PROP_TP_MESSAGE:
315                 priv->tp_message = g_value_dup_object (value);
316                 break;
317         default:
318                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
319                 break;
320         };
321 }
322
323 EmpathyMessage *
324 empathy_message_from_tpl_log_event (TplEvent *logevent)
325 {
326         EmpathyMessage *retval = NULL;
327         EmpathyClientFactory *factory;
328         TpAccount *account = NULL;
329         TplEntity *receiver = NULL;
330         TplEntity *sender = NULL;
331         gchar *body = NULL;
332         const gchar *token = NULL, *supersedes = NULL;
333         EmpathyContact *contact;
334         TpChannelTextMessageType type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
335         gint64 timestamp, original_timestamp = 0;
336
337         g_return_val_if_fail (TPL_IS_EVENT (logevent), NULL);
338
339         factory = empathy_client_factory_dup ();
340         /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
341          * won't be selected any non-existing (ie removed) account.
342          * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
343          * might add a not existing account to the AM. tp_account_new () probably
344          * will be the best way to handle it.
345          * Note: When creating an EmpathyContact from a TplEntity instance, the
346          * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
347          * avatar (contact_get_avatar_filename () need a TpAccount).
348          * If the way EmpathyContact stores the avatar is changes, it might not be
349          * needed anymore any TpAccount passing and the following call will be
350          * useless */
351         account = tp_simple_client_factory_ensure_account (
352                         TP_SIMPLE_CLIENT_FACTORY (factory),
353                         tpl_event_get_account_path (logevent), NULL, NULL);
354         g_object_unref (factory);
355
356         if (TPL_IS_TEXT_EVENT (logevent)) {
357                 TplTextEvent *textevent = TPL_TEXT_EVENT (logevent);
358
359                 supersedes = tpl_text_event_get_supersedes_token (textevent);
360
361                 /* tp-logger is kind of messy in that instead of having
362                  * timestamp and original-timestamp like Telepathy it has
363                  * timestamp (which is the original) and edited-timestamp,
364                  * (which is when the message was edited) */
365                 if (tp_str_empty (supersedes)) {
366                         /* not an edited message */
367                         timestamp = tpl_event_get_timestamp (logevent);
368                 } else {
369                         /* this is an edited event */
370                         original_timestamp = tpl_event_get_timestamp (logevent);
371                         timestamp = tpl_text_event_get_edit_timestamp (textevent);
372                 }
373
374                 body = g_strdup (tpl_text_event_get_message (textevent));
375
376                 type = tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent));
377                 token = tpl_text_event_get_message_token (textevent);
378         }
379         else if (TPL_IS_CALL_EVENT (logevent)) {
380                 TplCallEvent *call = TPL_CALL_EVENT (logevent);
381
382                 timestamp = tpl_event_get_timestamp (logevent);
383
384                 if (tpl_call_event_get_end_reason (call) == TP_CALL_STATE_CHANGE_REASON_NO_ANSWER)
385                         body = g_strdup_printf (_("Missed call from %s"),
386                                 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
387                 else if (tpl_entity_get_entity_type (tpl_event_get_sender (logevent)) == TPL_ENTITY_SELF)
388                         /* Translators: this is an outgoing call, e.g. 'Called Alice' */
389                         body = g_strdup_printf (_("Called %s"),
390                                 tpl_entity_get_alias (tpl_event_get_receiver (logevent)));
391                 else
392                         body = g_strdup_printf (_("Call from %s"),
393                                 tpl_entity_get_alias (tpl_event_get_sender (logevent)));
394         }
395         else {
396                 /* Unknown event type */
397                 return NULL;
398         }
399
400         receiver = tpl_event_get_receiver (logevent);
401         sender = tpl_event_get_sender (logevent);
402
403         retval = g_object_new (EMPATHY_TYPE_MESSAGE,
404                 "type", type,
405                 "token", token,
406                 "supersedes", supersedes,
407                 "body", body,
408                 "is-backlog", TRUE,
409                 "timestamp", timestamp,
410                 "original-timestamp", original_timestamp,
411                 NULL);
412
413         if (receiver != NULL) {
414                 contact = empathy_contact_from_tpl_contact (account, receiver);
415                 empathy_message_set_receiver (retval, contact);
416                 g_object_unref (contact);
417         }
418
419         if (sender != NULL) {
420                 contact = empathy_contact_from_tpl_contact (account, sender);
421                 empathy_message_set_sender (retval, contact);
422                 g_object_unref (contact);
423         }
424
425         g_free (body);
426
427         return retval;
428 }
429
430 TpMessage *
431 empathy_message_get_tp_message (EmpathyMessage *message)
432 {
433         EmpathyMessagePriv *priv;
434
435         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
436
437         priv = GET_PRIV (message);
438
439         return priv->tp_message;
440 }
441
442 TpChannelTextMessageType
443 empathy_message_get_tptype (EmpathyMessage *message)
444 {
445         EmpathyMessagePriv *priv;
446
447         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
448                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
449
450         priv = GET_PRIV (message);
451
452         return priv->type;
453 }
454
455 EmpathyContact *
456 empathy_message_get_sender (EmpathyMessage *message)
457 {
458         EmpathyMessagePriv *priv;
459
460         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
461
462         priv = GET_PRIV (message);
463
464         return priv->sender;
465 }
466
467 void
468 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
469 {
470         EmpathyMessagePriv *priv;
471         EmpathyContact     *old_sender;
472
473         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
474         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
475
476         priv = GET_PRIV (message);
477
478         old_sender = priv->sender;
479         priv->sender = g_object_ref (contact);
480
481         if (old_sender) {
482                 g_object_unref (old_sender);
483         }
484
485         g_object_notify (G_OBJECT (message), "sender");
486 }
487
488 EmpathyContact *
489 empathy_message_get_receiver (EmpathyMessage *message)
490 {
491         EmpathyMessagePriv *priv;
492
493         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
494
495         priv = GET_PRIV (message);
496
497         return priv->receiver;
498 }
499
500 void
501 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
502 {
503         EmpathyMessagePriv *priv;
504         EmpathyContact     *old_receiver;
505
506         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
507         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
508
509         priv = GET_PRIV (message);
510
511         old_receiver = priv->receiver;
512         priv->receiver = g_object_ref (contact);
513
514         if (old_receiver) {
515                 g_object_unref (old_receiver);
516         }
517
518         g_object_notify (G_OBJECT (message), "receiver");
519 }
520
521 const gchar *
522 empathy_message_get_token (EmpathyMessage *message)
523 {
524         EmpathyMessagePriv *priv;
525
526         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
527
528         priv = GET_PRIV (message);
529
530         return priv->token;
531 }
532
533 const gchar *
534 empathy_message_get_supersedes (EmpathyMessage *message)
535 {
536         EmpathyMessagePriv *priv;
537
538         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
539
540         priv = GET_PRIV (message);
541
542         return priv->supersedes;
543 }
544
545 gboolean
546 empathy_message_is_edit (EmpathyMessage *message)
547 {
548         EmpathyMessagePriv *priv;
549
550         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
551
552         priv = GET_PRIV (message);
553
554         return !tp_str_empty (priv->supersedes);
555 }
556
557 const gchar *
558 empathy_message_get_body (EmpathyMessage *message)
559 {
560         EmpathyMessagePriv *priv;
561
562         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
563
564         priv = GET_PRIV (message);
565
566         return priv->body;
567 }
568
569 gint64
570 empathy_message_get_timestamp (EmpathyMessage *message)
571 {
572         EmpathyMessagePriv *priv;
573
574         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
575
576         priv = GET_PRIV (message);
577
578         return priv->timestamp;
579 }
580
581 gint64
582 empathy_message_get_original_timestamp (EmpathyMessage *message)
583 {
584         EmpathyMessagePriv *priv;
585
586         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
587
588         priv = GET_PRIV (message);
589
590         return priv->original_timestamp;
591 }
592
593 gboolean
594 empathy_message_is_backlog (EmpathyMessage *message)
595 {
596         EmpathyMessagePriv *priv;
597
598         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
599
600         priv = GET_PRIV (message);
601
602         return priv->is_backlog;
603 }
604
605 TpChannelTextMessageType
606 empathy_message_type_from_str (const gchar *type_str)
607 {
608         if (strcmp (type_str, "normal") == 0) {
609                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
610         }
611         if (strcmp (type_str, "action") == 0) {
612                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
613         }
614         else if (strcmp (type_str, "notice") == 0) {
615                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
616         }
617         else if (strcmp (type_str, "auto-reply") == 0) {
618                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
619         }
620
621         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
622 }
623
624 const gchar *
625 empathy_message_type_to_str (TpChannelTextMessageType type)
626 {
627         switch (type) {
628         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
629                 return "action";
630         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
631                 return "notice";
632         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
633                 return "auto-reply";
634         case TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT:
635                 return "delivery-report";
636         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
637         default:
638                 return "normal";
639         }
640 }
641
642 gboolean
643 empathy_message_is_incoming (EmpathyMessage *message)
644 {
645         EmpathyMessagePriv *priv = GET_PRIV (message);
646
647         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
648
649         return priv->incoming;
650 }
651
652 gboolean
653 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
654 {
655         EmpathyMessagePriv *priv1;
656         EmpathyMessagePriv *priv2;
657
658         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
659         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
660
661         priv1 = GET_PRIV (message1);
662         priv2 = GET_PRIV (message2);
663
664         if (priv1->timestamp == priv2->timestamp &&
665                         !tp_strdiff (priv1->body, priv2->body)) {
666                 return TRUE;
667         }
668
669         return FALSE;
670 }
671
672 EmpathyMessage *
673 empathy_message_new_from_tp_message (TpMessage *tp_msg,
674                                      gboolean incoming)
675 {
676         EmpathyMessage *message;
677         gchar *body;
678         gint64 timestamp;
679         gint64 original_timestamp;
680         const GHashTable *part = tp_message_peek (tp_msg, 0);
681
682         g_return_val_if_fail (TP_IS_MESSAGE (tp_msg), NULL);
683
684         body = tp_message_to_text (tp_msg, NULL);
685
686         timestamp = tp_message_get_sent_timestamp (tp_msg);
687         if (timestamp == 0)
688                 timestamp = tp_message_get_received_timestamp (tp_msg);
689
690         original_timestamp = tp_asv_get_int64 (part,
691                 "original-message-received", NULL);
692
693         message = g_object_new (EMPATHY_TYPE_MESSAGE,
694                 "body", body,
695                 "token", tp_message_get_token (tp_msg),
696                 "supersedes", tp_message_get_supersedes (tp_msg),
697                 "type", tp_message_get_message_type (tp_msg),
698                 "timestamp", timestamp,
699                 "original-timestamp", original_timestamp,
700                 "is-backlog", tp_message_is_scrollback (tp_msg),
701                 "incoming", incoming,
702                 "tp-message", tp_msg,
703                 NULL);
704
705         g_free (body);
706         return message;
707 }