]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Updated.
[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 /* Stolen from telepathy-glib */
203 gboolean
204 empathy_strdiff (const gchar *left, const gchar *right)
205 {
206   if ((NULL == left) != (NULL == right))
207     return TRUE;
208
209   else if (left == right)
210     return FALSE;
211
212   else
213     return (0 != strcmp (left, right));
214 }
215
216 /* Stolen from telepathy-glib */
217 static inline gboolean
218 _esc_ident_bad (gchar c, gboolean is_first)
219 {
220   return ((c < 'a' || c > 'z') &&
221           (c < 'A' || c > 'Z') &&
222           (c < '0' || c > '9' || is_first));
223 }
224
225 /* Stolen from telepathy-glib */
226 gchar *
227 empathy_escape_as_identifier (const gchar *name)
228 {
229   gboolean bad = FALSE;
230   size_t len = 0;
231   GString *op;
232   const gchar *ptr, *first_ok;
233
234   g_return_val_if_fail (name != NULL, NULL);
235
236   for (ptr = name; *ptr; ptr++)
237     {
238       if (_esc_ident_bad (*ptr, ptr == name))
239         {
240           bad = TRUE;
241           len += 3;
242         }
243       else
244         len++;
245     }
246
247   /* fast path if it's clean */
248   if (!bad)
249     return g_strdup (name);
250
251   /* If strictly less than ptr, first_ok is the first uncopied safe character.
252    */
253   first_ok = name;
254   op = g_string_sized_new (len);
255   for (ptr = name; *ptr; ptr++)
256     {
257       if (_esc_ident_bad (*ptr, ptr == name))
258         {
259           /* copy preceding safe characters if any */
260           if (first_ok < ptr)
261             {
262               g_string_append_len (op, first_ok, ptr - first_ok);
263             }
264           /* escape the unsafe character */
265           g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
266           /* restart after it */
267           first_ok = ptr + 1;
268         }
269     }
270   /* copy trailing safe characters if any */
271   if (first_ok < ptr)
272     {
273       g_string_append_len (op, first_ok, ptr - first_ok);
274     }
275   return g_string_free (op, FALSE);
276 }
277
278 gboolean
279 empathy_xml_validate (xmlDoc      *doc,
280                      const gchar *dtd_filename)
281 {
282         gchar        *path, *escaped;
283         xmlValidCtxt  cvp;
284         xmlDtd       *dtd;
285         gboolean      ret;
286
287         path = g_build_filename (DATADIR, "empathy", dtd_filename, NULL);
288
289         /* The list of valid chars is taken from libxml. */
290         escaped = xmlURIEscapeStr (path, ":@&=+$,/?;");
291
292         g_free (path);
293
294         memset (&cvp, 0, sizeof (cvp));
295         dtd = xmlParseDTD (NULL, escaped);
296         ret = xmlValidateDtd (&cvp, doc, dtd);
297
298         xmlFree (escaped);
299         xmlFreeDtd (dtd);
300
301         return ret;
302 }
303
304 xmlNodePtr
305 empathy_xml_node_get_child (xmlNodePtr   node, 
306                            const gchar *child_name)
307 {
308         xmlNodePtr l;
309
310         g_return_val_if_fail (node != NULL, NULL);
311         g_return_val_if_fail (child_name != NULL, NULL);
312
313         for (l = node->children; l; l = l->next) {
314                 if (l->name && strcmp (l->name, child_name) == 0) {
315                         return l;
316                 }
317         }
318
319         return NULL;
320 }
321
322 xmlChar *
323 empathy_xml_node_get_child_content (xmlNodePtr   node, 
324                                    const gchar *child_name)
325 {
326         xmlNodePtr l;
327
328         g_return_val_if_fail (node != NULL, NULL);
329         g_return_val_if_fail (child_name != NULL, NULL);
330
331         l = empathy_xml_node_get_child (node, child_name);
332         if (l) {
333                 return xmlNodeGetContent (l);
334         }
335                 
336         return NULL;
337 }
338
339 xmlNodePtr
340 empathy_xml_node_find_child_prop_value (xmlNodePtr   node, 
341                                        const gchar *prop_name,
342                                        const gchar *prop_value)
343 {
344         xmlNodePtr l;
345         xmlNodePtr found = NULL;
346
347         g_return_val_if_fail (node != NULL, NULL);
348         g_return_val_if_fail (prop_name != NULL, NULL);
349         g_return_val_if_fail (prop_value != NULL, NULL);
350
351         for (l = node->children; l && !found; l = l->next) {
352                 xmlChar *prop;
353
354                 if (!xmlHasProp (l, prop_name)) {
355                         continue;
356                 }
357
358                 prop = xmlGetProp (l, prop_name);
359                 if (prop && strcmp (prop, prop_value) == 0) {
360                         found = l;
361                 }
362                 
363                 xmlFree (prop);
364         }
365                 
366         return found;
367 }
368
369 guint
370 empathy_account_hash (gconstpointer key)
371 {
372         g_return_val_if_fail (MC_IS_ACCOUNT (key), 0);
373
374         return g_str_hash (mc_account_get_unique_name (MC_ACCOUNT (key)));
375 }
376
377 gboolean
378 empathy_account_equal (gconstpointer a,
379                        gconstpointer b)
380 {
381         const gchar *name_a;
382         const gchar *name_b;
383
384         g_return_val_if_fail (MC_IS_ACCOUNT (a), FALSE);
385         g_return_val_if_fail (MC_IS_ACCOUNT (b), FALSE);
386
387         name_a = mc_account_get_unique_name (MC_ACCOUNT (a));
388         name_b = mc_account_get_unique_name (MC_ACCOUNT (b));
389
390         return g_str_equal (name_a, name_b);
391 }
392
393 MissionControl *
394 empathy_mission_control_new (void)
395 {
396         static MissionControl *mc = NULL;
397
398         if (!mc) {
399                 mc = mission_control_new (tp_get_bus ());
400                 g_object_add_weak_pointer (G_OBJECT (mc), (gpointer) &mc);
401         } else {
402                 g_object_ref (mc);
403         }
404
405         return mc;
406 }
407
408 gchar *
409 empathy_inspect_channel (McAccount *account,
410                          TpChan    *tp_chan)
411 {
412         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
413         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
414
415         return empathy_inspect_handle (account,
416                                        tp_chan->handle,
417                                        tp_chan->handle_type);
418 }
419
420 gchar *
421 empathy_inspect_handle (McAccount *account,
422                         guint      handle,
423                         guint      handle_type)
424 {
425         MissionControl  *mc;
426         TpConn          *tp_conn;
427         GArray          *handles;
428         gchar          **names;
429         gchar           *name;
430         GError          *error = NULL;
431
432         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
433         g_return_val_if_fail (handle != 0, NULL);
434         g_return_val_if_fail (handle_type != 0, NULL);
435
436         mc = empathy_mission_control_new ();
437         tp_conn = mission_control_get_connection (mc, account, NULL);
438         g_object_unref (mc);
439
440         if (!tp_conn) {
441                 return NULL;
442         }
443
444         /* Get the handle's name */
445         handles = g_array_new (FALSE, FALSE, sizeof (guint));
446         g_array_append_val (handles, handle);
447         if (!tp_conn_inspect_handles (DBUS_G_PROXY (tp_conn),
448                                       handle_type,
449                                       handles,
450                                       &names,
451                                       &error)) {
452                 empathy_debug (DEBUG_DOMAIN, 
453                               "Couldn't get id: %s",
454                               error ? error->message : "No error given");
455
456                 g_clear_error (&error);
457                 g_array_free (handles, TRUE);
458                 g_object_unref (tp_conn);
459                 
460                 return NULL;
461         }
462
463         g_array_free (handles, TRUE);
464         name = *names;
465         g_free (names);
466         g_object_unref (tp_conn);
467
468         return name;
469 }
470
471 void
472 empathy_call_contact (EmpathyContact *contact)
473 {
474 #ifdef HAVE_VOIP
475         MissionControl *mc;
476         McAccount      *account;
477         TpConn         *tp_conn;
478         gchar          *object_path;
479         const gchar    *bus_name;
480         TpChan         *new_chan;
481         EmpathyTpGroup *group;
482         GError         *error = NULL;
483
484         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
485
486         /* StreamedMedia channels must have handle=0 and handle_type=none.
487          * To call a contact we have to add him in the group interface of the
488          * channel. MissionControl will detect the channel creation and 
489          * dispatch it to the VoIP chandler automatically. */
490
491         mc = empathy_mission_control_new ();
492         account = empathy_contact_get_account (contact);
493         tp_conn = mission_control_get_connection (mc, account, NULL);
494         /* FIXME: Should be async */
495         if (!tp_conn_request_channel (DBUS_G_PROXY (tp_conn),
496                                       TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
497                                       TP_HANDLE_TYPE_NONE,
498                                       0,
499                                       FALSE,
500                                       &object_path,
501                                       &error)) {
502                 empathy_debug (DEBUG_DOMAIN, 
503                               "Couldn't request channel: %s",
504                               error ? error->message : "No error given");
505                 g_clear_error (&error);
506                 g_object_unref (mc);
507                 g_object_unref (tp_conn);
508                 return;
509         }
510
511         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
512         new_chan = tp_chan_new (tp_get_bus (),
513                                 bus_name,
514                                 object_path,
515                                 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
516                                 TP_HANDLE_TYPE_NONE,
517                                 0);
518
519         group = empathy_tp_group_new (account, new_chan);
520         empathy_tp_group_add_member (group, contact, "");
521
522         g_object_unref (group);
523         g_object_unref (mc);
524         g_object_unref (tp_conn);
525         g_object_unref (new_chan);
526         g_free (object_path);
527 #endif
528 }
529