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