]> git.0d.be Git - empathy.git/blob - libempathy/empathy-message.c
Updated Basque language
[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
31 #include "empathy-message.h"
32 #include "empathy-utils.h"
33 #include "empathy-enum-types.h"
34
35 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyMessage)
36 typedef struct {
37         TpChannelTextMessageType  type;
38         EmpathyContact           *sender;
39         EmpathyContact           *receiver;
40         gchar                    *body;
41         time_t                    timestamp;
42         gboolean                  is_backlog;
43         guint                     id;
44         gboolean                  incoming;
45 } EmpathyMessagePriv;
46
47 static void empathy_message_finalize   (GObject            *object);
48 static void message_get_property      (GObject            *object,
49                                        guint               param_id,
50                                        GValue             *value,
51                                        GParamSpec         *pspec);
52 static void message_set_property      (GObject            *object,
53                                        guint               param_id,
54                                        const GValue       *value,
55                                        GParamSpec         *pspec);
56
57 G_DEFINE_TYPE (EmpathyMessage, empathy_message, G_TYPE_OBJECT);
58
59 enum {
60         PROP_0,
61         PROP_TYPE,
62         PROP_SENDER,
63         PROP_RECEIVER,
64         PROP_BODY,
65         PROP_TIMESTAMP,
66         PROP_IS_BACKLOG,
67         PROP_INCOMING,
68 };
69
70 static void
71 empathy_message_class_init (EmpathyMessageClass *class)
72 {
73         GObjectClass *object_class;
74
75         object_class = G_OBJECT_CLASS (class);
76         object_class->finalize     = empathy_message_finalize;
77         object_class->get_property = message_get_property;
78         object_class->set_property = message_set_property;
79
80         g_object_class_install_property (object_class,
81                                          PROP_TYPE,
82                                          g_param_spec_uint ("type",
83                                                             "Message Type",
84                                                             "The type of message",
85                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
86                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY,
87                                                             TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
88                                                             G_PARAM_READWRITE));
89         g_object_class_install_property (object_class,
90                                          PROP_SENDER,
91                                          g_param_spec_object ("sender",
92                                                               "Message Sender",
93                                                               "The sender of the message",
94                                                               EMPATHY_TYPE_CONTACT,
95                                                               G_PARAM_READWRITE));
96         g_object_class_install_property (object_class,
97                                          PROP_RECEIVER,
98                                          g_param_spec_object ("receiver",
99                                                               "Message Receiver",
100                                                               "The receiver of the message",
101                                                               EMPATHY_TYPE_CONTACT,
102                                                               G_PARAM_READWRITE));
103         g_object_class_install_property (object_class,
104                                          PROP_BODY,
105                                          g_param_spec_string ("body",
106                                                               "Message Body",
107                                                               "The content of the message",
108                                                               NULL,
109                                                               G_PARAM_READWRITE));
110         g_object_class_install_property (object_class,
111                                          PROP_TIMESTAMP,
112                                          g_param_spec_long ("timestamp",
113                                                             "timestamp",
114                                                             "timestamp",
115                                                             -1,
116                                                             G_MAXLONG,
117                                                             -1,
118                                                             G_PARAM_READWRITE));
119         g_object_class_install_property (object_class,
120                                          PROP_IS_BACKLOG,
121                                          g_param_spec_boolean ("is-backlog",
122                                                                "History message",
123                                                                "If the message belongs to history",
124                                                                FALSE,
125                                                                G_PARAM_READWRITE));
126
127
128         g_object_class_install_property (object_class,
129                                          PROP_INCOMING,
130                                          g_param_spec_boolean ("incoming",
131                                                                "Incoming",
132                                                                "If this is an incoming (as opposed to sent) message",
133                                                                FALSE,
134                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135
136         g_type_class_add_private (object_class, sizeof (EmpathyMessagePriv));
137
138 }
139
140 static void
141 empathy_message_init (EmpathyMessage *message)
142 {
143         EmpathyMessagePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (message,
144                 EMPATHY_TYPE_MESSAGE, EmpathyMessagePriv);
145
146         message->priv = priv;
147         priv->timestamp = empathy_time_get_current ();
148 }
149
150 static void
151 empathy_message_finalize (GObject *object)
152 {
153         EmpathyMessagePriv *priv;
154
155         priv = GET_PRIV (object);
156
157         if (priv->sender) {
158                 g_object_unref (priv->sender);
159         }
160         if (priv->receiver) {
161                 g_object_unref (priv->receiver);
162         }
163
164         g_free (priv->body);
165
166         G_OBJECT_CLASS (empathy_message_parent_class)->finalize (object);
167 }
168
169 static void
170 message_get_property (GObject    *object,
171                       guint       param_id,
172                       GValue     *value,
173                       GParamSpec *pspec)
174 {
175         EmpathyMessagePriv *priv;
176
177         priv = GET_PRIV (object);
178
179         switch (param_id) {
180         case PROP_TYPE:
181                 g_value_set_uint (value, priv->type);
182                 break;
183         case PROP_SENDER:
184                 g_value_set_object (value, priv->sender);
185                 break;
186         case PROP_RECEIVER:
187                 g_value_set_object (value, priv->receiver);
188                 break;
189         case PROP_BODY:
190                 g_value_set_string (value, priv->body);
191                 break;
192         case PROP_INCOMING:
193                 g_value_set_boolean (value, priv->incoming);
194                 break;
195         default:
196                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
197                 break;
198         };
199 }
200
201 static void
202 message_set_property (GObject      *object,
203                       guint         param_id,
204                       const GValue *value,
205                       GParamSpec   *pspec)
206 {
207         EmpathyMessagePriv *priv;
208
209         priv = GET_PRIV (object);
210
211         switch (param_id) {
212         case PROP_TYPE:
213                 empathy_message_set_tptype (EMPATHY_MESSAGE (object),
214                                             g_value_get_uint (value));
215                 break;
216         case PROP_SENDER:
217                 empathy_message_set_sender (EMPATHY_MESSAGE (object),
218                                            EMPATHY_CONTACT (g_value_get_object (value)));
219                 break;
220         case PROP_RECEIVER:
221                 empathy_message_set_receiver (EMPATHY_MESSAGE (object),
222                                              EMPATHY_CONTACT (g_value_get_object (value)));
223                 break;
224         case PROP_BODY:
225                 empathy_message_set_body (EMPATHY_MESSAGE (object),
226                                          g_value_get_string (value));
227                 break;
228         case PROP_INCOMING:
229                 priv->incoming = g_value_get_boolean (value);
230                 break;
231         default:
232                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
233                 break;
234         };
235 }
236
237 static gboolean
238 has_prefix_case (const gchar *s,
239                  const gchar *prefix)
240 {
241         return g_ascii_strncasecmp (s, prefix, strlen (prefix)) == 0;
242 }
243
244 /*
245  * Constructs an EmpathyMessage based on user input, which may include "/me"
246  * and friends.
247  *
248  * Returns: an #EmpathyMessage if @message could be parsed, or %NULL if
249  *          @message was an unknown command.
250  */
251 EmpathyMessage *
252 empathy_message_new_from_entry (const gchar *message)
253 {
254         TpChannelTextMessageType t = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
255
256         g_return_val_if_fail (message != NULL, NULL);
257
258         if (message[0] == '/') {
259                 if (g_ascii_strcasecmp (message, "/me") == 0) {
260                         message = "";
261                         t = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
262                 } else if (has_prefix_case (message, "/me ")) {
263                         message += strlen ("/me ");
264                         t = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
265                 } else if (has_prefix_case (message, "/say ")) {
266                         message += strlen ("/say ");
267                 } else {
268                         /* Also allow messages with two slashes before the
269                          * first space, so it is possible to send a /unix/path.
270                          * This heuristic is kind of crap.
271                          */
272                         gboolean second_slash = FALSE;
273                         const gchar *m = message + 1;
274
275                         while (!second_slash && *m != '\0' && *m != ' ') {
276                                 if (*m == '/')
277                                         second_slash = TRUE;
278
279                                 m++;
280                         }
281
282                         if (!second_slash)
283                                 return NULL;
284                 }
285         }
286
287         return g_object_new (EMPATHY_TYPE_MESSAGE,
288                              "type", t,
289                              "body", message,
290                              NULL);
291 }
292
293 EmpathyMessage *
294 empathy_message_new (const gchar *body)
295 {
296         return g_object_new (EMPATHY_TYPE_MESSAGE,
297                              "body", body,
298                              NULL);
299 }
300
301 TpChannelTextMessageType
302 empathy_message_get_tptype (EmpathyMessage *message)
303 {
304         EmpathyMessagePriv *priv;
305
306         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message),
307                               TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL);
308
309         priv = GET_PRIV (message);
310
311         return priv->type;
312 }
313
314 void
315 empathy_message_set_tptype (EmpathyMessage           *message,
316                             TpChannelTextMessageType  type)
317 {
318         EmpathyMessagePriv *priv;
319
320         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
321
322         priv = GET_PRIV (message);
323
324         priv->type = type;
325
326         g_object_notify (G_OBJECT (message), "type");
327 }
328
329 EmpathyContact *
330 empathy_message_get_sender (EmpathyMessage *message)
331 {
332         EmpathyMessagePriv *priv;
333
334         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
335
336         priv = GET_PRIV (message);
337
338         return priv->sender;
339 }
340
341 void
342 empathy_message_set_sender (EmpathyMessage *message, EmpathyContact *contact)
343 {
344         EmpathyMessagePriv *priv;
345         EmpathyContact     *old_sender;
346
347         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
348         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
349
350         priv = GET_PRIV (message);
351
352         old_sender = priv->sender;
353         priv->sender = g_object_ref (contact);
354
355         if (old_sender) {
356                 g_object_unref (old_sender);
357         }
358
359         g_object_notify (G_OBJECT (message), "sender");
360 }
361
362 EmpathyContact *
363 empathy_message_get_receiver (EmpathyMessage *message)
364 {
365         EmpathyMessagePriv *priv;
366
367         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
368
369         priv = GET_PRIV (message);
370
371         return priv->receiver;
372 }
373
374 void
375 empathy_message_set_receiver (EmpathyMessage *message, EmpathyContact *contact)
376 {
377         EmpathyMessagePriv *priv;
378         EmpathyContact     *old_receiver;
379
380         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
381         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
382
383         priv = GET_PRIV (message);
384
385         old_receiver = priv->receiver;
386         priv->receiver = g_object_ref (contact);
387
388         if (old_receiver) {
389                 g_object_unref (old_receiver);
390         }
391
392         g_object_notify (G_OBJECT (message), "receiver");
393 }
394
395 const gchar *
396 empathy_message_get_body (EmpathyMessage *message)
397 {
398         EmpathyMessagePriv *priv;
399
400         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), NULL);
401
402         priv = GET_PRIV (message);
403
404         return priv->body;
405 }
406
407 void
408 empathy_message_set_body (EmpathyMessage *message,
409                           const gchar    *body)
410 {
411         EmpathyMessagePriv       *priv = GET_PRIV (message);
412
413         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
414
415         g_free (priv->body);
416
417         if (body) {
418                 priv->body = g_strdup (body);
419         } else {
420                 priv->body = NULL;
421         }
422
423         g_object_notify (G_OBJECT (message), "body");
424 }
425
426 time_t
427 empathy_message_get_timestamp (EmpathyMessage *message)
428 {
429         EmpathyMessagePriv *priv;
430
431         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), -1);
432
433         priv = GET_PRIV (message);
434
435         return priv->timestamp;
436 }
437
438 void
439 empathy_message_set_timestamp (EmpathyMessage *message,
440                                time_t          timestamp)
441 {
442         EmpathyMessagePriv *priv;
443
444         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
445         g_return_if_fail (timestamp >= -1);
446
447         priv = GET_PRIV (message);
448
449         if (timestamp <= 0) {
450                 priv->timestamp = empathy_time_get_current ();
451         } else {
452                 priv->timestamp = timestamp;
453         }
454
455         g_object_notify (G_OBJECT (message), "timestamp");
456 }
457
458 gboolean
459 empathy_message_is_backlog (EmpathyMessage *message)
460 {
461         EmpathyMessagePriv *priv;
462
463         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
464
465         priv = GET_PRIV (message);
466
467         return priv->is_backlog;
468 }
469
470 void
471 empathy_message_set_is_backlog (EmpathyMessage *message,
472                                 gboolean is_backlog)
473 {
474         EmpathyMessagePriv *priv;
475
476         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
477
478         priv = GET_PRIV (message);
479
480         priv->is_backlog = is_backlog;
481
482         g_object_notify (G_OBJECT (message), "is-backlog");
483 }
484
485 #define IS_SEPARATOR(ch) (ch == ' ' || ch == ',' || ch == '.' || ch == ':')
486 gboolean
487 empathy_message_should_highlight (EmpathyMessage *message)
488 {
489         EmpathyContact *contact;
490         const gchar   *msg, *to;
491         gchar         *cf_msg, *cf_to;
492         gchar         *ch;
493         gboolean       ret_val;
494
495         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
496
497         ret_val = FALSE;
498
499         msg = empathy_message_get_body (message);
500         if (!msg) {
501                 return FALSE;
502         }
503
504         contact = empathy_message_get_receiver (message);
505         if (!contact || !empathy_contact_is_user (contact)) {
506                 return FALSE;
507         }
508
509         to = empathy_contact_get_name (contact);
510         if (!to) {
511                 return FALSE;
512         }
513
514         cf_msg = g_utf8_casefold (msg, -1);
515         cf_to = g_utf8_casefold (to, -1);
516
517         ch = strstr (cf_msg, cf_to);
518         if (ch == NULL) {
519                 goto finished;
520         }
521         if (ch != cf_msg) {
522                 /* Not first in the message */
523                 if (!IS_SEPARATOR (*(ch - 1))) {
524                         goto finished;
525                 }
526         }
527
528         ch = ch + strlen (cf_to);
529         if (ch >= cf_msg + strlen (cf_msg)) {
530                 ret_val = TRUE;
531                 goto finished;
532         }
533
534         if (IS_SEPARATOR (*ch)) {
535                 ret_val = TRUE;
536                 goto finished;
537         }
538
539 finished:
540         g_free (cf_msg);
541         g_free (cf_to);
542
543         return ret_val;
544 }
545
546 TpChannelTextMessageType
547 empathy_message_type_from_str (const gchar *type_str)
548 {
549         if (strcmp (type_str, "normal") == 0) {
550                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
551         }
552         if (strcmp (type_str, "action") == 0) {
553                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION;
554         }
555         else if (strcmp (type_str, "notice") == 0) {
556                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE;
557         }
558         else if (strcmp (type_str, "auto-reply") == 0) {
559                 return TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY;
560         }
561
562         return TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
563 }
564
565 const gchar *
566 empathy_message_type_to_str (TpChannelTextMessageType type)
567 {
568         switch (type) {
569         case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION:
570                 return "action";
571         case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE:
572                 return "notice";
573         case TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY:
574                 return "auto-reply";
575         default:
576                 return "normal";
577         }
578 }
579
580 guint
581 empathy_message_get_id (EmpathyMessage *message)
582 {
583         EmpathyMessagePriv *priv = GET_PRIV (message);
584
585         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), 0);
586
587         return priv->id;
588 }
589
590 void
591 empathy_message_set_id (EmpathyMessage *message, guint id)
592 {
593         EmpathyMessagePriv *priv = GET_PRIV (message);
594
595         priv->id = id;
596 }
597
598 void
599 empathy_message_set_incoming (EmpathyMessage *message, gboolean incoming)
600 {
601         EmpathyMessagePriv *priv;
602
603         g_return_if_fail (EMPATHY_IS_MESSAGE (message));
604
605         priv = GET_PRIV (message);
606
607         priv->incoming = incoming;
608
609         g_object_notify (G_OBJECT (message), "incoming");
610 }
611
612 gboolean
613 empathy_message_is_incoming (EmpathyMessage *message)
614 {
615         EmpathyMessagePriv *priv = GET_PRIV (message);
616
617         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message), FALSE);
618
619         return priv->incoming;
620 }
621
622 gboolean
623 empathy_message_equal (EmpathyMessage *message1, EmpathyMessage *message2)
624 {
625         EmpathyMessagePriv *priv1;
626         EmpathyMessagePriv *priv2;
627
628         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message1), FALSE);
629         g_return_val_if_fail (EMPATHY_IS_MESSAGE (message2), FALSE);
630
631         priv1 = GET_PRIV (message1);
632         priv2 = GET_PRIV (message2);
633
634         if (priv1->id == priv2->id && !tp_strdiff (priv1->body, priv2->body)) {
635                 return TRUE;
636         }
637
638         return FALSE;
639 }