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