Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
rb-ext-db-key.c:527:23 | clang-analyzer | Access to field 'values' results in a dereference of a null pointer (loaded from variable 'f') | ||
rb-ext-db-key.c:527:23 | clang-analyzer | Access to field 'values' results in a dereference of a null pointer (loaded from variable 'f') |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2011 Jonathan Matthew <jonathan@d14n.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #include "config.h"
30
31 #include <string.h>
32
33 #include "rb-ext-db-key.h"
34
35 #include "rb-debug.h"
36
37 /**
38 * SECTION:rb-ext-db-key
39 * @short_description: key for external metadata lookups
40 *
41 * An external metadata key consists of one or more required fields (such as
42 * the album name for album art lookups), zero or more optional fields
43 * (such as the artist name), and zero or more informational fields (such as
44 * the musicbrainz album ID).
45 */
46
47 typedef struct
48 {
49 char *name;
50 GPtrArray *values;
51 gboolean match_null;
52 } RBExtDBField;
53
54 struct _RBExtDBKey
55 {
56 gboolean lookup;
57 RBExtDBField *multi_field;
58 GList *fields;
59 GList *info;
60 };
61
62 static void
63 rb_ext_db_field_free (RBExtDBField *field)
64 {
65 g_free (field->name);
66 g_ptr_array_free (field->values, TRUE);
67 g_slice_free (RBExtDBField, field);
68 }
69
70 static RBExtDBField *
71 rb_ext_db_field_copy (RBExtDBField *field)
72 {
73 RBExtDBField *copy;
74 int i;
75
76 copy = g_slice_new0 (RBExtDBField);
77 copy->name = g_strdup (field->name);
78 copy->values = g_ptr_array_new_with_free_func (g_free);
79 for (i = 0; i < field->values->len; i++) {
80 g_ptr_array_add (copy->values, g_strdup (g_ptr_array_index (field->values, i)));
81 }
82 return copy;
83 }
84
85 GType
86 rb_ext_db_key_get_type (void)
87 {
88 static GType type = 0;
89
90 if (G_UNLIKELY (type == 0)) {
91 type = g_boxed_type_register_static ("RBExtDBKey",
92 (GBoxedCopyFunc) rb_ext_db_key_copy,
93 (GBoxedFreeFunc) rb_ext_db_key_free);
94 }
95
96 return type;
97 }
98
99 /**
100 * rb_ext_db_key_copy:
101 * @key: a #RBExtDBKey
102 *
103 * Copies a key.
104 *
105 * Return value: copied key
106 */
107 RBExtDBKey *
108 rb_ext_db_key_copy (RBExtDBKey *key)
109 {
110 RBExtDBKey *copy;
111 GList *l;
112
113 copy = g_slice_new0 (RBExtDBKey);
114 copy->lookup = key->lookup;
115 copy->multi_field = key->multi_field;
116 for (l = key->fields; l != NULL; l = l->next) {
117 copy->fields = g_list_append (copy->fields, rb_ext_db_field_copy (l->data));
118 }
119 for (l = key->info; l != NULL; l = l->next) {
120 copy->info = g_list_append (copy->info, rb_ext_db_field_copy (l->data));
121 }
122 return copy;
123 }
124
125 /**
126 * rb_ext_db_key_free:
127 * @key: a #RBExtDBKey
128 *
129 * Frees a key
130 */
131 void
132 rb_ext_db_key_free (RBExtDBKey *key)
133 {
134 g_list_free_full (key->fields, (GDestroyNotify) rb_ext_db_field_free);
135 g_list_free_full (key->info, (GDestroyNotify) rb_ext_db_field_free);
136 g_slice_free (RBExtDBKey, key);
137 }
138
139 static RBExtDBKey *
140 do_create (const char *field, const char *value, gboolean lookup)
141 {
142 RBExtDBKey *key;
143 key = g_slice_new0 (RBExtDBKey);
144 key->lookup = lookup;
145 rb_ext_db_key_add_field (key, field, value);
146 return key;
147 }
148
149 /**
150 * rb_ext_db_key_create_lookup:
151 * @field: required field name
152 * @value: value for field
153 *
154 * Creates a new metadata lookup key with a single field.
155 * Use @rb_ext_db_key_add_field to add more.
156 *
157 * Return value: the new key
158 */
159 RBExtDBKey *
160 rb_ext_db_key_create_lookup (const char *field, const char *value)
161 {
162 return do_create (field, value, TRUE);
163 }
164
165 /**
166 * rb_ext_db_key_create_storage:
167 * @field: required field name
168 * @value: value for field
169 *
170 * Creates a new metadata storage key with a single field.
171 * Use @rb_ext_db_key_add_field to add more.
172 *
173 * Return value: the new key
174 */
175 RBExtDBKey *
176 rb_ext_db_key_create_storage (const char *field, const char *value)
177 {
178 return do_create (field, value, FALSE);
179 }
180
181 /**
182 * rb_ext_db_key_is_lookup:
183 * @key: a #RBExtDBKey
184 *
185 * Returns %TRUE if the key is a lookup key
186 *
187 * Return value: whether the key is a lookup key
188 */
189 gboolean
190 rb_ext_db_key_is_lookup (RBExtDBKey *key)
191 {
192 return key->lookup;
193 }
194
195 static void
196 add_to_list (GList **list, RBExtDBField **multi, const char *name, const char *value)
197 {
198 RBExtDBField *f;
199 GList *l;
200 int i;
201
202 for (l = *list; l != NULL; l = l->next) {
203 f = l->data;
204 if (strcmp (f->name, name) == 0) {
205 g_assert (multi != NULL);
206
207 if (value != NULL) {
208 for (i = 0; i < f->values->len; i++) {
209 if (strcmp (g_ptr_array_index (f->values, i), value) == 0) {
210 /* duplicate value */
211 return;
212 }
213 }
214 g_assert (*multi == NULL || *multi == f);
215 g_ptr_array_add (f->values, g_strdup (value));
216 *multi = f;
217 } else {
218 g_assert (*multi == NULL || *multi == f);
219 f->match_null = TRUE;
220 *multi = f;
221 }
222 return;
223 }
224 }
225
226 f = g_slice_new0 (RBExtDBField);
227 f->name = g_strdup (name);
228 f->values = g_ptr_array_new_with_free_func (g_free);
229 g_ptr_array_add (f->values, g_strdup (value));
230 *list = g_list_append (*list, f);
231 }
232
233 static char **
234 get_list_names (GList *list)
235 {
236 char **names;
237 GList *l;
238 int i;
239
240 names = g_new0 (char *, g_list_length (list) + 1);
241 i = 0;
242 for (l = list; l != NULL; l = l->next) {
243 RBExtDBField *f = l->data;
244 names[i++] = g_strdup (f->name);
245 }
246
247 return names;
248 }
249
250 static GPtrArray *
251 get_list_values (GList *list, const char *field)
252 {
253 RBExtDBField *f;
254 GList *l;
255
256 for (l = list; l != NULL; l = l->next) {
257 f = l->data;
258 if (strcmp (f->name, field) == 0) {
259 return f->values;
260 }
261 }
262
263 return NULL;
264 }
265
266
267 /**
268 * rb_ext_db_key_add_field:
269 * @key: a #RBExtDBKey
270 * @field: name of the field to add
271 * @value: field value
272 *
273 * Adds a field to the key, or an additional value to an existing field.
274 */
275 void
276 rb_ext_db_key_add_field (RBExtDBKey *key,
277 const char *field,
278 const char *value)
279 {
280 add_to_list (&key->fields, &key->multi_field, field, value);
281 }
282
283 /**
284 * rb_ext_db_key_get_field_names:
285 * @key: a #RBExtDBKey
286 *
287 * Returns a NULL-terminated array containing the names of the fields
288 * present in the key.
289 *
290 * Return value: (transfer full): array of field names
291 */
292 char **
293 rb_ext_db_key_get_field_names (RBExtDBKey *key)
294 {
295 return get_list_names (key->fields);
296 }
297
298 /**
299 * rb_ext_db_key_get_field:
300 * @key: a #RBExtDBKey
301 * @field: field to retrieve
302 *
303 * Extracts the value for a single-valued field.
304 *
305 * Return value: field value, or NULL
306 */
307 const char *
308 rb_ext_db_key_get_field (RBExtDBKey *key, const char *field)
309 {
310 GPtrArray *v = get_list_values (key->fields, field);
311 if (v != NULL && v->len > 0) {
312 return g_ptr_array_index (v, 0);
313 } else {
314 return NULL;
315 }
316 }
317
318
319 /**
320 * rb_ext_db_key_get_field_values:
321 * @key: a #RBExtDBKey
322 * @field: field to retrieve
323 *
324 * Extracts the values for the specified field.
325 *
326 * Return value: (transfer full): field values, or NULL
327 */
328 char **
329 rb_ext_db_key_get_field_values (RBExtDBKey *key, const char *field)
330 {
331 GPtrArray *v = get_list_values (key->fields, field);
332 char **strv;
333 int i;
334
335 if (v == NULL) {
336 return NULL;
337 }
338
339 strv = g_new0 (char *, v->len + 1);
340 for (i = 0; i < v->len; i++) {
341 strv[i] = g_strdup (g_ptr_array_index (v, i));
342 }
343 return strv;
344 }
345
346 /**
347 * rb_ext_db_key_add_info:
348 * @key: a #RBExtDBKey
349 * @name: name of the field to add
350 * @value: field value
351 *
352 * Adds an information field to the key.
353 */
354 void
355 rb_ext_db_key_add_info (RBExtDBKey *key,
356 const char *name,
357 const char *value)
358 {
359 add_to_list (&key->info, NULL, name, value);
360 }
361
362 /**
363 * rb_ext_db_key_get_info_names:
364 * @key: a #RBExtDBKey
365 *
366 * Returns a NULL-terminated array containing the names of the info
367 * fields * present in the key.
368 *
369 * Return value: (transfer full): array of info field names
370 */
371 char **
372 rb_ext_db_key_get_info_names (RBExtDBKey *key)
373 {
374 return get_list_names (key->info);
375 }
376
377 /**
378 * rb_ext_db_key_get_info:
379 * @key: a #RBExtDBKey
380 * @name: info field to retrieve
381 *
382 * Extracts the value for the specified info field.
383 *
384 * Return value: field value, or NULL
385 */
386 const char *
387 rb_ext_db_key_get_info (RBExtDBKey *key, const char *name)
388 {
389 GPtrArray *v = get_list_values (key->info, name);
390 if (v != NULL && v->len > 0) {
391 return g_ptr_array_index (v, 0);
392 } else {
393 return NULL;
394 }
395 }
396
397
398
399
400 static gboolean
401 match_field (RBExtDBKey *key, RBExtDBField *field)
402 {
403 GPtrArray *values;
404 int i;
405 int j;
406
407 values = get_list_values (key->fields, field->name);
408 if (values == NULL) {
409 return field->match_null;
410 }
411
412 for (i = 0; i < field->values->len; i++) {
413 const char *a = g_ptr_array_index (field->values, i);
414 for (j = 0; j < values->len; j++) {
415 const char *b = g_ptr_array_index (values, j);
416 if (strcmp (a, b) == 0)
417 return TRUE;
418 }
419 }
420 return FALSE;
421 }
422
423 /**
424 * rb_ext_db_key_matches:
425 * @a: first #RBExtDBKey
426 * @b: second #RBExtDBKey
427 *
428 * Checks whether the fields specified in @a match @b.
429 * For keys to match, they must have the same set of required fields,
430 * and the values for all must match. Optional fields must have the
431 * same values if present in both. Informational fields are ignored.
432 *
433 * Return value: %TRUE if the keys match
434 */
435 gboolean
436 rb_ext_db_key_matches (RBExtDBKey *a, RBExtDBKey *b)
437 {
438 GList *l;
439
440 for (l = a->fields; l != NULL; l = l->next) {
441 RBExtDBField *f = l->data;
442 if (match_field (b, f) == FALSE) {
443 return FALSE;
444 }
445 }
446
447 for (l = b->fields; l != NULL; l = l->next) {
448 RBExtDBField *f = l->data;
449 if (match_field (a, f) == FALSE) {
450 return FALSE;
451 }
452 }
453
454 return TRUE;
455 }
456
457 /**
458 * rb_ext_db_key_field_matches:
459 * @key: an #RBExtDBKey
460 * @field: a field to check
461 * @value: a value to match against
462 *
463 * Checks whether a specified field in @key matches a value.
464 * This can be used to match keys against other types of data.
465 * To match keys against each other, use @rb_ext_db_key_matches.
466 *
467 * Return value: %TRUE if the field matches the value
468 */
469 gboolean
470 rb_ext_db_key_field_matches (RBExtDBKey *key, const char *field, const char *value)
471 {
472 GPtrArray *v;
473 int i;
474
475 v = get_list_values (key->fields, field);
476 if (v == NULL) {
477 /* if the key doesn't have this field, anything matches */
478 return TRUE;
479 }
480
481 if (value == NULL) {
482 if (key->multi_field == NULL) {
483 /* no multi field, so null can't match */
484 return FALSE;
485 }
486
487 if (g_strcmp0 (field, key->multi_field->name) == 0) {
488 /* this is the multi field, so null might match */
489 return key->multi_field->match_null;
490 } else {
491 /* this isn't the multi field, null can't match */
492 return FALSE;
493 }
494 }
495
496 for (i = 0; i < v->len; i++) {
497 if (strcmp (g_ptr_array_index (v, i), value) == 0) {
498 return TRUE;
499 }
500 }
501
502 return FALSE;
503 }
504
505 static gboolean
506 create_store_key (RBExtDBKey *key, int option, TDB_DATA *data)
507 {
508 GByteArray *k;
509 GList *l;
510 guint8 nul = '\0';
511
512 if (key->multi_field != NULL &&
513 option > key->multi_field->values->len &&
514 key->multi_field->match_null == FALSE) {
515 return FALSE;
516 } else if (key->multi_field == NULL && option != 0) {
517 return FALSE;
518 }
519
520 k = g_byte_array_sized_new (512);
521 for (l = key->fields; l != NULL; l = l->next) {
522 RBExtDBField *f = l->data;
523 const char *value;
524
525 if (f != key->multi_field) {
526 value = g_ptr_array_index (f->values, 0);
527 } else if (option < f->values->len) {
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
528 value = g_ptr_array_index (f->values, option);
529 } else {
530 continue;
531 }
532 g_byte_array_append (k, (guint8 *)f->name, strlen (f->name));
533 g_byte_array_append (k, &nul, 1);
534 g_byte_array_append (k, (guint8 *)value, strlen (value));
535 g_byte_array_append (k, &nul, 1);
536 }
537
538 data->dsize = k->len;
539 data->dptr = g_byte_array_free (k, FALSE);
540 return TRUE;
541 }
542
543 /**
544 * rb_ext_db_key_lookups: (skip):
545 * @key: a #RBExtDBKey
546 * @callback: a callback to process lookup keys
547 * @user_data: data to pass to @callback
548 *
549 * Generates the set of possible lookup keys for @key and
550 * passes them to @callback in order. If the callback returns
551 * %FALSE, processing will stop.
552 *
553 * This should only be used by the metadata store itself.
554 * Metadata providers and consumers shouldn't need to do this.
555 */
556 void
557 rb_ext_db_key_lookups (RBExtDBKey *key,
558 RBExtDBKeyLookupCallback callback,
559 gpointer user_data)
560 {
561 int i = 0;
562 while (TRUE) {
563 TDB_DATA sk;
564 gboolean result;
565
566 if (create_store_key (key, i, &sk) == FALSE)
567 break;
568
569 result = callback (sk, user_data);
570 g_free (sk.dptr);
571
572 if (result == FALSE)
573 break;
574
575 i++;
576 }
577 }
578
579 /**
580 * rb_ext_db_key_to_store_key: (skip):
581 * @key: a @RBExtDBKey
582 *
583 * Generates the storage key for @key. This is the value that should
584 * be used to store an item identified by this key in the store.
585 * The storage key includes all optional fields, so keys passed to
586 * this function should be constructed using only the optional fields
587 * that were used to locate the item. The caller must free the data
588 * pointer inside @data.
589 *
590 * This should only be used by the metadata store itself.
591 * Metadata providers and consumers shouldn't need to do this.
592 *
593 * Return value: TDB_DATA structure containing storage key
594 */
595 TDB_DATA
596 rb_ext_db_key_to_store_key (RBExtDBKey *key)
597 {
598 TDB_DATA k = {0,};
599 create_store_key (key, 0, &k);
600 return k;
601 }