hythmbox-2.98/metadata/rb-ext-db-key.c

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) {
Access to field 'values' results in a dereference of a null pointer (loaded from variable 'f')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'values' results in a dereference of a null pointer (loaded from variable 'f')
(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 }