empathy_message_equal: always use the timestamp+body (#621797)
[empathy-mirror.git] / libempathy / empathy-message.c
blob85d5cbc588bf107e5cce92eef8057e2d71b03540
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.
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>
25 #include "config.h"
27 #include <string.h>
29 #include <telepathy-glib/util.h>
30 #ifdef ENABLE_TPL
31 #include <telepathy-glib/account.h>
32 #include <telepathy-glib/account-manager.h>
34 #include <telepathy-logger/contact.h>
35 #include <telepathy-logger/log-entry.h>
36 #include <telepathy-logger/log-entry-text.h>
37 #endif /* ENABLE_TPL */
39 #include "empathy-message.h"
40 #include "empathy-utils.h"
41 #include "empathy-enum-types.h"
43 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
44 typedef struct {
45 TpChannelTextMessageType type;
46 EmpathyContact *sender;
47 EmpathyContact *receiver;
48 gchar *body;
49 time_t timestamp;
50 gboolean is_backlog;
51 guint id;
52 gboolean incoming;
53 TpChannelTextMessageFlags flags;
54 } EmpathyMessagePriv;
56 static void empathy_message_finalize (GObject *object);
57 static void message_get_property (GObject *object,
58 guint param_id,
59 GValue *value,
60 GParamSpec *pspec);
61 static void message_set_property (GObject *object,
62 guint param_id,
63 const GValue *value,
64 GParamSpec *pspec);
66 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
68 enum {
69 PROP_0,
70 PROP_TYPE,
71 PROP_SENDER,
72 PROP_RECEIVER,
73 PROP_BODY,
74 PROP_TIMESTAMP,
75 PROP_IS_BACKLOG,
76 PROP_INCOMING,
77 PROP_FLAGS,
80 static void
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,
91 PROP_TYPE,
92 g_param_spec_uint ("type",
93 "Message 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));
99 g_object_class_install_property (object_class,
100 PROP_SENDER,
101 g_param_spec_object ("sender",
102 "Message Sender",
103 "The sender of the message",
104 EMPATHY_TYPE_CONTACT,
105 G_PARAM_READWRITE));
106 g_object_class_install_property (object_class,
107 PROP_RECEIVER,
108 g_param_spec_object ("receiver",
109 "Message Receiver",
110 "The receiver of the message",
111 EMPATHY_TYPE_CONTACT,
112 G_PARAM_READWRITE));
113 g_object_class_install_property (object_class,
114 PROP_BODY,
115 g_param_spec_string ("body",
116 "Message Body",
117 "The content of the message",
118 NULL,
119 G_PARAM_READWRITE));
120 g_object_class_install_property (object_class,
121 PROP_TIMESTAMP,
122 g_param_spec_long ("timestamp",
123 "timestamp",
124 "timestamp",
126 G_MAXLONG,
128 G_PARAM_READWRITE));
129 g_object_class_install_property (object_class,
130 PROP_IS_BACKLOG,
131 g_param_spec_boolean ("is-backlog",
132 "History message",
133 "If the message belongs to history",
134 FALSE,
135 G_PARAM_READWRITE));
138 g_object_class_install_property (object_class,
139 PROP_INCOMING,
140 g_param_spec_boolean ("incoming",
141 "Incoming",
142 "If this is an incoming (as opposed to sent) message",
143 FALSE,
144 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146 g_object_class_install_property (object_class,
147 PROP_FLAGS,
148 g_param_spec_uint ("flags",
149 "Flags",
150 "The TpChannelTextMessageFlags of this message",
151 0, G_MAXUINT, 0,
152 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
154 g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
158 static void
159 empathy_message_init (EmpathyMessage *message)
161 EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
162 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
164 message->priv = priv;
165 priv->timestamp = empathy_time_get_current ();
168 static void
169 empathy_message_finalize (GObject *object)
171 EmpathyMessagePriv *priv;
173 priv = GET_PRIV (object);
175 if (priv->sender) {
176 g_object_unref (priv->sender);
178 if (priv->receiver) {
179 g_object_unref (priv->receiver);
182 g_free (priv->body);
184 G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
187 static void
188 message_get_property (GObject *object,
189 guint param_id,
190 GValue *value,
191 GParamSpec *pspec)
193 EmpathyMessagePriv *priv;
195 priv = GET_PRIV (object);
197 switch (param_id) {
198 case PROP_TYPE:
199 g_value_set_uint (value, priv->type);
200 break;
201 case PROP_SENDER:
202 g_value_set_object (value, priv->sender);
203 break;
204 case PROP_RECEIVER:
205 g_value_set_object (value, priv->receiver);
206 break;
207 case PROP_BODY:
208 g_value_set_string (value, priv->body);
209 break;
210 case PROP_INCOMING:
211 g_value_set_boolean (value, priv->incoming);
212 break;
213 default:
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
215 break;
219 static void
220 message_set_property (GObject *object,
221 guint param_id,
222 const GValue *value,
223 GParamSpec *pspec)
225 EmpathyMessagePriv *priv;
227 priv = GET_PRIV (object);
229 switch (param_id) {
230 case PROP_TYPE:
231 empathy_message_set_tptype (EMPATHY_MESSAGE (object),
232 g_value_get_uint (value));
233 break;
234 case PROP_SENDER:
235 empathy_message_set_sender (EMPATHY_MESSAGE (object),
236 EMPATHY_CONTACT (g_value_get_object (value)));
237 break;
238 case PROP_RECEIVER:
239 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
240 EMPATHY_CONTACT (g_value_get_object (value)));
241 break;
242 case PROP_BODY:
243 empathy_message_set_body (EMPATHY_MESSAGE (object),
244 g_value_get_string (value));
245 break;
246 case PROP_INCOMING:
247 priv->incoming = g_value_get_boolean (value);
248 break;
249 default:
250 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
251 break;
255 EmpathyMessage *
256 empathy_message_new (const gchar *body)
258 return g_object_new (EMPATHY_TYPE_MESSAGE,
259 "body", body,
260 NULL);
263 #ifdef ENABLE_TPL
264 EmpathyMessage *
265 empathy_message_from_tpl_log_entry (TplLogEntry *logentry)
267 EmpathyMessage *retval = NULL;
268 TpAccountManager *acc_man = NULL;
269 TpAccount *account = NULL;
270 TplContact *receiver = NULL;
271 TplContact *sender = NULL;
272 gchar *body= NULL;
274 g_return_val_if_fail (TPL_IS_LOG_ENTRY (logentry), NULL);
276 acc_man = tp_account_manager_dup ();
277 /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
278 * won't be selected any non-existing (ie removed) account.
279 * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
280 * might add a not existing account to the AM. tp_account_new () probably
281 * will be the best way to handle it.
282 * Note: When creating an EmpathyContact from a TplContact instance, the
283 * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
284 * avatar (contact_get_avatar_filename () need a TpAccount).
285 * If the way EmpathyContact stores the avatar is changes, it might not be
286 * needed anymore any TpAccount passing and the following call will be
287 * useless */
288 account = tp_account_manager_ensure_account (acc_man,
289 tpl_log_entry_get_account_path (logentry));
290 g_object_unref (acc_man);
292 /* TODO Currently only TplLogEntryText exists as subclass of TplLogEntry, in
293 * future more TplLogEntry will exist and EmpathyMessage should probably
294 * be enhanced to support other types of log entries (ie TplLogEntryCall).
296 * For now we just check (simply) that we are dealing with the only supported type,
297 * then there will be a if/then/else or switch handling all the supported
298 * cases.
300 if (!TPL_IS_LOG_ENTRY_TEXT (logentry))
301 return NULL;
303 body = g_strdup (tpl_log_entry_text_get_message (
304 TPL_LOG_ENTRY_TEXT (logentry)));
305 receiver = tpl_log_entry_text_get_receiver (TPL_LOG_ENTRY_TEXT (logentry));
306 sender = tpl_log_entry_text_get_sender (TPL_LOG_ENTRY_TEXT (logentry));
308 retval = empathy_message_new (body);
309 if (receiver != NULL)
310 empathy_message_set_receiver (retval,
311 empathy_contact_from_tpl_contact (account, receiver));
312 if (sender != NULL)
313 empathy_message_set_sender (retval,
314 empathy_contact_from_tpl_contact (account, sender));
316 empathy_message_set_timestamp (retval,
317 tpl_log_entry_get_timestamp (logentry));
318 empathy_message_set_id (retval,
319 tpl_log_entry_text_get_log_id (TPL_LOG_ENTRY_TEXT (logentry)));
320 empathy_message_set_is_backlog (retval, FALSE);
322 g_free (body);
324 return retval;
326 #endif /* ENABLE_TPL */
328 TpChannelTextMessageType
329 empathy_message_get_tptype (EmpathyMessage *message)
331 EmpathyMessagePriv *priv;
333 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
334 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
336 priv = GET_PRIV (message);
338 return priv->type;
341 void
342 empathy_message_set_tptype (EmpathyMessage *message,
343 TpChannelTextMessageType type)
345 EmpathyMessagePriv *priv;
347 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
349 priv = GET_PRIV (message);
351 priv->type = type;
353 g_object_notify (G_OBJECT (message), "type");
356 EmpathyContact *
357 empathy_message_get_sender (EmpathyMessage *message)
359 EmpathyMessagePriv *priv;
361 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
363 priv = GET_PRIV (message);
365 return priv->sender;
368 void
369 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
371 EmpathyMessagePriv *priv;
372 EmpathyContact *old_sender;
374 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
375 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
377 priv = GET_PRIV (message);
379 old_sender = priv->sender;
380 priv->sender = g_object_ref (contact);
382 if (old_sender) {
383 g_object_unref (old_sender);
386 g_object_notify (G_OBJECT (message), "sender");
389 EmpathyContact *
390 empathy_message_get_receiver (EmpathyMessage *message)
392 EmpathyMessagePriv *priv;
394 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
396 priv = GET_PRIV (message);
398 return priv->receiver;
401 void
402 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
404 EmpathyMessagePriv *priv;
405 EmpathyContact *old_receiver;
407 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
408 g_return_if_fail (EMPATHY_IS_CONTACT (contact));
410 priv = GET_PRIV (message);
412 old_receiver = priv->receiver;
413 priv->receiver = g_object_ref (contact);
415 if (old_receiver) {
416 g_object_unref (old_receiver);
419 g_object_notify (G_OBJECT (message), "receiver");
422 const gchar *
423 empathy_message_get_body (EmpathyMessage *message)
425 EmpathyMessagePriv *priv;
427 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
429 priv = GET_PRIV (message);
431 return priv->body;
434 void
435 empathy_message_set_body (EmpathyMessage *message,
436 const gchar *body)
438 EmpathyMessagePriv *priv = GET_PRIV (message);
440 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
442 g_free (priv->body);
444 if (body) {
445 priv->body = g_strdup (body);
446 } else {
447 priv->body = NULL;
450 g_object_notify (G_OBJECT (message), "body");
453 time_t
454 empathy_message_get_timestamp (EmpathyMessage *message)
456 EmpathyMessagePriv *priv;
458 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
460 priv = GET_PRIV (message);
462 return priv->timestamp;
465 void
466 empathy_message_set_timestamp (EmpathyMessage *message,
467 time_t timestamp)
469 EmpathyMessagePriv *priv;
471 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
472 g_return_if_fail (timestamp >= -1);
474 priv = GET_PRIV (message);
476 if (timestamp <= 0) {
477 priv->timestamp = empathy_time_get_current ();
478 } else {
479 priv->timestamp = timestamp;
482 g_object_notify (G_OBJECT (message), "timestamp");
485 gboolean
486 empathy_message_is_backlog (EmpathyMessage *message)
488 EmpathyMessagePriv *priv;
490 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
492 priv = GET_PRIV (message);
494 return priv->is_backlog;
497 void
498 empathy_message_set_is_backlog (EmpathyMessage *message,
499 gboolean is_backlog)
501 EmpathyMessagePriv *priv;
503 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
505 priv = GET_PRIV (message);
507 priv->is_backlog = is_backlog;
509 g_object_notify (G_OBJECT (message), "is-backlog");
512 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
513 gboolean
514 empathy_message_should_highlight (EmpathyMessage *message)
516 EmpathyContact *contact;
517 const gchar *msg, *to;
518 gchar *cf_msg, *cf_to;
519 gchar *ch;
520 gboolean ret_val;
521 TpChannelTextMessageFlags flags;
523 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
525 ret_val = FALSE;
527 msg = empathy_message_get_body (message);
528 if (!msg) {
529 return FALSE;
532 contact = empathy_message_get_receiver (message);
533 if (!contact || !empathy_contact_is_user (contact)) {
534 return FALSE;
537 to = empathy_contact_get_name (contact);
538 if (!to) {
539 return FALSE;
542 flags = empathy_message_get_flags (message);
543 if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
544 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
545 * have already been received by the user before (and so are in the logs) */
546 return FALSE;
549 cf_msg = g_utf8_casefold (msg, -1);
550 cf_to = g_utf8_casefold (to, -1);
552 ch = strstr (cf_msg, cf_to);
553 if (ch == NULL) {
554 goto finished;
556 if (ch != cf_msg) {
557 /* Not first in the message */
558 if (!IS_SEPARATOR (*(ch - 1))) {
559 goto finished;
563 ch = ch + strlen (cf_to);
564 if (ch >= cf_msg + strlen (cf_msg)) {
565 ret_val = TRUE;
566 goto finished;
569 if (IS_SEPARATOR (*ch)) {
570 ret_val = TRUE;
571 goto finished;
574 finished:
575 g_free (cf_msg);
576 g_free (cf_to);
578 return ret_val;
581 TpChannelTextMessageType
582 empathy_message_type_from_str (const gchar *type_str)
584 if (strcmp (type_str, "normal") == 0) {
585 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
587 if (strcmp (type_str, "action") == 0) {
588 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
590 else if (strcmp (type_str, "notice") == 0) {
591 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
593 else if (strcmp (type_str, "auto-reply") == 0) {
594 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
597 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
600 const gchar *
601 empathy_message_type_to_str (TpChannelTextMessageType type)
603 switch (type) {
604 case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
605 return "action";
606 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
607 return "notice";
608 case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
609 return "auto-reply";
610 default:
611 return "normal";
615 guint
616 empathy_message_get_id (EmpathyMessage *message)
618 EmpathyMessagePriv *priv = GET_PRIV (message);
620 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
622 return priv->id;
625 void
626 empathy_message_set_id (EmpathyMessage *message, guint id)
628 EmpathyMessagePriv *priv = GET_PRIV (message);
630 priv->id = id;
633 void
634 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
636 EmpathyMessagePriv *priv;
638 g_return_if_fail (EMPATHY_IS_MESSAGE (message));
640 priv = GET_PRIV (message);
642 priv->incoming = incoming;
644 g_object_notify (G_OBJECT (message), "incoming");
647 gboolean
648 empathy_message_is_incoming (EmpathyMessage *message)
650 EmpathyMessagePriv *priv = GET_PRIV (message);
652 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
654 return priv->incoming;
657 gboolean
658 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
660 EmpathyMessagePriv *priv1;
661 EmpathyMessagePriv *priv2;
663 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
664 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
666 priv1 = GET_PRIV (message1);
667 priv2 = GET_PRIV (message2);
669 if (priv1->timestamp == priv2->timestamp &&
670 !tp_strdiff (priv1->body, priv2->body)) {
671 return TRUE;
674 return FALSE;
677 TpChannelTextMessageFlags
678 empathy_message_get_flags (EmpathyMessage *self)
680 EmpathyMessagePriv *priv = GET_PRIV (self);
682 g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
684 return priv->flags;
687 void
688 empathy_message_set_flags (EmpathyMessage *self,
689 TpChannelTextMessageFlags flags)
691 EmpathyMessagePriv *priv;
693 g_return_if_fail (EMPATHY_IS_MESSAGE (self));
695 priv = GET_PRIV (self);
697 priv->flags = flags;
699 g_object_notify (G_OBJECT (self), "flags");