tests: Fix up the javascript and python tests
[platform/upstream/libsecret.git] / libsecret / secret-attributes.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2011 Collabora Ltd.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2.1 of the licence or (at
8  * your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  *
12  * Author: Stef Walter <stefw@gnome.org>
13  */
14
15 #include "config.h"
16
17 #include "secret-attributes.h"
18 #include "secret-private.h"
19
20 #include <string.h>
21
22 /**
23  * SECTION:secret-attributes
24  * @title: Secret Attributes
25  * @short_description: secret attributes
26  *
27  * Each item has a set of attributes, which are used to locate the item later.
28  * These are not stored or transferred in a secure manner. Each attribute has
29  * a string name and a string value. These attributes are represented by a
30  * #GHashTable with string keys and values.
31  *
32  * Use secret_attributes_build() to simply build up a set of attributes.
33  */
34
35 GVariant *
36 _secret_attributes_to_variant (GHashTable *attributes,
37                                const gchar *schema_name)
38 {
39         GHashTableIter iter;
40         GVariantBuilder builder;
41         const gchar *name;
42         const gchar *value;
43
44         g_return_val_if_fail (attributes != NULL, NULL);
45
46         g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
47
48         g_hash_table_iter_init (&iter, attributes);
49         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&value)) {
50                 if (!schema_name || !g_str_equal (name, "xdg:schema"))
51                         g_variant_builder_add (&builder, "{ss}", name, value);
52         }
53
54         if (schema_name)
55                 g_variant_builder_add (&builder, "{ss}", "xdg:schema", schema_name);
56
57         return g_variant_builder_end (&builder);
58 }
59
60 GHashTable *
61 _secret_attributes_for_variant (GVariant *variant)
62 {
63         GVariantIter iter;
64         GHashTable *attributes;
65         gchar *value;
66         gchar *key;
67
68         attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
69
70         g_variant_iter_init (&iter, variant);
71         while (g_variant_iter_next (&iter, "{ss}", &key, &value))
72                 g_hash_table_insert (attributes, key, value);
73
74         return attributes;
75 }
76
77 /**
78  * secret_attributes_build: (skip)
79  * @schema: the schema for the attributes
80  * @...: the attribute keys and values, terminated with %NULL
81  *
82  * Build up a hash table of attribute values.
83  *
84  * The variable argument list should contain pairs of a) The attribute name as
85  * a null-terminated string, followed by b) attribute value, either a character
86  * string, an int number, or a gboolean value, as defined in the password
87  * @schema. The list of attribtues should be terminated with a %NULL.
88  *
89  * Returns: (transfer full) (element-type utf8 utf8): a new table of
90  *          attributes, to be released with g_hash_table_unref()
91  */
92 GHashTable *
93 secret_attributes_build (const SecretSchema *schema,
94                          ...)
95 {
96         GHashTable *attributes;
97         va_list va;
98
99         va_start (va, schema);
100         attributes = secret_attributes_buildv (schema, va);
101         va_end (va);
102
103         return attributes;
104 }
105
106 /**
107  * secret_attributes_buildv: (skip)
108  * @schema: the schema for the attributes
109  * @va: the attribute keys and values, terminated with %NULL
110  *
111  * Build up a hash table of attribute values.
112  *
113  * The variable argument list should contain pairs of a) The attribute name as
114  * a null-terminated string, followed by b) attribute value, either a character
115  * string, an int number, or a gboolean value, as defined in the password
116  * @schema. The list of attribtues should be terminated with a %NULL.
117  *
118  * Returns: (transfer full) (element-type utf8 utf8): a new table of
119  *          attributes, to be released with g_hash_table_unref()
120  */
121 GHashTable *
122 secret_attributes_buildv (const SecretSchema *schema,
123                           va_list va)
124 {
125         const gchar *attribute_name;
126         SecretSchemaAttributeType type;
127         GHashTable *attributes;
128         const gchar *string;
129         gboolean type_found;
130         gchar *value = NULL;
131         gboolean boolean;
132         gint integer;
133         gint i;
134
135         g_return_val_if_fail (schema != NULL, NULL);
136
137         attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
138
139         for (;;) {
140                 attribute_name = va_arg (va, const gchar *);
141                 if (attribute_name == NULL)
142                         break;
143
144                 type_found = FALSE;
145                 for (i = 0; i < G_N_ELEMENTS (schema->attributes); ++i) {
146                         if (!schema->attributes[i].name)
147                                 break;
148                         if (g_str_equal (schema->attributes[i].name, attribute_name)) {
149                                 type_found = TRUE;
150                                 type = schema->attributes[i].type;
151                                 break;
152                         }
153                 }
154
155                 if (!type_found) {
156                         g_critical ("The attribute '%s' was not found in the password schema.", attribute_name);
157                         g_hash_table_unref (attributes);
158                         return NULL;
159                 }
160
161                 switch (type) {
162                 case SECRET_SCHEMA_ATTRIBUTE_BOOLEAN:
163                         boolean = va_arg (va, gboolean);
164                         value = g_strdup (boolean ? "true" : "false");
165                         break;
166                 case SECRET_SCHEMA_ATTRIBUTE_STRING:
167                         string = va_arg (va, gchar *);
168                         if (string == NULL) {
169                                 g_critical ("The value for attribute '%s' was NULL", attribute_name);
170                                 return NULL;
171                         }
172                         if (!g_utf8_validate (string, -1, NULL)) {
173                                 g_critical ("The value for attribute '%s' was not a valid UTF-8 string.", attribute_name);
174                                 g_hash_table_unref (attributes);
175                                 return NULL;
176                         }
177                         value = g_strdup (string);
178                         break;
179                 case SECRET_SCHEMA_ATTRIBUTE_INTEGER:
180                         integer = va_arg (va, gint);
181                         value = g_strdup_printf ("%d", integer);
182                         break;
183                 default:
184                         g_critical ("The password attribute '%s' has an invalid type in the password schema.", attribute_name);
185                         g_hash_table_unref (attributes);
186                         return NULL;
187                 }
188
189                 g_hash_table_insert (attributes, g_strdup (attribute_name), value);
190         }
191
192         return attributes;
193 }
194
195 gboolean
196 _secret_attributes_validate (const SecretSchema *schema,
197                              GHashTable *attributes,
198                              const char *pretty_function,
199                              gboolean matching)
200 {
201         const SecretSchemaAttribute *attribute;
202         GHashTableIter iter;
203         gboolean any;
204         gchar *key;
205         gchar *value;
206         gchar *end;
207         gint i;
208
209         g_return_val_if_fail (schema != NULL, FALSE);
210
211         g_hash_table_iter_init (&iter, attributes);
212         while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value)) {
213                 any = TRUE;
214
215                 /* If the 'xdg:schema' meta-attribute is present,
216                    ensure that it is consistent with the schema
217                    name. */
218                 if (g_str_equal (key, "xdg:schema")) {
219                         if (!g_str_equal (value, schema->name)) {
220                                 g_critical ("%s: xdg:schema value %s differs from schema %s:",
221                                             pretty_function, value, schema->name);
222                                 return FALSE;
223                         }
224                         continue;
225                 }
226
227                 /* Pass through libgnomekeyring specific attributes */
228                 if (g_str_has_prefix (key, "gkr:"))
229                         continue;
230
231                 /* Find the attribute */
232                 attribute = NULL;
233                 for (i = 0; i < G_N_ELEMENTS (schema->attributes); i++) {
234                         if (schema->attributes[i].name == NULL)
235                                 break;
236                         if (g_str_equal (schema->attributes[i].name, key)) {
237                                 attribute = &schema->attributes[i];
238                                 break;
239                         }
240                 }
241
242                 if (attribute == NULL) {
243                         g_critical ("%s: invalid %s attribute for %s schema",
244                                     pretty_function, key, schema->name);
245                         return FALSE;
246                 }
247
248                 switch (attribute->type) {
249                 case SECRET_SCHEMA_ATTRIBUTE_BOOLEAN:
250                         if (!g_str_equal (value, "true") && !g_str_equal (value, "false")) {
251                                 g_critical ("%s: invalid %s boolean value for %s schema: %s",
252                                             pretty_function, key, schema->name, value);
253                                 return FALSE;
254                         }
255                         break;
256                 case SECRET_SCHEMA_ATTRIBUTE_INTEGER:
257                         end = NULL;
258                         g_ascii_strtoll (value, &end, 10);
259                         if (!end || end[0] != '\0') {
260                                 g_warning ("%s: invalid %s integer value for %s schema: %s",
261                                            pretty_function, key, schema->name, value);
262                                 return FALSE;
263                         }
264                         break;
265                 case SECRET_SCHEMA_ATTRIBUTE_STRING:
266                         if (!g_utf8_validate (value, -1, NULL)) {
267                                 g_warning ("%s: invalid %s string value for %s schema: %s",
268                                            pretty_function, key, schema->name, value);
269                                 return FALSE;
270                         }
271                         break;
272                 default:
273                         g_warning ("%s: invalid %s value type in %s schema",
274                                    pretty_function, key, schema->name);
275                         return FALSE;
276                 }
277         }
278
279         /* Nothing to match on, resulting search would match everything :S */
280         if (matching && !any && schema->flags & SECRET_SCHEMA_DONT_MATCH_NAME) {
281                 g_warning ("%s: must specify at least one attribute to match",
282                            pretty_function);
283                 return FALSE;
284         }
285
286         return TRUE;
287 }
288
289 GHashTable *
290 _secret_attributes_copy (GHashTable *attributes)
291 {
292         GHashTableIter iter;
293         GHashTable *copy;
294         gchar *key;
295         gchar *value;
296
297         if (attributes == NULL)
298                 return NULL;
299
300         copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
301
302         g_hash_table_iter_init (&iter, attributes);
303         while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
304                 g_hash_table_insert (copy, g_strdup (key), g_strdup (value));
305
306         return copy;
307 }