1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2004-2007 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Xavier Claessens <xclaesse@gmail.com>
29 #include <telepathy-glib/util.h>
30 #include <telepathy-glib/account.h>
31 #include <telepathy-glib/account-manager.h>
33 #include <telepathy-logger/entity.h>
34 #include <telepathy-logger/event.h>
35 #include <telepathy-logger/text-event.h>
37 #include "empathy-message.h"
38 #include "empathy-utils.h"
39 #include "empathy-enum-types.h"
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
43 TpMessage
*tp_message
;
44 TpChannelTextMessageType type
;
45 EmpathyContact
*sender
;
46 EmpathyContact
*receiver
;
52 TpChannelTextMessageFlags flags
;
55 static void empathy_message_finalize (GObject
*object
);
56 static void message_get_property (GObject
*object
,
60 static void message_set_property (GObject
*object
,
65 G_DEFINE_TYPE (EmpathyMessage
, empathy_message
, G_TYPE_OBJECT
);
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 ("body",
118 "The content of the message",
120 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
121 G_PARAM_CONSTRUCT_ONLY
));
122 g_object_class_install_property (object_class
,
124 g_param_spec_int64 ("timestamp",
127 G_MININT64
, G_MAXINT64
, 0,
128 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
129 G_PARAM_CONSTRUCT_ONLY
));
130 g_object_class_install_property (object_class
,
132 g_param_spec_boolean ("is-backlog",
134 "If the message belongs to history",
136 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
137 G_PARAM_CONSTRUCT_ONLY
));
140 g_object_class_install_property (object_class
,
142 g_param_spec_boolean ("incoming",
144 "If this is an incoming (as opposed to sent) message",
146 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
147 G_PARAM_CONSTRUCT_ONLY
));
149 g_object_class_install_property (object_class
,
151 g_param_spec_uint ("flags",
153 "The TpChannelTextMessageFlags of this message",
155 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
156 G_PARAM_CONSTRUCT_ONLY
));
158 g_object_class_install_property (object_class
,
160 g_param_spec_object ("tp-message",
162 "The TpMessage of this message",
164 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
|
165 G_PARAM_CONSTRUCT_ONLY
));
167 g_type_class_add_private (object_class
, sizeof (EmpathyMessagePriv
));
172 empathy_message_init (EmpathyMessage
*message
)
174 EmpathyMessagePriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (message
,
175 EMPATHY_TYPE_MESSAGE
, EmpathyMessagePriv
);
177 message
->priv
= priv
;
178 priv
->timestamp
= empathy_time_get_current ();
182 empathy_message_finalize (GObject
*object
)
184 EmpathyMessagePriv
*priv
;
186 priv
= GET_PRIV (object
);
189 g_object_unref (priv
->sender
);
191 if (priv
->receiver
) {
192 g_object_unref (priv
->receiver
);
195 if (priv
->tp_message
) {
196 g_object_unref (priv
->tp_message
);
201 G_OBJECT_CLASS (empathy_message_parent_class
)->finalize (object
);
205 message_get_property (GObject
*object
,
210 EmpathyMessagePriv
*priv
;
212 priv
= GET_PRIV (object
);
216 g_value_set_uint (value
, priv
->type
);
219 g_value_set_object (value
, priv
->sender
);
222 g_value_set_object (value
, priv
->receiver
);
225 g_value_set_string (value
, priv
->body
);
228 g_value_set_int64 (value
, priv
->timestamp
);
230 case PROP_IS_BACKLOG
:
231 g_value_set_boolean (value
, priv
->is_backlog
);
234 g_value_set_boolean (value
, priv
->incoming
);
237 g_value_set_uint (value
, priv
->flags
);
239 case PROP_TP_MESSAGE
:
240 g_value_set_object (value
, priv
->tp_message
);
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
249 message_set_property (GObject
*object
,
254 EmpathyMessagePriv
*priv
;
256 priv
= GET_PRIV (object
);
260 priv
->type
= g_value_get_uint (value
);
263 empathy_message_set_sender (EMPATHY_MESSAGE (object
),
264 EMPATHY_CONTACT (g_value_get_object (value
)));
267 empathy_message_set_receiver (EMPATHY_MESSAGE (object
),
268 EMPATHY_CONTACT (g_value_get_object (value
)));
271 g_assert (priv
->body
== NULL
); /* construct only */
272 priv
->body
= g_value_dup_string (value
);
275 priv
->timestamp
= g_value_get_int64 (value
);
276 if (priv
->timestamp
<= 0)
277 priv
->timestamp
= empathy_time_get_current ();
279 case PROP_IS_BACKLOG
:
280 priv
->is_backlog
= g_value_get_boolean (value
);
283 priv
->incoming
= g_value_get_boolean (value
);
286 priv
->flags
= g_value_get_uint (value
);
288 case PROP_TP_MESSAGE
:
289 priv
->tp_message
= g_value_dup_object (value
);
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
298 empathy_message_from_tpl_log_event (TplEvent
*logevent
)
300 EmpathyMessage
*retval
= NULL
;
301 TpAccountManager
*acc_man
= NULL
;
302 TpAccount
*account
= NULL
;
303 TplEntity
*receiver
= NULL
;
304 TplEntity
*sender
= NULL
;
306 EmpathyContact
*contact
;
308 g_return_val_if_fail (TPL_IS_EVENT (logevent
), NULL
);
310 acc_man
= tp_account_manager_dup ();
311 /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
312 * won't be selected any non-existing (ie removed) account.
313 * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
314 * might add a not existing account to the AM. tp_account_new () probably
315 * will be the best way to handle it.
316 * Note: When creating an EmpathyContact from a TplEntity instance, the
317 * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
318 * avatar (contact_get_avatar_filename () need a TpAccount).
319 * If the way EmpathyContact stores the avatar is changes, it might not be
320 * needed anymore any TpAccount passing and the following call will be
322 account
= tp_account_manager_ensure_account (acc_man
,
323 tpl_event_get_account_path (logevent
));
324 g_object_unref (acc_man
);
326 /* TODO Currently only TplTextEvent exists as subclass of TplEvent, in
327 * future more TplEvent will exist and EmpathyMessage should probably
328 * be enhanced to support other types of log entries (ie TplCallEvent).
330 * For now we just check (simply) that we are dealing with the only supported type,
331 * then there will be a if/then/else or switch handling all the supported
334 if (!TPL_IS_TEXT_EVENT (logevent
))
337 body
= g_strdup (tpl_text_event_get_message (
338 TPL_TEXT_EVENT (logevent
)));
339 receiver
= tpl_event_get_receiver (logevent
);
340 sender
= tpl_event_get_sender (logevent
);
342 retval
= g_object_new (EMPATHY_TYPE_MESSAGE
,
343 "type", tpl_text_event_get_message_type (TPL_TEXT_EVENT (logevent
)),
346 "timestamp", tpl_event_get_timestamp (logevent
),
349 if (receiver
!= NULL
) {
350 contact
= empathy_contact_from_tpl_contact (account
, receiver
);
351 empathy_message_set_receiver (retval
, contact
);
352 g_object_unref (contact
);
355 if (sender
!= NULL
) {
356 contact
= empathy_contact_from_tpl_contact (account
, sender
);
357 empathy_message_set_sender (retval
, contact
);
358 g_object_unref (contact
);
367 empathy_message_get_tp_message (EmpathyMessage
*message
)
369 EmpathyMessagePriv
*priv
;
371 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), NULL
);
373 priv
= GET_PRIV (message
);
375 return priv
->tp_message
;
378 TpChannelTextMessageType
379 empathy_message_get_tptype (EmpathyMessage
*message
)
381 EmpathyMessagePriv
*priv
;
383 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
),
384 TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
);
386 priv
= GET_PRIV (message
);
392 empathy_message_get_sender (EmpathyMessage
*message
)
394 EmpathyMessagePriv
*priv
;
396 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), NULL
);
398 priv
= GET_PRIV (message
);
404 empathy_message_set_sender (EmpathyMessage
*message
, EmpathyContact
*contact
)
406 EmpathyMessagePriv
*priv
;
407 EmpathyContact
*old_sender
;
409 g_return_if_fail (EMPATHY_IS_MESSAGE (message
));
410 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
412 priv
= GET_PRIV (message
);
414 old_sender
= priv
->sender
;
415 priv
->sender
= g_object_ref (contact
);
418 g_object_unref (old_sender
);
421 g_object_notify (G_OBJECT (message
), "sender");
425 empathy_message_get_receiver (EmpathyMessage
*message
)
427 EmpathyMessagePriv
*priv
;
429 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), NULL
);
431 priv
= GET_PRIV (message
);
433 return priv
->receiver
;
437 empathy_message_set_receiver (EmpathyMessage
*message
, EmpathyContact
*contact
)
439 EmpathyMessagePriv
*priv
;
440 EmpathyContact
*old_receiver
;
442 g_return_if_fail (EMPATHY_IS_MESSAGE (message
));
443 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
445 priv
= GET_PRIV (message
);
447 old_receiver
= priv
->receiver
;
448 priv
->receiver
= g_object_ref (contact
);
451 g_object_unref (old_receiver
);
454 g_object_notify (G_OBJECT (message
), "receiver");
458 empathy_message_get_body (EmpathyMessage
*message
)
460 EmpathyMessagePriv
*priv
;
462 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), NULL
);
464 priv
= GET_PRIV (message
);
470 empathy_message_get_timestamp (EmpathyMessage
*message
)
472 EmpathyMessagePriv
*priv
;
474 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), -1);
476 priv
= GET_PRIV (message
);
478 return priv
->timestamp
;
482 empathy_message_is_backlog (EmpathyMessage
*message
)
484 EmpathyMessagePriv
*priv
;
486 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), FALSE
);
488 priv
= GET_PRIV (message
);
490 return priv
->is_backlog
;
493 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
495 empathy_message_should_highlight (EmpathyMessage
*message
)
497 EmpathyContact
*contact
;
498 const gchar
*msg
, *to
;
499 gchar
*cf_msg
, *cf_to
;
502 TpChannelTextMessageFlags flags
;
504 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), FALSE
);
508 msg
= empathy_message_get_body (message
);
513 contact
= empathy_message_get_receiver (message
);
514 if (!contact
|| !empathy_contact_is_user (contact
)) {
518 to
= empathy_contact_get_alias (contact
);
523 flags
= empathy_message_get_flags (message
);
524 if (flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK
) {
525 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
526 * have already been received by the user before (and so are in the logs) */
530 cf_msg
= g_utf8_casefold (msg
, -1);
531 cf_to
= g_utf8_casefold (to
, -1);
533 ch
= strstr (cf_msg
, cf_to
);
538 /* Not first in the message */
539 if (!IS_SEPARATOR (*(ch
- 1))) {
544 ch
= ch
+ strlen (cf_to
);
545 if (ch
>= cf_msg
+ strlen (cf_msg
)) {
550 if (IS_SEPARATOR (*ch
)) {
562 TpChannelTextMessageType
563 empathy_message_type_from_str (const gchar
*type_str
)
565 if (strcmp (type_str
, "normal") == 0) {
566 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
;
568 if (strcmp (type_str
, "action") == 0) {
569 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION
;
571 else if (strcmp (type_str
, "notice") == 0) {
572 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE
;
574 else if (strcmp (type_str
, "auto-reply") == 0) {
575 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY
;
578 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
;
582 empathy_message_type_to_str (TpChannelTextMessageType type
)
585 case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION
:
587 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE
:
589 case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY
:
591 case TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT
:
592 return "delivery-report";
593 case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
:
600 empathy_message_is_incoming (EmpathyMessage
*message
)
602 EmpathyMessagePriv
*priv
= GET_PRIV (message
);
604 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message
), FALSE
);
606 return priv
->incoming
;
610 empathy_message_equal (EmpathyMessage
*message1
, EmpathyMessage
*message2
)
612 EmpathyMessagePriv
*priv1
;
613 EmpathyMessagePriv
*priv2
;
615 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1
), FALSE
);
616 g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2
), FALSE
);
618 priv1
= GET_PRIV (message1
);
619 priv2
= GET_PRIV (message2
);
621 if (priv1
->timestamp
== priv2
->timestamp
&&
622 !tp_strdiff (priv1
->body
, priv2
->body
)) {
629 TpChannelTextMessageFlags
630 empathy_message_get_flags (EmpathyMessage
*self
)
632 EmpathyMessagePriv
*priv
= GET_PRIV (self
);
634 g_return_val_if_fail (EMPATHY_IS_MESSAGE (self
), 0);
640 empathy_message_new_from_tp_message (TpMessage
*tp_msg
,
643 EmpathyMessage
*message
;
645 TpChannelTextMessageFlags flags
;
647 g_return_val_if_fail (TP_IS_MESSAGE (tp_msg
), NULL
);
649 body
= tp_message_to_text (tp_msg
, &flags
);
651 message
= g_object_new (EMPATHY_TYPE_MESSAGE
,
653 "type", tp_message_get_message_type (tp_msg
),
654 "timestamp", tp_message_get_received_timestamp (tp_msg
),
656 "is-backlog", flags
& TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK
,
657 "incoming", incoming
,
658 "tp-message", tp_msg
,