]> git.0d.be Git - empathy.git/blob - libempathy/empathy-utils.c
Conflicts:
[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 (DATADIR, "empathy", dtd_filename, NULL);
212
213         /* The list of valid chars is taken from libxml. */
214         escaped = xmlURIEscapeStr (path, ":@&=+$,/?;");
215
216         g_free (path);
217
218         memset (&cvp, 0, sizeof (cvp));
219         dtd = xmlParseDTD (NULL, escaped);
220         ret = xmlValidateDtd (&cvp, doc, dtd);
221
222         xmlFree (escaped);
223         xmlFreeDtd (dtd);
224
225         return ret;
226 }
227
228 xmlNodePtr
229 empathy_xml_node_get_child (xmlNodePtr   node, 
230                            const gchar *child_name)
231 {
232         xmlNodePtr l;
233
234         g_return_val_if_fail (node != NULL, NULL);
235         g_return_val_if_fail (child_name != NULL, NULL);
236
237         for (l = node->children; l; l = l->next) {
238                 if (l->name && strcmp (l->name, child_name) == 0) {
239                         return l;
240                 }
241         }
242
243         return NULL;
244 }
245
246 xmlChar *
247 empathy_xml_node_get_child_content (xmlNodePtr   node, 
248                                    const gchar *child_name)
249 {
250         xmlNodePtr l;
251
252         g_return_val_if_fail (node != NULL, NULL);
253         g_return_val_if_fail (child_name != NULL, NULL);
254
255         l = empathy_xml_node_get_child (node, child_name);
256         if (l) {
257                 return xmlNodeGetContent (l);
258         }
259                 
260         return NULL;
261 }
262
263 xmlNodePtr
264 empathy_xml_node_find_child_prop_value (xmlNodePtr   node, 
265                                        const gchar *prop_name,
266                                        const gchar *prop_value)
267 {
268         xmlNodePtr l;
269         xmlNodePtr found = NULL;
270
271         g_return_val_if_fail (node != NULL, NULL);
272         g_return_val_if_fail (prop_name != NULL, NULL);
273         g_return_val_if_fail (prop_value != NULL, NULL);
274
275         for (l = node->children; l && !found; l = l->next) {
276                 xmlChar *prop;
277
278                 if (!xmlHasProp (l, prop_name)) {
279                         continue;
280                 }
281
282                 prop = xmlGetProp (l, prop_name);
283                 if (prop && strcmp (prop, prop_value) == 0) {
284                         found = l;
285                 }
286                 
287                 xmlFree (prop);
288         }
289                 
290         return found;
291 }
292
293 guint
294 empathy_account_hash (gconstpointer key)
295 {
296         g_return_val_if_fail (MC_IS_ACCOUNT (key), 0);
297
298         return g_str_hash (mc_account_get_unique_name (MC_ACCOUNT (key)));
299 }
300
301 gboolean
302 empathy_account_equal (gconstpointer a,
303                        gconstpointer b)
304 {
305         const gchar *name_a;
306         const gchar *name_b;
307
308         g_return_val_if_fail (MC_IS_ACCOUNT (a), FALSE);
309         g_return_val_if_fail (MC_IS_ACCOUNT (b), FALSE);
310
311         name_a = mc_account_get_unique_name (MC_ACCOUNT (a));
312         name_b = mc_account_get_unique_name (MC_ACCOUNT (b));
313
314         return g_str_equal (name_a, name_b);
315 }
316
317 MissionControl *
318 empathy_mission_control_new (void)
319 {
320         static MissionControl *mc = NULL;
321
322         if (!mc) {
323                 mc = mission_control_new (tp_get_bus ());
324                 g_object_add_weak_pointer (G_OBJECT (mc), (gpointer) &mc);
325         } else {
326                 g_object_ref (mc);
327         }
328
329         return mc;
330 }
331
332 gchar *
333 empathy_inspect_channel (McAccount *account,
334                          TpChan    *tp_chan)
335 {
336         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
337         g_return_val_if_fail (TELEPATHY_IS_CHAN (tp_chan), NULL);
338
339         return empathy_inspect_handle (account,
340                                        tp_chan->handle,
341                                        tp_chan->handle_type);
342 }
343
344 gchar *
345 empathy_inspect_handle (McAccount *account,
346                         guint      handle,
347                         guint      handle_type)
348 {
349         MissionControl  *mc;
350         TpConn          *tp_conn;
351         GArray          *handles;
352         gchar          **names;
353         gchar           *name;
354         GError          *error = NULL;
355
356         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
357         g_return_val_if_fail (handle != 0, NULL);
358         g_return_val_if_fail (handle_type != 0, NULL);
359
360         mc = empathy_mission_control_new ();
361         tp_conn = mission_control_get_connection (mc, account, NULL);
362         g_object_unref (mc);
363
364         if (!tp_conn) {
365                 return NULL;
366         }
367
368         /* Get the handle's name */
369         handles = g_array_new (FALSE, FALSE, sizeof (guint));
370         g_array_append_val (handles, handle);
371         if (!tp_conn_inspect_handles (DBUS_G_PROXY (tp_conn),
372                                       handle_type,
373                                       handles,
374                                       &names,
375                                       &error)) {
376                 empathy_debug (DEBUG_DOMAIN, 
377                               "Couldn't get id: %s",
378                               error ? error->message : "No error given");
379
380                 g_clear_error (&error);
381                 g_array_free (handles, TRUE);
382                 g_object_unref (tp_conn);
383                 
384                 return NULL;
385         }
386
387         g_array_free (handles, TRUE);
388         name = *names;
389         g_free (names);
390         g_object_unref (tp_conn);
391
392         return name;
393 }
394
395 void
396 empathy_call_contact (EmpathyContact *contact)
397 {
398 #ifdef HAVE_VOIP
399         MissionControl *mc;
400         McAccount      *account;
401         TpConn         *tp_conn;
402         gchar          *object_path;
403         const gchar    *bus_name;
404         TpChan         *new_chan;
405         EmpathyTpGroup *group;
406         GError         *error = NULL;
407
408         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
409
410         /* StreamedMedia channels must have handle=0 and handle_type=none.
411          * To call a contact we have to add him in the group interface of the
412          * channel. MissionControl will detect the channel creation and 
413          * dispatch it to the VoIP chandler automatically. */
414
415         mc = empathy_mission_control_new ();
416         account = empathy_contact_get_account (contact);
417         tp_conn = mission_control_get_connection (mc, account, NULL);
418         /* FIXME: Should be async */
419         if (!tp_conn_request_channel (DBUS_G_PROXY (tp_conn),
420                                       TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
421                                       TP_HANDLE_TYPE_NONE,
422                                       0,
423                                       FALSE,
424                                       &object_path,
425                                       &error)) {
426                 empathy_debug (DEBUG_DOMAIN, 
427                               "Couldn't request channel: %s",
428                               error ? error->message : "No error given");
429                 g_clear_error (&error);
430                 g_object_unref (mc);
431                 g_object_unref (tp_conn);
432                 return;
433         }
434
435         bus_name = dbus_g_proxy_get_bus_name (DBUS_G_PROXY (tp_conn));
436         new_chan = tp_chan_new (tp_get_bus (),
437                                 bus_name,
438                                 object_path,
439                                 TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA,
440                                 TP_HANDLE_TYPE_NONE,
441                                 0);
442
443         group = empathy_tp_group_new (account, new_chan);
444         empathy_tp_group_add_member (group, contact, "");
445
446         g_object_unref (group);
447         g_object_unref (mc);
448         g_object_unref (tp_conn);
449         g_object_unref (new_chan);
450         g_free (object_path);
451 #endif
452 }
453