1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of libaccounts-glib
5  *
6  * Copyright (C) 2009-2010 Nokia Corporation.
7  * Copyright (C) 2012-2016 Canonical Ltd.
8  * Copyright (C) 2012 Intel Corporation.
9  *
10  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
11  * Contact: Jussi Laako <jussi.laako@linux.intel.com>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public License
15  * version 2.1 as published by the Free Software Foundation.
16  *
17  * This library is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
25  * 02110-1301 USA
26  */
27 
28 #include "ag-util.h"
29 #include "ag-debug.h"
30 #include "ag-errors.h"
31 
32 #include <gio/gio.h>
33 #include <sched.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 GString *
_ag_string_append_printf(GString * string,const gchar * format,...)39 _ag_string_append_printf (GString *string, const gchar *format, ...)
40 {
41     va_list ap;
42     char *sql;
43 
44     va_start (ap, format);
45     sql = sqlite3_vmprintf (format, ap);
46     va_end (ap);
47 
48     if (sql)
49     {
50         g_string_append (string, sql);
51         sqlite3_free (sql);
52     }
53 
54     return string;
55 }
56 
57 GValue *
_ag_value_slice_dup(const GValue * value)58 _ag_value_slice_dup (const GValue *value)
59 {
60     GValue *copy;
61 
62     if (!value) return NULL;
63     copy = g_slice_new0 (GValue);
64     g_value_init (copy, G_VALUE_TYPE (value));
65     g_value_copy (value, copy);
66     return copy;
67 }
68 
69 void
_ag_value_slice_free(GValue * value)70 _ag_value_slice_free (GValue *value)
71 {
72     if (!value) return;
73     g_value_unset (value);
74     g_slice_free (GValue, value);
75 }
76 
77 GVariant *
_ag_value_to_variant(const GValue * in_value)78 _ag_value_to_variant (const GValue *in_value)
79 {
80     const GVariantType *type;
81     GValue transformed_value = G_VALUE_INIT;
82     const GValue *value;
83 
84     g_return_val_if_fail (in_value != NULL, NULL);
85 
86     /* transform some GValues which g_dbus_gvalue_to_gvariant() cannot handle */
87     if (G_VALUE_TYPE (in_value) == G_TYPE_CHAR)
88     {
89         g_value_init (&transformed_value, G_TYPE_INT);
90         if (G_UNLIKELY (!g_value_transform (in_value, &transformed_value)))
91         {
92             g_warning ("%s: could not transform %s to %s", G_STRFUNC,
93                        G_VALUE_TYPE_NAME (in_value),
94                        G_VALUE_TYPE_NAME (&transformed_value));
95             return NULL;
96         }
97 
98         value = &transformed_value;
99     }
100     else
101     {
102         value = in_value;
103     }
104 
105     type = _ag_type_from_g_type (G_VALUE_TYPE (value));
106     return g_dbus_gvalue_to_gvariant (value, type);
107 }
108 
109 gchar *
_ag_value_to_db(GVariant * value,gboolean type_annotate)110 _ag_value_to_db (GVariant *value, gboolean type_annotate)
111 {
112     return g_variant_print (value, type_annotate);
113 }
114 
115 const GVariantType *
_ag_type_from_g_type(GType type)116 _ag_type_from_g_type (GType type)
117 {
118     switch (type)
119     {
120     case G_TYPE_STRING:
121         return G_VARIANT_TYPE_STRING;
122     case G_TYPE_INT:
123     case G_TYPE_CHAR:
124         return G_VARIANT_TYPE_INT32;
125     case G_TYPE_UINT:
126         return G_VARIANT_TYPE_UINT32;
127     case G_TYPE_BOOLEAN:
128         return G_VARIANT_TYPE_BOOLEAN;
129     case G_TYPE_UCHAR:
130         return G_VARIANT_TYPE_BYTE;
131     case G_TYPE_INT64:
132         return G_VARIANT_TYPE_INT64;
133     case G_TYPE_UINT64:
134         return G_VARIANT_TYPE_UINT64;
135     default:
136         /* handle dynamic types here */
137         if (type == G_TYPE_STRV)
138             return G_VARIANT_TYPE_STRING_ARRAY;
139 
140         g_warning ("%s: unsupported type ``%s''", G_STRFUNC,
141                    g_type_name (type));
142         return NULL;
143     }
144 }
145 
146 void
_ag_value_from_variant(GValue * value,GVariant * variant)147 _ag_value_from_variant (GValue *value, GVariant *variant)
148 {
149     g_dbus_gvariant_to_gvalue (variant, value);
150 }
151 
152 static GVariant *
_ag_value_from_string(const gchar * type,const gchar * string)153 _ag_value_from_string (const gchar *type, const gchar *string)
154 {
155     GVariant *variant;
156     GError *error = NULL;
157 
158     if (G_UNLIKELY (!string)) return NULL;
159 
160     /* g_variant_parse() expects all strings to be enclosed in quotes, which we
161      * wouldn't like to enforce in the XML files. So, if we know that we are
162      * reading a string, just build the GValue right away */
163     if (type != NULL && type[0] == 's' && type[1] == '\0' &&
164         string[0] != '"' && string[0] != '\'')
165     {
166         return g_variant_new_string (string);
167     }
168 
169     variant = g_variant_parse ((GVariantType *)type, string,
170                                NULL, NULL, &error);
171     if (error != 0)
172     {
173         g_warning ("%s: error parsing type \"%s\" ``%s'': %s",
174                    G_STRFUNC, type, string, error->message);
175         g_error_free (error);
176         return NULL;
177     }
178 
179     return variant;
180 }
181 
182 GVariant *
_ag_value_from_db(sqlite3_stmt * stmt,gint col_type,gint col_value)183 _ag_value_from_db (sqlite3_stmt *stmt, gint col_type, gint col_value)
184 {
185     gchar *string_value;
186     gchar *type;
187 
188     type = (gchar *)sqlite3_column_text (stmt, col_type);
189     string_value = (gchar *)sqlite3_column_text (stmt, col_value);
190 
191     return _ag_value_from_string (type, string_value);
192 }
193 
194 /**
195  * ag_errors_quark:
196  *
197  * Return the libaccounts-glib error domain.
198  *
199  * Returns: the libaccounts-glib error domain.
200  */
201 GQuark
ag_errors_quark(void)202 ag_errors_quark (void)
203 {
204     static gsize quark = 0;
205 
206     if (g_once_init_enter (&quark))
207     {
208         GQuark domain = g_quark_from_static_string ("ag_errors");
209 
210         g_assert (sizeof (GQuark) <= sizeof (gsize));
211 
212         g_once_init_leave (&quark, domain);
213     }
214 
215     return (GQuark) quark;
216 }
217 
218 /**
219  * ag_accounts_error_quark:
220  *
221  * Return the libaccounts-glib error domain.
222  *
223  * Returns: the libaccounts-glib error domain.
224  */
225 GQuark
ag_accounts_error_quark(void)226 ag_accounts_error_quark (void)
227 {
228     return ag_errors_quark ();
229 }
230 
231 gboolean
_ag_xml_get_element_data(xmlTextReaderPtr reader,const gchar ** dest_ptr)232 _ag_xml_get_element_data (xmlTextReaderPtr reader, const gchar **dest_ptr)
233 {
234     gint node_type;
235 
236     if (dest_ptr) *dest_ptr = NULL;
237 
238     if (xmlTextReaderIsEmptyElement (reader))
239         return TRUE;
240 
241     if (xmlTextReaderRead (reader) != 1)
242         return FALSE;
243 
244     node_type = xmlTextReaderNodeType (reader);
245     if (node_type != XML_READER_TYPE_TEXT)
246         return (node_type == XML_READER_TYPE_END_ELEMENT) ? TRUE : FALSE;
247 
248     if (dest_ptr)
249         *dest_ptr = (const gchar *)xmlTextReaderConstValue (reader);
250 
251     return TRUE;
252 }
253 
254 static gboolean
close_element(xmlTextReaderPtr reader)255 close_element (xmlTextReaderPtr reader)
256 {
257     if (xmlTextReaderRead (reader) != 1 ||
258         xmlTextReaderNodeType (reader) != XML_READER_TYPE_END_ELEMENT)
259         return FALSE;
260 
261     return TRUE;
262 }
263 
264 gboolean
_ag_xml_dup_element_data(xmlTextReaderPtr reader,gchar ** dest_ptr)265 _ag_xml_dup_element_data (xmlTextReaderPtr reader, gchar **dest_ptr)
266 {
267     const gchar *data;
268     gboolean ret;
269 
270     ret = _ag_xml_get_element_data (reader, &data);
271     if (dest_ptr)
272         *dest_ptr = g_strdup (data);
273 
274     close_element (reader);
275     return ret;
276 }
277 
278 gboolean
_ag_xml_get_boolean(xmlTextReaderPtr reader,gboolean * dest_boolean)279 _ag_xml_get_boolean (xmlTextReaderPtr reader, gboolean *dest_boolean)
280 {
281     GVariant *variant;
282     const gchar *data;
283     gboolean ok;
284 
285     ok = _ag_xml_get_element_data (reader, &data);
286     if (G_UNLIKELY (!ok)) return FALSE;
287 
288     variant = _ag_value_from_string ("b", data);
289     if (G_UNLIKELY (variant == NULL)) return FALSE;
290 
291     *dest_boolean = g_variant_get_boolean (variant);
292     g_variant_unref (variant);
293 
294     ok = close_element (reader);
295 
296     return ok;
297 }
298 
299 static gboolean
parse_param(xmlTextReaderPtr reader,GVariant ** value)300 parse_param (xmlTextReaderPtr reader, GVariant **value)
301 {
302     const gchar *str_value;
303     xmlChar *str_type = NULL;
304     gboolean ok;
305     const gchar *type;
306 
307     str_type = xmlTextReaderGetAttribute (reader,
308                                           (xmlChar *) "type");
309     if (!str_type)
310         type = "s";
311     else
312     {
313         type = (const gchar*)str_type;
314     }
315 
316     ok = _ag_xml_get_element_data (reader, &str_value);
317     if (G_UNLIKELY (!ok)) goto error;
318 
319     /* Empty value is not an error, but simply ignored */
320     if (G_UNLIKELY (!str_value)) goto finish;
321 
322     *value = _ag_value_from_string (type, str_value);
323 
324     ok = close_element (reader);
325     if (G_UNLIKELY (!ok))
326     {
327         g_variant_unref (*value);
328         *value = NULL;
329         goto error;
330     }
331 
332 finish:
333     ok = TRUE;
334 error:
335     if (str_type != NULL)
336         xmlFree(str_type);
337     return ok;
338 }
339 
340 gboolean
_ag_xml_parse_settings(xmlTextReaderPtr reader,const gchar * group,GHashTable * settings)341 _ag_xml_parse_settings (xmlTextReaderPtr reader, const gchar *group,
342                         GHashTable *settings)
343 {
344     const gchar *name;
345     int ret, type;
346 
347     ret = xmlTextReaderRead (reader);
348     while (ret == 1)
349     {
350         name = (const gchar *)xmlTextReaderConstName (reader);
351         if (G_UNLIKELY (!name)) return FALSE;
352 
353         type = xmlTextReaderNodeType (reader);
354         if (type == XML_READER_TYPE_END_ELEMENT)
355             break;
356 
357         if (type == XML_READER_TYPE_ELEMENT)
358         {
359             gboolean ok;
360 
361             DEBUG_INFO ("found name %s", name);
362             if (strcmp (name, "setting") == 0)
363             {
364                 GVariant *value = NULL;
365                 xmlChar *key_name;
366                 gchar *key;
367 
368                 key_name = xmlTextReaderGetAttribute (reader, (xmlChar *)"name");
369                 key = g_strdup_printf ("%s%s", group, (const gchar*)key_name);
370 
371                 if (key_name) xmlFree (key_name);
372 
373                 ok = parse_param (reader, &value);
374                 if (ok && value != NULL)
375                 {
376                     g_variant_take_ref (value);
377                     g_hash_table_insert (settings, key, value);
378                 }
379                 else
380                 {
381                     if (value != NULL) g_variant_unref (value);
382                     g_free (key);
383                 }
384             }
385             else if (strcmp (name, "group") == 0 &&
386                      xmlTextReaderHasAttributes (reader))
387             {
388                 /* it's a subgroup */
389                 if (!xmlTextReaderIsEmptyElement (reader))
390                 {
391                     xmlChar *group_name;
392                     gchar *subgroup;
393 
394                     group_name = xmlTextReaderGetAttribute (reader,
395                                                             (xmlChar *)"name");
396                     subgroup = g_strdup_printf ("%s%s/", group,
397                                                 (const gchar *)group_name);
398                     if (group_name) xmlFree (group_name);
399 
400                     ok = _ag_xml_parse_settings (reader, subgroup, settings);
401                     g_free (subgroup);
402                 }
403                 else
404                     ok = TRUE;
405             }
406             else
407             {
408                 g_warning ("%s: using wrong XML for groups; "
409                            "please change to <group name=\"%s\">",
410                            xmlTextReaderConstBaseUri (reader), name);
411                 /* it's a subgroup */
412                 if (!xmlTextReaderIsEmptyElement (reader))
413                 {
414                     gchar *subgroup;
415 
416                     subgroup = g_strdup_printf ("%s%s/", group, name);
417                     ok = _ag_xml_parse_settings (reader, subgroup, settings);
418                     g_free (subgroup);
419                 }
420                 else
421                     ok = TRUE;
422             }
423 
424             if (G_UNLIKELY (!ok)) return FALSE;
425         }
426 
427         ret = xmlTextReaderNext (reader);
428     }
429     return TRUE;
430 }
431 
_ag_xml_parse_element_list(xmlTextReaderPtr reader,const gchar * match,GHashTable ** list)432 gboolean _ag_xml_parse_element_list (xmlTextReaderPtr reader, const gchar *match,
433                                      GHashTable **list)
434 {
435     gboolean ok = FALSE;
436     const gchar *ename;
437     gchar *data;
438     int res, etype;
439 
440     *list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
441 
442     res = xmlTextReaderRead (reader);
443     while (res == 1)
444     {
445         ename = (const gchar *) xmlTextReaderConstName (reader);
446         if (G_UNLIKELY (!ename)) return FALSE;
447 
448         etype = xmlTextReaderNodeType (reader);
449         if (etype == XML_READER_TYPE_END_ELEMENT)
450             break;
451 
452         if (etype == XML_READER_TYPE_ELEMENT)
453         {
454             if (strcmp (ename, match) == 0)
455             {
456                 if (_ag_xml_dup_element_data (reader, &data))
457                 {
458                     g_hash_table_insert (*list, data, NULL);
459                     ok = TRUE;
460                 }
461                 else return FALSE;
462             }
463         }
464 
465         res = xmlTextReaderNext (reader);
466     }
467     return ok;
468 }
469 
470 static inline gboolean
_esc_ident_bad(gchar c,gboolean is_first)471 _esc_ident_bad (gchar c, gboolean is_first)
472 {
473   return ((c < 'a' || c > 'z') &&
474           (c < 'A' || c > 'Z') &&
475           (c < '0' || c > '9' || is_first));
476 }
477 
478 /**
479  * _ag_dbus_escape_as_identifier:
480  * @name: The string to be escaped
481  *
482  * Taken from telepathy-glib's tp_escape_as_identifier().
483  *
484  * Escape an arbitrary string so it follows the rules for a C identifier,
485  * and hence an object path component, interface element component,
486  * bus name component or member name in D-Bus.
487  *
488  * Unlike g_strcanon this is a reversible encoding, so it preserves
489  * distinctness.
490  *
491  * The escaping consists of replacing all non-alphanumerics, and the first
492  * character if it's a digit, with an underscore and two lower-case hex
493  * digits:
494  *
495  *    "0123abc_xyz\x01\xff" -> _30123abc_5fxyz_01_ff
496  *
497  * i.e. similar to URI encoding, but with _ taking the role of %, and a
498  * smaller allowed set. As a special case, "" is escaped to "_" (just for
499  * completeness, really).
500  *
501  * Returns: the escaped string, which must be freed by the caller with #g_free
502  */
503 gchar *
_ag_dbus_escape_as_identifier(const gchar * name)504 _ag_dbus_escape_as_identifier (const gchar *name)
505 {
506     gboolean bad = FALSE;
507     size_t len = 0;
508     GString *op;
509     const gchar *ptr, *first_ok;
510 
511     g_return_val_if_fail (name != NULL, NULL);
512 
513     /* fast path for empty name */
514     if (name[0] == '\0')
515         return g_strdup ("_");
516 
517     for (ptr = name; *ptr; ptr++)
518     {
519         if (_esc_ident_bad (*ptr, ptr == name))
520         {
521             bad = TRUE;
522             len += 3;
523         }
524         else
525             len++;
526     }
527 
528     /* fast path if it's clean */
529     if (!bad)
530         return g_strdup (name);
531 
532     /* If strictly less than ptr, first_ok is the first uncopied safe
533      * character. */
534     first_ok = name;
535     op = g_string_sized_new (len);
536     for (ptr = name; *ptr; ptr++)
537     {
538         if (_esc_ident_bad (*ptr, ptr == name))
539         {
540             /* copy preceding safe characters if any */
541             if (first_ok < ptr)
542             {
543                 g_string_append_len (op, first_ok, ptr - first_ok);
544             }
545             /* escape the unsafe character */
546             g_string_append_printf (op, "_%02x", (unsigned char)(*ptr));
547             /* restart after it */
548             first_ok = ptr + 1;
549         }
550     }
551     /* copy trailing safe characters if any */
552     if (first_ok < ptr)
553     {
554         g_string_append_len (op, first_ok, ptr - first_ok);
555     }
556     return g_string_free (op, FALSE);
557 }
558 
559 /**
560  * _ag_find_libaccounts_file:
561  * @file_id: the base name of the file, without suffix.
562  * @suffix: the file suffix.
563  * @env_var: name of the environment variable which could specify an override
564  * path.
565  * @subdir: file will be searched in $XDG_DATA_DIRS/<subdir>/
566  *
567  * Search for the libaccounts file @file_id.
568  *
569  * Returns: the path of the file, if found, %NULL otherwise.
570  */
571 gchar *
_ag_find_libaccounts_file(const gchar * file_id,const gchar * suffix,const gchar * env_var,const gchar * subdir)572 _ag_find_libaccounts_file (const gchar *file_id,
573                            const gchar *suffix,
574                            const gchar *env_var,
575                            const gchar *subdir)
576 {
577     const gchar * const *dirs;
578     const gchar *dirname;
579     const gchar *env_dirname;
580     gchar *filename, *filepath, *desktop_override = NULL;
581 
582     filename = g_strconcat (file_id, suffix, NULL);
583     env_dirname = g_getenv (env_var);
584     if (env_dirname)
585     {
586         filepath = g_build_filename (env_dirname, filename, NULL);
587         if (g_file_test (filepath, G_FILE_TEST_IS_REGULAR))
588             goto found;
589         g_free (filepath);
590     }
591 
592     dirname = g_get_user_data_dir ();
593     if (G_LIKELY (dirname))
594     {
595         filepath = g_build_filename (dirname, subdir, filename, NULL);
596         if (g_file_test (filepath, G_FILE_TEST_IS_REGULAR))
597             goto found;
598         g_free (filepath);
599     }
600 
601     /* Check what desktop is this running on */
602     env_dirname = g_getenv ("XDG_CURRENT_DESKTOP");
603     if (env_dirname)
604         desktop_override = g_ascii_strdown (env_dirname, -1);
605 
606     dirs = g_get_system_data_dirs ();
607     for (dirname = *dirs; dirname != NULL; dirs++, dirname = *dirs)
608     {
609         /* Check first if desktop override files exist and if yes, load them first */
610         if (desktop_override)
611         {
612             filepath = g_build_filename (dirname, subdir, desktop_override, filename, NULL);
613             if (g_file_test (filepath, G_FILE_TEST_IS_REGULAR))
614                 goto found;
615             g_free (filepath);
616         }
617         filepath = g_build_filename (dirname, subdir, filename, NULL);
618         if (g_file_test (filepath, G_FILE_TEST_IS_REGULAR))
619             goto found;
620         g_free (filepath);
621     }
622 
623     filepath = NULL;
624 found:
625     g_free (desktop_override);
626     g_free (filename);
627     return filepath;
628 }
629 
630