]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
Merge EmpathyContact:name and *_set_alias() to EmpathyContact:alias
[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 <telepathy-glib/util.h>
30 #include <telepathy-glib/account.h>
31 #include <telepathy-glib/account-manager.h>
32
33 #include <telepathy-logger/entity.h>
34 #include <telepathy-logger/entry.h>
35 #include <telepathy-logger/entry-text.h>
36
37 #include "empathy-message.h"
38 #include "empathy-utils.h"
39 #include "empathy-enum-types.h"
40
41 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
42 typedef struct {
43         TpChannelTextMessageType  type;
44         EmpathyContact           *sender;
45         EmpathyContact           *receiver;
46         gchar                    *body;
47         time_t                    timestamp;
48         gboolean                  is_backlog;
49         guint                     id;
50         gboolean                  incoming;
51         TpChannelTextMessageFlags flags;
52 } EmpathyMessagePriv;
53
54 static void empathy_message_finalize   (GObject            *object);
55 static void message_get_property      (GObject            *object,
56                                        guint               param_id,
57                                        GValue             *value,
58                                        GParamSpec         *pspec);
59 static void message_set_property      (GObject            *object,
60                                        guint               param_id,
61                                        const GValue       *value,
62                                        GParamSpec         *pspec);
63
64 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
65
66 enum {
67         PROP_0,
68         PROP_TYPE,
69         PROP_SENDER,
70         PROP_RECEIVER,
71         PROP_BODY,
72         PROP_TIMESTAMP,
73         PROP_IS_BACKLOG,
74         PROP_INCOMING,
75         PROP_FLAGS,
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));
97         g_object_class_install_property (object_class,
98                                          PROP_SENDER,
99                                          g_param_spec_object ("sender",
100                                                               "Message Sender",
101                                                               "The sender of the message",
102                                                               EMPATHY_TYPE_CONTACT,
103                                                               G_PARAM_READWRITE));
104         g_object_class_install_property (object_class,
105                                          PROP_RECEIVER,
106                                          g_param_spec_object ("receiver",
107                                                               "Message Receiver",
108                                                               "The receiver of the message",
109                                                               EMPATHY_TYPE_CONTACT,
110                                                               G_PARAM_READWRITE));
111         g_object_class_install_property (object_class,
112                                          PROP_BODY,
113                                          g_param_spec_string ("body",
114                                                               "Message Body",
115                                                               "The content of the message",
116                                                               NULL,
117                                                               G_PARAM_READWRITE));
118         g_object_class_install_property (object_class,
119                                          PROP_TIMESTAMP,
120                                          g_param_spec_long ("timestamp",
121                                                             "timestamp",
122                                                             "timestamp",
123                                                             -1,
124                                                             G_MAXLONG,
125                                                             -1,
126                                                             G_PARAM_READWRITE));
127         g_object_class_install_property (object_class,
128                                          PROP_IS_BACKLOG,
129                                          g_param_spec_boolean ("is-backlog",
130                                                                "History message",
131                                                                "If the message belongs to history",
132                                                                FALSE,
133                                                                G_PARAM_READWRITE));
134
135
136         g_object_class_install_property (object_class,
137                                          PROP_INCOMING,
138                                          g_param_spec_boolean ("incoming",
139                                                                "Incoming",
140                                                                "If this is an incoming (as opposed to sent) message",
141                                                                FALSE,
142                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
143
144         g_object_class_install_property (object_class,
145                                          PROP_FLAGS,
146                                          g_param_spec_uint ("flags",
147                                                                "Flags",
148                                                                "The TpChannelTextMessageFlags of this message",
149                                                                0, G_MAXUINT, 0,
150                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151
152         g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
153
154 }
155
156 static void
157 empathy_message_init (EmpathyMessage *message)
158 {
159         EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
160                 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
161
162         message->priv = priv;
163         priv->timestamp = empathy_time_get_current ();
164 }
165
166 static void
167 empathy_message_finalize (GObject *object)
168 {
169         EmpathyMessagePriv *priv;
170
171         priv = GET_PRIV (object);
172
173         if (priv->sender) {
174                 g_object_unref (priv->sender);
175         }
176         if (priv->receiver) {
177                 g_object_unref (priv->receiver);
178         }
179
180         g_free (priv->body);
181
182         G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
183 }
184
185 static void
186 message_get_property (GObject    *object,
187                       guint       param_id,
188                       GValue     *value,
189                       GParamSpec *pspec)
190 {
191         EmpathyMessagePriv *priv;
192
193         priv = GET_PRIV (object);
194
195         switch (param_id) {
196         case PROP_TYPE:
197                 g_value_set_uint (value, priv->type);
198                 break;
199         case PROP_SENDER:
200                 g_value_set_object (value, priv->sender);
201                 break;
202         case PROP_RECEIVER:
203                 g_value_set_object (value, priv->receiver);
204                 break;
205         case PROP_BODY:
206                 g_value_set_string (value, priv->body);
207                 break;
208         case PROP_INCOMING:
209                 g_value_set_boolean (value, priv->incoming);
210                 break;
211         default:
212                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
213                 break;
214         };
215 }
216
217 static void
218 message_set_property (GObject      *object,
219                       guint         param_id,
220                       const GValue *value,
221                       GParamSpec   *pspec)
222 {
223         EmpathyMessagePriv *priv;
224
225         priv = GET_PRIV (object);
226
227         switch (param_id) {
228         case PROP_TYPE:
229                 empathy_message_set_tptype (EMPATHY_MESSAGE (object),
230                                             g_value_get_uint (value));
231                 break;
232         case PROP_SENDER:
233                 empathy_message_set_sender (EMPATHY_MESSAGE (object),
234                                            EMPATHY_CONTACT (g_value_get_object (value)));
235                 break;
236         case PROP_RECEIVER:
237                 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
238                                              EMPATHY_CONTACT (g_value_get_object (value)));
239                 break;
240         case PROP_BODY:
241                 empathy_message_set_body (EMPATHY_MESSAGE (object),
242                                          g_value_get_string (value));
243                 break;
244         case PROP_INCOMING:
245                 priv->incoming = g_value_get_boolean (value);
246                 break;
247         default:
248                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
249                 break;
250         };
251 }
252
253 EmpathyMessage *
254 empathy_message_new (const gchar *body)
255 {
256         return g_object_new (EMPATHY_TYPE_MESSAGE,
257                              "body", body,
258                              NULL);
259 }
260
261 EmpathyMessage *
262 empathy_message_from_tpl_log_entry (TplEntry *logentry)
263 {
264         EmpathyMessage *retval = NULL;
265         TpAccountManager *acc_man = NULL;
266         TpAccount *account = NULL;
267         TplEntity *receiver = NULL;
268         TplEntity *sender = NULL;
269         gchar *body= NULL;
270
271         g_return_val_if_fail (TPL_IS_ENTRY (logentry), NULL);
272
273         acc_man = tp_account_manager_dup ();
274         /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
275          * won't be selected any non-existing (ie removed) account.
276          * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
277          * might add a not existing account to the AM. tp_account_new () probably
278          * will be the best way to handle it.
279          * Note: When creating an EmpathyContact from a TplEntity instance, the
280          * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
281          * avatar (contact_get_avatar_filename () need a TpAccount).
282          * If the way EmpathyContact stores the avatar is changes, it might not be
283          * needed anymore any TpAccount passing and the following call will be
284          * useless */
285         account = tp_account_manager_ensure_account (acc_man,
286                         tpl_entry_get_account_path (logentry));
287         g_object_unref (acc_man);
288
289         /* TODO Currently only TplLogEntryText exists as subclass of TplEntry, in
290          * future more TplEntry will exist and EmpathyMessage should probably
291          * be enhanced to support other types of log entries (ie TplLogEntryCall).
292          *
293          * For now we just check (simply) that we are dealing with the only supported type,
294          * then there will be a if/then/else or switch handling all the supported
295          * cases.
296          */
297         if (!TPL_IS_ENTRY_TEXT (logentry))
298                 return NULL;
299
300         body = g_strdup (tpl_entry_text_get_message (
301                                 TPL_ENTRY_TEXT (logentry)));
302         receiver = tpl_entry_get_receiver (logentry);
303         sender = tpl_entry_get_sender (logentry);
304
305         retval = empathy_message_new (body);
306         if (receiver != NULL)
307                 empathy_message_set_receiver (retval,
308                                 empathy_contact_from_tpl_contact (account, receiver));
309         if (sender != NULL)
310                 empathy_message_set_sender (retval,
311                                 empathy_contact_from_tpl_contact (account, sender));
312
313         empathy_message_set_timestamp (retval,
314                         tpl_entry_get_timestamp (logentry));
315         empathy_message_set_id (retval,
316                         tpl_entry_text_get_pending_msg_id (TPL_ENTRY_TEXT (logentry)));
317         empathy_message_set_is_backlog (retval, TRUE);
318
319         g_free (body);
320
321         return retval;
322 }
323
324 TpChannelTextMessageType
325 empathy_message_get_tptype (EmpathyMessage *message)
326 {
327         EmpathyMessagePriv *priv;
328
329         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
330                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
331
332         priv = GET_PRIV (message);
333
334         return priv->type;
335 }
336
337 void
338 empathy_message_set_tptype (EmpathyMessage           *message,
339                             TpChannelTextMessageType  type)
340 {
341         EmpathyMessagePriv *priv;
342
343         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
344
345         priv = GET_PRIV (message);
346
347         priv->type = type;
348
349         g_object_notify (G_OBJECT (message), "type");
350 }
351
352 EmpathyContact *
353 empathy_message_get_sender (EmpathyMessage *message)
354 {
355         EmpathyMessagePriv *priv;
356
357         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
358
359         priv = GET_PRIV (message);
360
361         return priv->sender;
362 }
363
364 void
365 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
366 {
367         EmpathyMessagePriv *priv;
368         EmpathyContact     *old_sender;
369
370         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
371         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
372
373         priv = GET_PRIV (message);
374
375         old_sender = priv->sender;
376         priv->sender = g_object_ref (contact);
377
378         if (old_sender) {
379                 g_object_unref (old_sender);
380         }
381
382         g_object_notify (G_OBJECT (message), "sender");
383 }
384
385 EmpathyContact *
386 empathy_message_get_receiver (EmpathyMessage *message)
387 {
388         EmpathyMessagePriv *priv;
389
390         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
391
392         priv = GET_PRIV (message);
393
394         return priv->receiver;
395 }
396
397 void
398 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
399 {
400         EmpathyMessagePriv *priv;
401         EmpathyContact     *old_receiver;
402
403         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
404         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
405
406         priv = GET_PRIV (message);
407
408         old_receiver = priv->receiver;
409         priv->receiver = g_object_ref (contact);
410
411         if (old_receiver) {
412                 g_object_unref (old_receiver);
413         }
414
415         g_object_notify (G_OBJECT (message), "receiver");
416 }
417
418 const gchar *
419 empathy_message_get_body (EmpathyMessage *message)
420 {
421         EmpathyMessagePriv *priv;
422
423         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
424
425         priv = GET_PRIV (message);
426
427         return priv->body;
428 }
429
430 void
431 empathy_message_set_body (EmpathyMessage *message,
432                           const gchar    *body)
433 {
434         EmpathyMessagePriv       *priv = GET_PRIV (message);
435
436         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
437
438         g_free (priv->body);
439
440         if (body) {
441                 priv->body = g_strdup (body);
442         } else {
443                 priv->body = NULL;
444         }
445
446         g_object_notify (G_OBJECT (message), "body");
447 }
448
449 time_t
450 empathy_message_get_timestamp (EmpathyMessage *message)
451 {
452         EmpathyMessagePriv *priv;
453
454         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
455
456         priv = GET_PRIV (message);
457
458         return priv->timestamp;
459 }
460
461 void
462 empathy_message_set_timestamp (EmpathyMessage *message,
463                                time_t          timestamp)
464 {
465         EmpathyMessagePriv *priv;
466
467         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
468         g_return_if_fail (timestamp >= -1);
469
470         priv = GET_PRIV (message);
471
472         if (timestamp <= 0) {
473                 priv->timestamp = empathy_time_get_current ();
474         } else {
475                 priv->timestamp = timestamp;
476         }
477
478         g_object_notify (G_OBJECT (message), "timestamp");
479 }
480
481 gboolean
482 empathy_message_is_backlog (EmpathyMessage *message)
483 {
484         EmpathyMessagePriv *priv;
485
486         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
487
488         priv = GET_PRIV (message);
489
490         return priv->is_backlog;
491 }
492
493 void
494 empathy_message_set_is_backlog (EmpathyMessage *message,
495                                 gboolean is_backlog)
496 {
497         EmpathyMessagePriv *priv;
498
499         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
500
501         priv = GET_PRIV (message);
502
503         priv->is_backlog = is_backlog;
504
505         g_object_notify (G_OBJECT (message), "is-backlog");
506 }
507
508 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
509 gboolean
510 empathy_message_should_highlight (EmpathyMessage *message)
511 {
512         EmpathyContact *contact;
513         const gchar   *msg, *to;
514         gchar         *cf_msg, *cf_to;
515         gchar         *ch;
516         gboolean       ret_val;
517         TpChannelTextMessageFlags flags;
518
519         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
520
521         ret_val = FALSE;
522
523         msg = empathy_message_get_body (message);
524         if (!msg) {
525                 return FALSE;
526         }
527
528         contact = empathy_message_get_receiver (message);
529         if (!contact || !empathy_contact_is_user (contact)) {
530                 return FALSE;
531         }
532
533         to = empathy_contact_get_alias (contact);
534         if (!to) {
535                 return FALSE;
536         }
537
538         flags = empathy_message_get_flags (message);
539         if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
540                 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
541                  * have already been received by the user before (and so are in the logs) */
542                 return FALSE;
543         }
544
545         cf_msg = g_utf8_casefold (msg, -1);
546         cf_to = g_utf8_casefold (to, -1);
547
548         ch = strstr (cf_msg, cf_to);
549         if (ch == NULL) {
550                 goto finished;
551         }
552         if (ch != cf_msg) {
553                 /* Not first in the message */
554                 if (!IS_SEPARATOR (*(ch - 1))) {
555                         goto finished;
556                 }
557         }
558
559         ch = ch + strlen (cf_to);
560         if (ch >= cf_msg + strlen (cf_msg)) {
561                 ret_val = TRUE;
562                 goto finished;
563         }
564
565         if (IS_SEPARATOR (*ch)) {
566                 ret_val = TRUE;
567                 goto finished;
568         }
569
570 finished:
571         g_free (cf_msg);
572         g_free (cf_to);
573
574         return ret_val;
575 }
576
577 TpChannelTextMessageType
578 empathy_message_type_from_str (const gchar *type_str)
579 {
580         if (strcmp (type_str, "normal") == 0) {
581                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
582         }
583         if (strcmp (type_str, "action") == 0) {
584                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
585         }
586         else if (strcmp (type_str, "notice") == 0) {
587                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
588         }
589         else if (strcmp (type_str, "auto-reply") == 0) {
590                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
591         }
592
593         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
594 }
595
596 const gchar *
597 empathy_message_type_to_str (TpChannelTextMessageType type)
598 {
599         switch (type) {
600         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
601                 return "action";
602         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
603                 return "notice";
604         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
605                 return "auto-reply";
606         default:
607                 return "normal";
608         }
609 }
610
611 guint
612 empathy_message_get_id (EmpathyMessage *message)
613 {
614         EmpathyMessagePriv *priv = GET_PRIV (message);
615
616         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
617
618         return priv->id;
619 }
620
621 void
622 empathy_message_set_id (EmpathyMessage *message, guint id)
623 {
624         EmpathyMessagePriv *priv = GET_PRIV (message);
625
626         priv->id = id;
627 }
628
629 void
630 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
631 {
632         EmpathyMessagePriv *priv;
633
634         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
635
636         priv = GET_PRIV (message);
637
638         priv->incoming = incoming;
639
640         g_object_notify (G_OBJECT (message), "incoming");
641 }
642
643 gboolean
644 empathy_message_is_incoming (EmpathyMessage *message)
645 {
646         EmpathyMessagePriv *priv = GET_PRIV (message);
647
648         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
649
650         return priv->incoming;
651 }
652
653 gboolean
654 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
655 {
656         EmpathyMessagePriv *priv1;
657         EmpathyMessagePriv *priv2;
658
659         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
660         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
661
662         priv1 = GET_PRIV (message1);
663         priv2 = GET_PRIV (message2);
664
665         if (priv1->timestamp == priv2->timestamp &&
666                         !tp_strdiff (priv1->body, priv2->body)) {
667                 return TRUE;
668         }
669
670         return FALSE;
671 }
672
673 TpChannelTextMessageFlags
674 empathy_message_get_flags (EmpathyMessage *self)
675 {
676         EmpathyMessagePriv *priv = GET_PRIV (self);
677
678         g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
679
680         return priv->flags;
681 }
682
683 void
684 empathy_message_set_flags        (EmpathyMessage           *self,
685                                 TpChannelTextMessageFlags flags)
686 {
687         EmpathyMessagePriv *priv;
688
689         g_return_if_fail (EMPATHY_IS_MESSAGE (self));
690
691         priv = GET_PRIV (self);
692
693         priv->flags = flags;
694
695         g_object_notify (G_OBJECT (self), "flags");
696 }