]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
796d23ddf1675b2b30b64d05d145be97b8d22a6c
[empathy.git] / libempathy / empathy-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2003-2007 Imendio AB
4  * Copyright (C) 2007 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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Richard Hult <richard@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  *          Xavier Claessens <xclaesse@gmail.com>
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <regex.h>
32
33 #include <glib/gi18n.h>
34
35 #include <libxml/uri.h>
36 #include <libtelepathy/tp-helpers.h>
37
38 #include "empathy-debug.h"
39 #include "empathy-utils.h"
40 #include "empathy-contact-manager.h"
41 #include "empathy-tp-group.h"
42
43 #define DEBUG_DOMAIN "Utils"
44
45 static void regex_init (void);
46
47 gchar *
48 empathy_substring (const gchar *str,
49                   gint         start,
50                   gint         end)
51 {
52         return g_strndup (str + start, end - start);
53 }
54
55 /*
56  * Regular Expression code to match urls.
57  */
58 #define USERCHARS "-A-Za-z0-9"
59 #define PASSCHARS "-A-Za-z0-9,?;.:/!%$^*&~\"#'"
60 #define HOSTCHARS "-A-Za-z0-9"
61 #define PATHCHARS "-A-Za-z0-9_$.+!*(),;:@&=?/~#%"
62 #define SCHEME    "(news:|telnet:|nntp:|file:/|https?:|ftps?:|webcal:)"
63 #define USER      "[" USERCHARS "]+(:["PASSCHARS "]+)?"
64 #define URLPATH   "/[" PATHCHARS "]*[^]'.}>) \t\r\n,\\\"]"
65
66 static regex_t dingus[EMPATHY_REGEX_ALL];
67
68 static void
69 regex_init (void)
70 {
71         static gboolean  inited = FALSE;
72         const gchar     *expression;
73         gint             i;
74
75         if (inited) {
76                 return;
77         }
78
79         for (i = 0; i < EMPATHY_REGEX_ALL; i++) {
80                 switch (i) {
81                 case EMPATHY_REGEX_AS_IS:
82                         expression =
83                                 SCHEME "//(" USER "@)?[" HOSTCHARS ".]+"
84                                 "(:[0-9]+)?(" URLPATH ")?";
85                         break;
86                 case EMPATHY_REGEX_BROWSER:
87                         expression =
88                                 "(www|ftp)[" HOSTCHARS "]*\\.[" HOSTCHARS ".]+"
89                                 "(:[0-9]+)?(" URLPATH ")?";
90                         break;
91                 case EMPATHY_REGEX_EMAIL:
92                         expression =
93                                 "(mailto:)?[a-z0-9][a-z0-9.-]*@[a-z0-9]"
94                                 "[a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+";
95                         break;
96                 case EMPATHY_REGEX_OTHER:
97                         expression =
98                                 "news:[-A-Z\\^_a-z{|}~!\"#$%&'()*+,./0-9;:=?`]+"
99                                 "@[" HOSTCHARS ".]+(:[0-9]+)?";
100                         break;
101                 default:
102                         /* Silence the compiler. */
103                         expression = NULL;
104                         continue;
105                 }
106
107                 memset (&dingus[i], 0, sizeof (regex_t));
108                 regcomp (&dingus[i], expression, REG_EXTENDED | REG_ICASE);
109         }
110
111         inited = TRUE;
112 }
113
114 gint
115 empathy_regex_match (EmpathyRegExType  type,
116                     const gchar     *msg,
117                     GArray          *start,
118                     GArray          *end)
119 {
120         regmatch_t matches[1];
121         gint       ret = 0;
122         gint       num_matches = 0;
123         gint       offset = 0;
124         gint       i;
125
126         g_return_val_if_fail (type >= 0 || type <= EMPATHY_REGEX_ALL, 0);
127
128         regex_init ();
129
130         while (!ret && type != EMPATHY_REGEX_ALL) {
131                 ret = regexec (&dingus[type], msg + offset, 1, matches, 0);
132                 if (ret == 0) {
133                         gint s;
134
135                         num_matches++;
136
137                         s = matches[0].rm_so + offset;
138                         offset = matches[0].rm_eo + offset;
139
140                         g_array_append_val (start, s);
141                         g_array_append_val (end, offset);
142                 }
143         }
144
145         if (type != EMPATHY_REGEX_ALL) {
146                 empathy_debug (DEBUG_DOMAIN,
147                               "Found %d matches for regex type:%d",
148                               num_matches, type);
149                 return num_matches;
150         }
151
152         /* If EMPATHY_REGEX_ALL then we run ALL regex's on the string. */
153         for (i = 0; i < EMPATHY_REGEX_ALL; i++, ret = 0) {
154                 while (!ret) {
155                         ret = regexec (&dingus[i], msg + offset, 1, matches, 0);
156                         if (ret == 0) {
157                                 gint s;
158
159                                 num_matches++;
160
161                                 s = matches[0].rm_so + offset;
162                                 offset = matches[0].rm_eo + offset;
163
164                                 g_array_append_val (start, s);
165                                 g_array_append_val (end, offset);
166                         }
167                 }
168         }
169
170         empathy_debug (DEBUG_DOMAIN,
171                       "Found %d matches for ALL regex types",
172                       num_matches);
173
174         return num_matches;
175 }
176
177 gint
178 empathy_strcasecmp (const gchar *s1,
179                    const gchar *s2)
180 {
181         return empathy_strncasecmp (s1, s2, -1);
182 }
183
184 gint
185 empathy_strncasecmp (const gchar *s1,
186                     const gchar *s2,
187                     gsize        n)
188 {
189         gchar *u1, *u2;
190         gint   ret_val;
191
192         u1 = g_utf8_casefold (s1, n);
193         u2 = g_utf8_casefold (s2, n);
194
195         ret_val = g_utf8_collate (u1, u2);
196         g_free (u1);
197         g_free (u2);
198
199         return ret_val;
200 }
201
202 gboolean
203 empathy_xml_validate (xmlDoc      *doc,
204                      const gchar *dtd_filename)
205 {
206         gchar        *path, *escaped;
207         xmlValidCtxt  cvp;
208         xmlDtd       *dtd;
209         gboolean      ret;
210
211         path = g_build_filename (UNINSTALLED_DTD_DIR, dtd_filename, NULL);
212         if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
213                 g_free (path);
214                 path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
215         }
216         empathy_debug (DEBUG_DOMAIN, "Loading dtd file %s", path);
217
218         /* The list of valid chars is taken from libxml. */
219         escaped = xmlURIEscapeStr (path, ":@&=+$,/?;");
220         g_free (path);
221
222         memset (&cvp, 0, sizeof (cvp));
223         dtd = xmlParseDTD (NULL, escaped);
224         ret = xmlValidateDtd (&cvp, doc, dtd);
225
226         xmlFree (escaped);
227         xmlFreeDtd (dtd);
228
229         return ret;
230 }
231
232 xmlNodePtr
233 empathy_xml_node_get_child (xmlNodePtr   node, 
234                            const gchar *child_name)
235 {
236         xmlNodePtr l;
237
238         g_return_val_if_fail (node != NULL, NULL);
239         g_return_val_if_fail (child_name != NULL, NULL);
240
241         for (l = node->children; l; l = l->next) {
242                 if (l->name && strcmp (l->name, child_name) == 0) {
243                         return l;
244                 }
245         }
246
247         return NULL;
248 }
249
250 xmlChar *
251 empathy_xml_node_get_child_content (xmlNodePtr   node, 
252                                    const gchar *child_name)
253 {
254         xmlNodePtr l;
255
256         g_return_val_if_fail (node != NULL, NULL);
257         g_return_val_if_fail (child_name != NULL, NULL);
258
259         l = empathy_xml_node_get_child (node, child_name);
260         if (l) {
261                 return xmlNodeGetContent (l);
262         }
263                 
264         return NULL;
265 }
266
267 xmlNodePtr
268 empathy_xml_node_find_child_prop_value (xmlNodePtr   node, 
269                                        const gchar *prop_name,
270                                        const gchar *prop_value)
271 {
272         xmlNodePtr l;
273         xmlNodePtr found = NULL;
274
275         g_return_val_if_fail (node != NULL, NULL);
276         g_return_val_if_fail (prop_name != NULL, NULL);
277         g_return_val_if_fail (prop_value != NULL, NULL);
278
279         for (l = node->children; l && !found; l = l->next) {
280                 xmlChar *prop;
281
282                 if (!xmlHasProp (l, prop_name)) {
283                         continue;
284                 }
285
286                 prop = xmlGetProp (l, prop_name);
287                 if (prop && strcmp (prop, prop_value) == 0) {
288                         found = l;
289                 }
290                 
291                 xmlFree (prop);
292         }
293                 
294         return found;
295 }
296
297 guint
298 empathy_account_hash (gconstpointer key)
299 {
300         g_return_val_if_fail (MC_IS_ACCOUNT (key), 0);
301
302         return g_str_hash (mc_account_get_unique_name (MC_ACCOUNT (key)));
303 }
304
305 gboolean
306 empathy_account_equal (gconstpointer a,
307                        gconstpointer b)
308 {
309         const gchar *name_a;
310         const gchar *name_b;
311
312         g_return_val_if_fail (MC_IS_ACCOUNT (a), FALSE);
313         g_return_val_if_fail (MC_IS_ACCOUNT (b), FALSE);
314
315         name_a = mc_account_get_unique_name (MC_ACCOUNT (a));
316         name_b = mc_account_get_unique_name (MC_ACCOUNT (b));
317
318         return g_str_equal (name_a, name_b);
319 }
320
321 MissionControl *
322 empathy_mission_control_new (void)
323 {
324         static MissionControl *mc = NULL;
325
326         if (!mc) {
327                 mc = mission_control_new (tp_get_bus ());
328                 g_object_add_weak_pointer (G_OBJECT (mc), (gpointer) &mc);
329         } else {
330                 g_object_ref (mc);
331         }
332
333         return mc;
334 }
335
336 gchar *
337 empathy_inspect_channel (McAccount *account,
338                          TpChan    *tp_chan)
339 {
340         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
341         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
342
343         return empathy_inspect_handle (account,
344                                        tp_chan->handle,
345                                        tp_chan->handle_type);
346 }
347
348 gchar *
349 empathy_inspect_handle (McAccount *account,
350                         guint      handle,
351                         guint      handle_type)
352 {
353         MissionControl  *mc;
354         TpConn          *tp_conn;
355         GArray          *handles;
356         gchar          **names;
357         gchar           *name;
358         GError          *error = NULL;
359
360         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
361         g_return_val_if_fail (handle != 0, NULL);
362         g_return_val_if_fail (handle_type != 0, NULL);
363
364         mc = empathy_mission_control_new ();
365         tp_conn = mission_control_get_connection (mc, account, NULL);
366         g_object_unref (mc);
367
368         if (!tp_conn) {
369                 return NULL;
370         }
371
372         /* Get the handle's name */
373         handles = g_array_new (FALSE, FALSE, sizeof (guint));
374         g_array_append_val (handles, handle);
375         if (!tp_conn_inspect_handles (DBUS_G_PROXY (tp_conn),
376                                       handle_type,
377                                       handles,
378                                       &names,
379                                       &error)) {
380                 empathy_debug (DEBUG_DOMAIN, 
381                               "Couldn't get id: %s",
382                               error ? error->message : "No error given");
383
384                 g_clear_error (&error);
385                 g_array_free (handles, TRUE);
386                 g_object_unref (tp_conn);
387                 
388                 return NULL;
389         }
390
391         g_array_free (handles, TRUE);
392         name = *names;
393         g_free (names);
394         g_object_unref (tp_conn);
395
396         return name;
397 }
398
399 void
400 empathy_call_with_contact (EmpathyContact  *contact)
401 {
402 #ifdef HAVE_VOIP
403         MissionControl *mc;
404
405         mc = empathy_mission_control_new ();
406         mission_control_request_channel (mc,
407                                          empathy_contact_get_account (contact),
408                                          TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
409                                          empathy_contact_get_handle (contact),
410                                          TP_HANDLE_TYPE_CONTACT,
411                                          NULL, NULL);
412         g_object_unref (mc);
413 #endif
414 }
415
416 void
417 empathy_call_with_contact_id (McAccount *account, const gchar *contact_id)
418 {
419 #ifdef HAVE_VOIP
420         MissionControl *mc;
421
422         mc = empathy_mission_control_new ();
423         mission_control_request_channel_with_string_handle (mc,
424                                                             account,
425                                                             TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
426                                                             contact_id,
427                                                             TP_HANDLE_TYPE_CONTACT,
428                                                             NULL, NULL);
429         g_object_unref (mc);
430 #endif
431 }
432
433 void
434 empathy_chat_with_contact (EmpathyContact  *contact)
435 {
436         MissionControl *mc;
437
438         mc = empathy_mission_control_new ();
439         mission_control_request_channel (mc,
440                                          empathy_contact_get_account (contact),
441                                          TP_IFACE_CHANNEL_TYPE_TEXT,
442                                          empathy_contact_get_handle (contact),
443                                          TP_HANDLE_TYPE_CONTACT,
444                                          NULL, NULL);
445         g_object_unref (mc);
446 }
447
448 void
449 empathy_chat_with_contact_id (McAccount *account, const gchar *contact_id)
450 {
451         MissionControl *mc;
452
453         mc = empathy_mission_control_new ();
454         mission_control_request_channel_with_string_handle (mc,
455                                                             account,
456                                                             TP_IFACE_CHANNEL_TYPE_TEXT,
457                                                             contact_id,
458                                                             TP_HANDLE_TYPE_CONTACT,
459                                                             NULL, NULL);
460         g_object_unref (mc);
461 }
462
463 const gchar *
464 empathy_presence_get_default_message (McPresence presence)
465 {
466         switch (presence) {
467         case MC_PRESENCE_AVAILABLE:
468                 return _("Available");
469         case MC_PRESENCE_DO_NOT_DISTURB:
470                 return _("Busy");
471         case MC_PRESENCE_AWAY:
472         case MC_PRESENCE_EXTENDED_AWAY:
473                 return _("Away");
474         case MC_PRESENCE_HIDDEN:
475                 return _("Hidden");
476         case MC_PRESENCE_OFFLINE:
477         case MC_PRESENCE_UNSET:
478                 return _("Offline");
479         default:
480                 g_assert_not_reached ();
481         }
482
483         return NULL;
484 }
485
486 const gchar *
487 empathy_presence_to_str (McPresence presence)
488 {
489         switch (presence) {
490         case MC_PRESENCE_AVAILABLE:
491                 return "available";
492         case MC_PRESENCE_DO_NOT_DISTURB:
493                 return "busy";
494         case MC_PRESENCE_AWAY:
495                 return "away";
496         case MC_PRESENCE_EXTENDED_AWAY:
497                 return "ext_away";
498         case MC_PRESENCE_HIDDEN:
499                 return "hidden";
500         case MC_PRESENCE_OFFLINE:
501                 return "offline";
502         case MC_PRESENCE_UNSET:
503                 return "unset";
504         default:
505                 g_assert_not_reached ();
506         }
507
508         return NULL;
509 }
510
511 McPresence
512 empathy_presence_from_str (const gchar *str)
513 {
514         if (strcmp (str, "available") == 0) {
515                 return MC_PRESENCE_AVAILABLE;
516         } else if ((strcmp (str, "dnd") == 0) || (strcmp (str, "busy") == 0)) {
517                 return MC_PRESENCE_DO_NOT_DISTURB;
518         } else if ((strcmp (str, "away") == 0) || (strcmp (str, "brb") == 0)) {
519                 return MC_PRESENCE_AWAY;
520         } else if ((strcmp (str, "xa") == 0) || (strcmp (str, "ext_away") == 0)) {
521                 return MC_PRESENCE_EXTENDED_AWAY;
522         } else if (strcmp (str, "hidden") == 0) {
523                 return MC_PRESENCE_HIDDEN;
524         } else if (strcmp (str, "offline") == 0) {
525                 return MC_PRESENCE_OFFLINE;
526         } else if (strcmp (str, "unset") == 0) {
527                 return MC_PRESENCE_UNSET;
528         }
529
530         return MC_PRESENCE_AVAILABLE;
531 }
532