]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
Merge branch 'notifications-635500'
[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         EmpathyContact *contact;
271
272         g_return_val_if_fail (TPL_IS_ENTRY (logentry), NULL);
273
274         acc_man = tp_account_manager_dup ();
275         /* FIXME Currently Empathy shows in the log viewer only valid accounts, so it
276          * won't be selected any non-existing (ie removed) account.
277          * When #610455 will be fixed, calling tp_account_manager_ensure_account ()
278          * might add a not existing account to the AM. tp_account_new () probably
279          * will be the best way to handle it.
280          * Note: When creating an EmpathyContact from a TplEntity instance, the
281          * TpAccount is passed *only* to let EmpathyContact be able to retrieve the
282          * avatar (contact_get_avatar_filename () need a TpAccount).
283          * If the way EmpathyContact stores the avatar is changes, it might not be
284          * needed anymore any TpAccount passing and the following call will be
285          * useless */
286         account = tp_account_manager_ensure_account (acc_man,
287                         tpl_entry_get_account_path (logentry));
288         g_object_unref (acc_man);
289
290         /* TODO Currently only TplLogEntryText exists as subclass of TplEntry, in
291          * future more TplEntry will exist and EmpathyMessage should probably
292          * be enhanced to support other types of log entries (ie TplLogEntryCall).
293          *
294          * For now we just check (simply) that we are dealing with the only supported type,
295          * then there will be a if/then/else or switch handling all the supported
296          * cases.
297          */
298         if (!TPL_IS_ENTRY_TEXT (logentry))
299                 return NULL;
300
301         body = g_strdup (tpl_entry_text_get_message (
302                                 TPL_ENTRY_TEXT (logentry)));
303         receiver = tpl_entry_get_receiver (logentry);
304         sender = tpl_entry_get_sender (logentry);
305
306         retval = empathy_message_new (body);
307         if (receiver != NULL) {
308                 contact = empathy_contact_from_tpl_contact (account, receiver);
309                 empathy_message_set_receiver (retval, contact);
310                 g_object_unref (contact);
311         }
312
313         if (sender != NULL) {
314                 contact = empathy_contact_from_tpl_contact (account, sender);
315                 empathy_message_set_sender (retval, contact);
316                 g_object_unref (contact);
317         }
318
319         empathy_message_set_timestamp (retval,
320                         tpl_entry_get_timestamp (logentry));
321         empathy_message_set_id (retval,
322                         tpl_entry_text_get_pending_msg_id (TPL_ENTRY_TEXT (logentry)));
323         empathy_message_set_is_backlog (retval, TRUE);
324
325         g_free (body);
326
327         return retval;
328 }
329
330 TpChannelTextMessageType
331 empathy_message_get_tptype (EmpathyMessage *message)
332 {
333         EmpathyMessagePriv *priv;
334
335         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
336                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
337
338         priv = GET_PRIV (message);
339
340         return priv->type;
341 }
342
343 void
344 empathy_message_set_tptype (EmpathyMessage           *message,
345                             TpChannelTextMessageType  type)
346 {
347         EmpathyMessagePriv *priv;
348
349         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
350
351         priv = GET_PRIV (message);
352
353         priv->type = type;
354
355         g_object_notify (G_OBJECT (message), "type");
356 }
357
358 EmpathyContact *
359 empathy_message_get_sender (EmpathyMessage *message)
360 {
361         EmpathyMessagePriv *priv;
362
363         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
364
365         priv = GET_PRIV (message);
366
367         return priv->sender;
368 }
369
370 void
371 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
372 {
373         EmpathyMessagePriv *priv;
374         EmpathyContact     *old_sender;
375
376         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
377         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
378
379         priv = GET_PRIV (message);
380
381         old_sender = priv->sender;
382         priv->sender = g_object_ref (contact);
383
384         if (old_sender) {
385                 g_object_unref (old_sender);
386         }
387
388         g_object_notify (G_OBJECT (message), "sender");
389 }
390
391 EmpathyContact *
392 empathy_message_get_receiver (EmpathyMessage *message)
393 {
394         EmpathyMessagePriv *priv;
395
396         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
397
398         priv = GET_PRIV (message);
399
400         return priv->receiver;
401 }
402
403 void
404 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
405 {
406         EmpathyMessagePriv *priv;
407         EmpathyContact     *old_receiver;
408
409         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
410         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
411
412         priv = GET_PRIV (message);
413
414         old_receiver = priv->receiver;
415         priv->receiver = g_object_ref (contact);
416
417         if (old_receiver) {
418                 g_object_unref (old_receiver);
419         }
420
421         g_object_notify (G_OBJECT (message), "receiver");
422 }
423
424 const gchar *
425 empathy_message_get_body (EmpathyMessage *message)
426 {
427         EmpathyMessagePriv *priv;
428
429         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
430
431         priv = GET_PRIV (message);
432
433         return priv->body;
434 }
435
436 void
437 empathy_message_set_body (EmpathyMessage *message,
438                           const gchar    *body)
439 {
440         EmpathyMessagePriv       *priv = GET_PRIV (message);
441
442         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
443
444         g_free (priv->body);
445
446         if (body) {
447                 priv->body = g_strdup (body);
448         } else {
449                 priv->body = NULL;
450         }
451
452         g_object_notify (G_OBJECT (message), "body");
453 }
454
455 time_t
456 empathy_message_get_timestamp (EmpathyMessage *message)
457 {
458         EmpathyMessagePriv *priv;
459
460         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
461
462         priv = GET_PRIV (message);
463
464         return priv->timestamp;
465 }
466
467 void
468 empathy_message_set_timestamp (EmpathyMessage *message,
469                                time_t          timestamp)
470 {
471         EmpathyMessagePriv *priv;
472
473         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
474         g_return_if_fail (timestamp >= -1);
475
476         priv = GET_PRIV (message);
477
478         if (timestamp <= 0) {
479                 priv->timestamp = empathy_time_get_current ();
480         } else {
481                 priv->timestamp = timestamp;
482         }
483
484         g_object_notify (G_OBJECT (message), "timestamp");
485 }
486
487 gboolean
488 empathy_message_is_backlog (EmpathyMessage *message)
489 {
490         EmpathyMessagePriv *priv;
491
492         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
493
494         priv = GET_PRIV (message);
495
496         return priv->is_backlog;
497 }
498
499 void
500 empathy_message_set_is_backlog (EmpathyMessage *message,
501                                 gboolean is_backlog)
502 {
503         EmpathyMessagePriv *priv;
504
505         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
506
507         priv = GET_PRIV (message);
508
509         priv->is_backlog = is_backlog;
510
511         g_object_notify (G_OBJECT (message), "is-backlog");
512 }
513
514 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
515 gboolean
516 empathy_message_should_highlight (EmpathyMessage *message)
517 {
518         EmpathyContact *contact;
519         const gchar   *msg, *to;
520         gchar         *cf_msg, *cf_to;
521         gchar         *ch;
522         gboolean       ret_val;
523         TpChannelTextMessageFlags flags;
524
525         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
526
527         ret_val = FALSE;
528
529         msg = empathy_message_get_body (message);
530         if (!msg) {
531                 return FALSE;
532         }
533
534         contact = empathy_message_get_receiver (message);
535         if (!contact || !empathy_contact_is_user (contact)) {
536                 return FALSE;
537         }
538
539         to = empathy_contact_get_alias (contact);
540         if (!to) {
541                 return FALSE;
542         }
543
544         flags = empathy_message_get_flags (message);
545         if (flags & TP_CHANNEL_TEXT_MESSAGE_FLAG_SCROLLBACK) {
546                 /* FIXME: Ideally we shouldn't highlight scrollback messages only if they
547                  * have already been received by the user before (and so are in the logs) */
548                 return FALSE;
549         }
550
551         cf_msg = g_utf8_casefold (msg, -1);
552         cf_to = g_utf8_casefold (to, -1);
553
554         ch = strstr (cf_msg, cf_to);
555         if (ch == NULL) {
556                 goto finished;
557         }
558         if (ch != cf_msg) {
559                 /* Not first in the message */
560                 if (!IS_SEPARATOR (*(ch - 1))) {
561                         goto finished;
562                 }
563         }
564
565         ch = ch + strlen (cf_to);
566         if (ch >= cf_msg + strlen (cf_msg)) {
567                 ret_val = TRUE;
568                 goto finished;
569         }
570
571         if (IS_SEPARATOR (*ch)) {
572                 ret_val = TRUE;
573                 goto finished;
574         }
575
576 finished:
577         g_free (cf_msg);
578         g_free (cf_to);
579
580         return ret_val;
581 }
582
583 TpChannelTextMessageType
584 empathy_message_type_from_str (const gchar *type_str)
585 {
586         if (strcmp (type_str, "normal") == 0) {
587                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
588         }
589         if (strcmp (type_str, "action") == 0) {
590                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
591         }
592         else if (strcmp (type_str, "notice") == 0) {
593                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
594         }
595         else if (strcmp (type_str, "auto-reply") == 0) {
596                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
597         }
598
599         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
600 }
601
602 const gchar *
603 empathy_message_type_to_str (TpChannelTextMessageType type)
604 {
605         switch (type) {
606         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
607                 return "action";
608         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
609                 return "notice";
610         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
611                 return "auto-reply";
612         case TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT:
613                 return "delivery-report";
614         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
615         default:
616                 return "normal";
617         }
618 }
619
620 guint
621 empathy_message_get_id (EmpathyMessage *message)
622 {
623         EmpathyMessagePriv *priv = GET_PRIV (message);
624
625         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
626
627         return priv->id;
628 }
629
630 void
631 empathy_message_set_id (EmpathyMessage *message, guint id)
632 {
633         EmpathyMessagePriv *priv = GET_PRIV (message);
634
635         priv->id = id;
636 }
637
638 void
639 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
640 {
641         EmpathyMessagePriv *priv;
642
643         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
644
645         priv = GET_PRIV (message);
646
647         priv->incoming = incoming;
648
649         g_object_notify (G_OBJECT (message), "incoming");
650 }
651
652 gboolean
653 empathy_message_is_incoming (EmpathyMessage *message)
654 {
655         EmpathyMessagePriv *priv = GET_PRIV (message);
656
657         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
658
659         return priv->incoming;
660 }
661
662 gboolean
663 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
664 {
665         EmpathyMessagePriv *priv1;
666         EmpathyMessagePriv *priv2;
667
668         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
669         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
670
671         priv1 = GET_PRIV (message1);
672         priv2 = GET_PRIV (message2);
673
674         if (priv1->timestamp == priv2->timestamp &&
675                         !tp_strdiff (priv1->body, priv2->body)) {
676                 return TRUE;
677         }
678
679         return FALSE;
680 }
681
682 TpChannelTextMessageFlags
683 empathy_message_get_flags (EmpathyMessage *self)
684 {
685         EmpathyMessagePriv *priv = GET_PRIV (self);
686
687         g_return_val_if_fail (EMPATHY_IS_MESSAGE (self), 0);
688
689         return priv->flags;
690 }
691
692 void
693 empathy_message_set_flags        (EmpathyMessage           *self,
694                                 TpChannelTextMessageFlags flags)
695 {
696         EmpathyMessagePriv *priv;
697
698         g_return_if_fail (EMPATHY_IS_MESSAGE (self));
699
700         priv = GET_PRIV (self);
701
702         priv->flags = flags;
703
704         g_object_notify (G_OBJECT (self), "flags");
705 }