tracker-0.16.2/src/tracker-extract/tracker-media-art.c

Location Tool Test ID Function Issue
tracker-media-art.c:174:4 gcc pointer-sign file_get_checksum_if_exists pointer targets in passing argument 4 of 'g_input_stream_read_all' differ in signedness

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found tracker-media-art.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the
  16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17  * Boston, MA  02110-1301, USA.
  18  */
  19 
  20 #include "config.h"
  21 
  22 #include <string.h>
  23 #include <stdlib.h>
  24 #include <sys/types.h>
  25 #include <sys/stat.h>
  26 #include <unistd.h>
  27 #include <ctype.h>
  28 #include <sys/types.h>
  29 #include <utime.h>
  30 #include <time.h>
  31 #include <errno.h>
  32 
  33 #include <glib.h>
  34 #include <glib/gprintf.h>
  35 #include <glib/gstdio.h>
  36 #include <gio/gio.h>
  37 
  38 #include <libtracker-miner/tracker-miner.h>
  39 #include <libtracker-common/tracker-file-utils.h>
  40 #include <libtracker-common/tracker-date-time.h>
  41 #include <libtracker-common/tracker-media-art.h>
  42 
  43 #include "tracker-media-art.h"
  44 #include "tracker-extract.h"
  45 #include "tracker-marshal.h"
  46 #include "tracker-media-art-generic.h"
  47 
  48 #define ALBUMARTER_SERVICE    "com.nokia.albumart"
  49 #define ALBUMARTER_PATH       "/com/nokia/albumart/Requester"
  50 #define ALBUMARTER_INTERFACE  "com.nokia.albumart.Requester"
  51 
  52 static const gchar *media_art_type_name[TRACKER_MEDIA_ART_TYPE_COUNT] = {
  53 	"invalid",
  54 	"album",
  55 	"video"
  56 };
  57 
  58 typedef struct {
  59 	TrackerStorage *storage;
  60 	gchar *art_path;
  61 	gchar *local_uri;
  62 } GetFileInfo;
  63 
  64 typedef struct {
  65 	gchar *uri;
  66 	TrackerMediaArtType type;
  67 	gchar *artist_strdown;
  68 	gchar *title_strdown;
  69 } TrackerMediaArtSearch;
  70 
  71 typedef enum {
  72 	IMAGE_MATCH_EXACT = 0,
  73 	IMAGE_MATCH_EXACT_SMALL = 1,
  74 	IMAGE_MATCH_SAME_DIRECTORY = 2,
  75 	IMAGE_MATCH_TYPE_COUNT
  76 } ImageMatchType;
  77 
  78 static gboolean initialized = FALSE;
  79 static gboolean disable_requests;
  80 static TrackerStorage *media_art_storage;
  81 static GHashTable *media_art_cache;
  82 static GDBusConnection *connection;
  83 
  84 static void
  85 media_art_queue_cb (GObject      *source_object,
  86                    GAsyncResult *res,
  87                    gpointer      user_data);
  88 
  89 
  90 static GDir *
  91 get_parent_g_dir (const gchar  *uri,
  92                   gchar       **dirname,
  93                   GError      **error)
  94 {
  95 	GFile *file, *dirf;
  96 	GDir *dir;
  97 
  98 	g_return_val_if_fail (dirname != NULL, NULL);
  99 
 100 	*dirname = NULL;
 101 
 102 	file = g_file_new_for_uri (uri);
 103 	dirf = g_file_get_parent (file);
 104 	if (dirf) {
 105 		*dirname = g_file_get_path (dirf);
 106 		g_object_unref (dirf);
 107 	}
 108 	g_object_unref (file);
 109 
 110 	if (*dirname == NULL) {
 111 		*error = g_error_new (G_FILE_ERROR,
 112 		                      G_FILE_ERROR_EXIST,
 113 		                      "No parent directory found for '%s'",
 114 		                      uri);
 115 		return NULL;
 116 	}
 117 
 118 	dir = g_dir_open (*dirname, 0, error);
 119 
 120 	return dir;
 121 }
 122 
 123 
 124 static gchar *
 125 checksum_for_data (GChecksumType  checksum_type,
 126                    const guchar  *data,
 127                    gsize          length)
 128 {
 129 	GChecksum *checksum;
 130 	gchar *retval;
 131 
 132 	checksum = g_checksum_new (checksum_type);
 133 	if (!checksum) {
 134 		return NULL;
 135 	}
 136 
 137 	g_checksum_update (checksum, data, length);
 138 	retval = g_strdup (g_checksum_get_string (checksum));
 139 	g_checksum_free (checksum);
 140 
 141 	return retval;
 142 }
 143 
 144 static gboolean
 145 file_get_checksum_if_exists (GChecksumType   checksum_type,
 146                              const gchar    *path,
 147                              gchar         **md5,
 148                              gboolean        check_jpeg,
 149                              gboolean       *is_jpeg)
 150 {
 151 	GFile *file = g_file_new_for_path (path);
 152 	GFileInputStream *stream;
 153 	GChecksum *checksum;
 154 	gboolean retval;
 155 
 156 	checksum = g_checksum_new (checksum_type);
 157 
 158 	if (!checksum) {
 159 		g_debug ("Can't create checksum engine");
 160 		g_object_unref (file);
 161 		return FALSE;
 162 	}
 163 
 164 	stream = g_file_read (file, NULL, NULL);
 165 
 166 	if (stream) {
 167 		gssize rsize;
 168 		guchar buffer[1024];
 169 
 170 		/* File exists & readable always means true retval */
 171 		retval = TRUE;
 172 
 173 		if (check_jpeg) {
 174 			if (g_input_stream_read_all (G_INPUT_STREAM (stream), buffer, 3, &rsize, NULL, NULL)) {
pointer targets in passing argument 4 of 'g_input_stream_read_all' differ in signedness
(emitted by gcc)
175 if (rsize >= 3 && buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff) { 176 if (is_jpeg) { 177 *is_jpeg = TRUE; 178 } 179 /* Add the read bytes to the checksum */ 180 g_checksum_update (checksum, buffer, rsize); 181 } else { 182 /* Larger than 3 bytes but incorrect jpeg header */ 183 if (is_jpeg) { 184 *is_jpeg = FALSE; 185 } 186 goto end; 187 } 188 } else { 189 /* Smaller than 3 bytes, not a jpeg */ 190 if (is_jpeg) { 191 *is_jpeg = FALSE; 192 } 193 goto end; 194 } 195 } 196 197 while ((rsize = g_input_stream_read (G_INPUT_STREAM (stream), buffer, 1024, NULL, NULL)) > 0) { 198 g_checksum_update (checksum, buffer, rsize); 199 } 200 201 if (md5) { 202 *md5 = g_strdup (g_checksum_get_string (checksum)); 203 } 204 205 } else { 206 g_debug ("%s isn't readable while calculating MD5 checksum", path); 207 /* File doesn't exist or isn't readable */ 208 retval = FALSE; 209 } 210 211 end: 212 213 if (stream) { 214 g_object_unref (stream); 215 } 216 g_checksum_free (checksum); 217 g_object_unref (file); 218 219 return retval; 220 } 221 222 static gboolean 223 convert_from_other_format (const gchar *found, 224 const gchar *target, 225 const gchar *album_path, 226 const gchar *artist) 227 { 228 gboolean retval; 229 gchar *sum1 = NULL; 230 gchar *target_temp; 231 232 target_temp = g_strdup_printf ("%s-tmp", target); 233 234 retval = tracker_media_art_file_to_jpeg (found, target_temp); 235 236 if (retval && (artist == NULL || g_strcmp0 (artist, " ") == 0)) { 237 if (g_rename (target_temp, album_path) == -1) { 238 g_debug ("rename(%s, %s) error: %s", target_temp, album_path, g_strerror (errno)); 239 } 240 } else if (retval && file_get_checksum_if_exists (G_CHECKSUM_MD5, target_temp, &sum1, FALSE, NULL)) { 241 gchar *sum2 = NULL; 242 if (file_get_checksum_if_exists (G_CHECKSUM_MD5, album_path, &sum2, FALSE, NULL)) { 243 if (g_strcmp0 (sum1, sum2) == 0) { 244 245 /* If album-space-md5.jpg is the same as found, 246 * make a symlink */ 247 248 if (symlink (album_path, target) != 0) { 249 g_debug ("symlink(%s, %s) error: %s", album_path, target, g_strerror (errno)); 250 retval = FALSE; 251 } else { 252 retval = TRUE; 253 } 254 255 g_unlink (target_temp); 256 257 } else { 258 259 /* If album-space-md5.jpg isn't the same as found, 260 * make a new album-md5-md5.jpg (found -> target) */ 261 262 if (g_rename (target_temp, album_path) == -1) { 263 g_debug ("rename(%s, %s) error: %s", target_temp, album_path, g_strerror (errno)); 264 } 265 } 266 g_free (sum2); 267 } else { 268 269 /* If there's not yet a album-space-md5.jpg, make one, 270 * and symlink album-md5-md5.jpg to it */ 271 272 g_rename (target_temp, album_path); 273 274 if (symlink (album_path, target) != 0) { 275 g_debug ("symlink(%s,%s) error: %s", album_path, target, g_strerror (errno)); 276 retval = FALSE; 277 } else { 278 retval = TRUE; 279 } 280 281 } 282 283 g_free (sum1); 284 } else if (retval) { 285 g_debug ("Can't read %s while calculating checksum", target_temp); 286 /* Can't read the file that it was converted to, strange ... */ 287 g_unlink (target_temp); 288 } 289 290 g_free (target_temp); 291 292 return retval; 293 } 294 295 static TrackerMediaArtSearch * 296 tracker_media_art_search_new (const gchar *uri, 297 TrackerMediaArtType type, 298 const gchar *artist, 299 const gchar *title) 300 { 301 TrackerMediaArtSearch *search; 302 gchar *temp; 303 304 search = g_slice_new0 (TrackerMediaArtSearch); 305 search->uri = g_strdup (uri); 306 search->type = type; 307 308 if (artist) { 309 temp = tracker_media_art_strip_invalid_entities (artist); 310 search->artist_strdown = g_utf8_strdown (temp, -1); 311 g_free (temp); 312 } 313 314 temp = tracker_media_art_strip_invalid_entities (title); 315 search->title_strdown = g_utf8_strdown (temp, -1); 316 g_free (temp); 317 318 return search; 319 } 320 321 static void 322 tracker_media_art_search_free (TrackerMediaArtSearch *search) 323 { 324 g_free (search->uri); 325 g_free (search->artist_strdown); 326 g_free (search->title_strdown); 327 328 g_slice_free (TrackerMediaArtSearch, search); 329 } 330 331 static ImageMatchType 332 classify_image_file (TrackerMediaArtSearch *search, 333 const gchar *file_name_strdown) 334 { 335 if ((search->artist_strdown && search->artist_strdown[0] != '\0' && 336 strstr (file_name_strdown, search->artist_strdown)) || 337 (search->title_strdown && search->title_strdown[0] != '\0' && 338 strstr (file_name_strdown, search->title_strdown))) { 339 return IMAGE_MATCH_EXACT; 340 } 341 342 if (search->type == TRACKER_MEDIA_ART_ALBUM) { 343 /* Accept cover, front, folder, AlbumArt_{GUID}_Large (first choice) 344 * second choice is AlbumArt_{GUID}_Small and AlbumArtSmall. We 345 * don't support just AlbumArt. (it must have a Small or Large) */ 346 347 if (strstr (file_name_strdown, "cover") || 348 strstr (file_name_strdown, "front") || 349 strstr (file_name_strdown, "folder")) { 350 return IMAGE_MATCH_EXACT; 351 } 352 353 if (strstr (file_name_strdown, "albumart")) { 354 if (strstr (file_name_strdown, "large")) { 355 return IMAGE_MATCH_EXACT; 356 } else if (strstr (file_name_strdown, "small")) { 357 return IMAGE_MATCH_EXACT_SMALL; 358 } 359 } 360 } 361 362 if (search->type == TRACKER_MEDIA_ART_VIDEO) { 363 if (strstr (file_name_strdown, "folder") || 364 strstr (file_name_strdown, "poster")) { 365 return IMAGE_MATCH_EXACT; 366 } 367 } 368 369 /* Lowest priority for other images, but we still might use it for videos */ 370 return IMAGE_MATCH_SAME_DIRECTORY; 371 } 372 373 static gchar * 374 tracker_media_art_find_by_artist_and_title (const gchar *uri, 375 TrackerMediaArtType type, 376 const gchar *artist, 377 const gchar *title) 378 { 379 TrackerMediaArtSearch *search; 380 GDir *dir; 381 GError *error = NULL; 382 gchar *dirname = NULL; 383 const gchar *name; 384 gchar *name_utf8, *name_strdown; 385 guint i; 386 gchar *art_file_name; 387 gchar *art_file_path; 388 gint priority; 389 390 GList *image_list[IMAGE_MATCH_TYPE_COUNT] = { NULL, }; 391 392 g_return_val_if_fail (type > TRACKER_MEDIA_ART_NONE && type < TRACKER_MEDIA_ART_TYPE_COUNT, FALSE); 393 g_return_val_if_fail (title != NULL, FALSE); 394 395 dir = get_parent_g_dir (uri, &dirname, &error); 396 397 if (!dir) { 398 g_debug ("Media art directory could not be opened: %s", 399 error ? error->message : "no error given"); 400 401 g_clear_error (&error); 402 g_free (dirname); 403 404 return NULL; 405 } 406 407 /* First, classify each file in the directory as either an image, relevant 408 * to the media object in question, or irrelevant. We use this information 409 * to decide if the image is a cover or if the file is in a random directory. 410 */ 411 412 search = tracker_media_art_search_new (uri, type, artist, title); 413 414 for (name = g_dir_read_name (dir); 415 name != NULL; 416 name = g_dir_read_name (dir)) { 417 418 name_utf8 = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); 419 420 if (!name_utf8) { 421 g_debug ("Could not convert filename '%s' to UTF-8", name); 422 continue; 423 } 424 425 name_strdown = g_utf8_strdown (name_utf8, -1); 426 427 if (g_str_has_suffix (name_strdown, "jpeg") || 428 g_str_has_suffix (name_strdown, "jpg") || 429 g_str_has_suffix (name_strdown, "png")) { 430 431 priority = classify_image_file (search, name_strdown); 432 image_list[priority] = g_list_prepend (image_list[priority], name_strdown); 433 } else { 434 g_free (name_strdown); 435 } 436 437 g_free (name_utf8); 438 } 439 440 /* Use the results to pick a media art image */ 441 442 art_file_name = NULL; 443 art_file_path = NULL; 444 445 if (g_list_length (image_list[IMAGE_MATCH_EXACT]) > 0) { 446 art_file_name = g_strdup (image_list[IMAGE_MATCH_EXACT]->data); 447 } else if (g_list_length (image_list[IMAGE_MATCH_EXACT_SMALL]) > 0) { 448 art_file_name = g_strdup (image_list[IMAGE_MATCH_EXACT_SMALL]->data); 449 } else { 450 if (type == TRACKER_MEDIA_ART_VIDEO && g_list_length (image_list[IMAGE_MATCH_SAME_DIRECTORY]) == 1) { 451 art_file_name = g_strdup (image_list[IMAGE_MATCH_SAME_DIRECTORY]->data); 452 } 453 } 454 455 for (i = 0; i < IMAGE_MATCH_TYPE_COUNT; i ++) { 456 g_list_foreach (image_list[i], (GFunc)g_free, NULL); 457 g_list_free (image_list[i]); 458 } 459 460 if (art_file_name) { 461 art_file_path = g_build_filename (dirname, art_file_name, NULL); 462 g_free (art_file_name); 463 } else { 464 g_debug ("Album art NOT found in same directory"); 465 art_file_path = NULL; 466 } 467 468 tracker_media_art_search_free (search); 469 g_dir_close (dir); 470 g_free (dirname); 471 472 return art_file_path; 473 } 474 475 static gboolean 476 media_art_heuristic (const gchar *artist, 477 const gchar *title, 478 TrackerMediaArtType type, 479 const gchar *filename_uri, 480 const gchar *local_uri) 481 { 482 gchar *art_file_path = NULL; 483 gchar *album_art_file_path = NULL; 484 gchar *target = NULL; 485 gchar *artist_stripped = NULL; 486 gchar *title_stripped = NULL; 487 gboolean retval = FALSE; 488 489 if (title == NULL || title[0] == '\0') { 490 g_debug ("Unable to fetch media art, no title specified"); 491 return FALSE; 492 } 493 494 if (artist) { 495 artist_stripped = tracker_media_art_strip_invalid_entities (artist); 496 } 497 title_stripped = tracker_media_art_strip_invalid_entities (title); 498 499 tracker_media_art_get_path (artist_stripped, 500 title_stripped, 501 media_art_type_name[type], 502 NULL, 503 &target, 504 NULL); 505 506 /* Copy from local album art (.mediaartlocal) to spec */ 507 if (local_uri) { 508 GFile *local_file, *file; 509 510 local_file = g_file_new_for_uri (local_uri); 511 512 if (g_file_query_exists (local_file, NULL)) { 513 g_debug ("Album art being copied from local (.mediaartlocal) file:'%s'", 514 local_uri); 515 516 file = g_file_new_for_path (target); 517 518 g_file_copy_async (local_file, file, 0, 0, 519 NULL, NULL, NULL, NULL, NULL); 520 521 g_object_unref (file); 522 g_object_unref (local_file); 523 524 g_free (target); 525 g_free (artist_stripped); 526 g_free (title_stripped); 527 528 return TRUE; 529 } 530 531 g_object_unref (local_file); 532 } 533 534 art_file_path = tracker_media_art_find_by_artist_and_title (filename_uri, type, artist, title); 535 536 if (art_file_path != NULL) { 537 if (g_str_has_suffix (art_file_path, "jpeg") || 538 g_str_has_suffix (art_file_path, "jpg")) { 539 540 gboolean is_jpeg = FALSE; 541 gchar *sum1 = NULL; 542 543 if (type != TRACKER_MEDIA_ART_ALBUM || (artist == NULL || g_strcmp0 (artist, " ") == 0)) { 544 GFile *art_file; 545 GFile *target_file; 546 GError *err = NULL; 547 548 g_debug ("Album art (JPEG) found in same directory being used:'%s'", art_file_path); 549 550 target_file = g_file_new_for_path (target); 551 art_file = g_file_new_for_path (art_file_path); 552 553 g_file_copy (art_file, target_file, 0, NULL, NULL, NULL, &err); 554 if (err) { 555 g_debug ("%s", err->message); 556 g_clear_error (&err); 557 } 558 g_object_unref (art_file); 559 g_object_unref (target_file); 560 } else if (file_get_checksum_if_exists (G_CHECKSUM_MD5, art_file_path, &sum1, TRUE, &is_jpeg)) { 561 /* Avoid duplicate artwork for each track in an album */ 562 tracker_media_art_get_path (NULL, 563 title_stripped, 564 media_art_type_name [type], 565 NULL, 566 &album_art_file_path, 567 NULL); 568 569 if (is_jpeg) { 570 gchar *sum2 = NULL; 571 572 g_debug ("Album art (JPEG) found in same directory being used:'%s'", art_file_path); 573 574 if (file_get_checksum_if_exists (G_CHECKSUM_MD5, album_art_file_path, &sum2, FALSE, NULL)) { 575 if (g_strcmp0 (sum1, sum2) == 0) { 576 /* If album-space-md5.jpg is the same as found, 577 * make a symlink */ 578 579 if (symlink (album_art_file_path, target) != 0) { 580 g_debug ("symlink(%s, %s) error: %s", album_art_file_path, target, g_strerror (errno)); 581 retval = FALSE; 582 } else { 583 retval = TRUE; 584 } 585 } else { 586 GFile *art_file; 587 GFile *target_file; 588 GError *err = NULL; 589 590 /* If album-space-md5.jpg isn't the same as found, 591 * make a new album-md5-md5.jpg (found -> target) */ 592 593 target_file = g_file_new_for_path (target); 594 art_file = g_file_new_for_path (art_file_path); 595 retval = g_file_copy (art_file, target_file, 0, NULL, NULL, NULL, &err); 596 if (err) { 597 g_debug ("%s", err->message); 598 g_clear_error (&err); 599 } 600 g_object_unref (art_file); 601 g_object_unref (target_file); 602 } 603 g_free (sum2); 604 } else { 605 GFile *art_file; 606 GFile *album_art_file; 607 GError *err = NULL; 608 609 /* If there's not yet a album-space-md5.jpg, make one, 610 * and symlink album-md5-md5.jpg to it */ 611 612 album_art_file = g_file_new_for_path (album_art_file_path); 613 art_file = g_file_new_for_path (art_file_path); 614 retval = g_file_copy (art_file, album_art_file, 0, NULL, NULL, NULL, &err); 615 616 if (err == NULL) { 617 if (symlink (album_art_file_path, target) != 0) { 618 g_debug ("symlink(%s, %s) error: %s", album_art_file_path, target, g_strerror (errno)); 619 retval = FALSE; 620 } else { 621 retval = TRUE; 622 } 623 } else { 624 g_debug ("%s", err->message); 625 g_clear_error (&err); 626 retval = FALSE; 627 } 628 629 g_object_unref (album_art_file); 630 g_object_unref (art_file); 631 } 632 } else { 633 g_debug ("Album art found in same directory but not a real JPEG file (trying to convert): '%s'", art_file_path); 634 retval = convert_from_other_format (art_file_path, target, album_art_file_path, artist); 635 } 636 637 g_free (sum1); 638 } else { 639 /* Can't read contents of the cover.jpg file ... */ 640 retval = FALSE; 641 } 642 } else if (g_str_has_suffix (art_file_path, "png")) { 643 if (!album_art_file_path) { 644 tracker_media_art_get_path (NULL, 645 title_stripped, 646 media_art_type_name[type], 647 NULL, 648 &album_art_file_path, 649 NULL); 650 } 651 652 g_debug ("Album art (PNG) found in same directory being used:'%s'", art_file_path); 653 retval = convert_from_other_format (art_file_path, target, album_art_file_path, artist); 654 } 655 656 g_free (art_file_path); 657 g_free (album_art_file_path); 658 } 659 660 g_free (target); 661 g_free (artist_stripped); 662 g_free (title_stripped); 663 664 return retval; 665 } 666 667 static gboolean 668 media_art_set (const unsigned char *buffer, 669 size_t len, 670 const gchar *mime, 671 TrackerMediaArtType type, 672 const gchar *artist, 673 const gchar *title, 674 const gchar *uri) 675 { 676 gchar *local_path; 677 gboolean retval = FALSE; 678 679 g_return_val_if_fail (type > TRACKER_MEDIA_ART_NONE && type < TRACKER_MEDIA_ART_TYPE_COUNT, FALSE); 680 681 if (!artist && !title) { 682 g_warning ("Could not save embedded album art, not enough metadata supplied"); 683 return FALSE; 684 } 685 686 tracker_media_art_get_path (artist, title, media_art_type_name[type], NULL, &local_path, NULL); 687 688 if (type != TRACKER_MEDIA_ART_ALBUM || (artist == NULL || g_strcmp0 (artist, " ") == 0)) { 689 retval = tracker_media_art_buffer_to_jpeg (buffer, len, mime, local_path); 690 } else { 691 gchar *album_path; 692 693 tracker_media_art_get_path (NULL, title, media_art_type_name[type], NULL, &album_path, NULL); 694 695 if (!g_file_test (album_path, G_FILE_TEST_EXISTS)) { 696 retval = tracker_media_art_buffer_to_jpeg (buffer, len, mime, album_path); 697 698 /* If album-space-md5.jpg doesn't exist, make one and make a symlink 699 * to album-md5-md5.jpg */ 700 701 if (retval && symlink (album_path, local_path) != 0) { 702 g_debug ("symlink(%s, %s) error: %s", album_path, local_path, g_strerror (errno)); 703 retval = FALSE; 704 } else { 705 retval = TRUE; 706 } 707 } else { 708 gchar *sum2 = NULL; 709 710 if (file_get_checksum_if_exists (G_CHECKSUM_MD5, album_path, &sum2, FALSE, NULL)) { 711 if ( !(g_strcmp0 (mime, "image/jpeg") == 0 || g_strcmp0 (mime, "JPG") == 0) || 712 ( !(len > 2 && buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff) )) { 713 gchar *sum1 = NULL; 714 gchar *temp = g_strdup_printf ("%s-tmp", album_path); 715 716 /* If buffer isn't a JPEG */ 717 718 retval = tracker_media_art_buffer_to_jpeg (buffer, len, mime, temp); 719 720 if (retval && file_get_checksum_if_exists (G_CHECKSUM_MD5, temp, &sum1, FALSE, NULL)) { 721 if (g_strcmp0 (sum1, sum2) == 0) { 722 723 /* If album-space-md5.jpg is the same as buffer, make a symlink 724 * to album-md5-md5.jpg */ 725 726 g_unlink (temp); 727 if (symlink (album_path, local_path) != 0) { 728 g_debug ("symlink(%s, %s) error: %s", album_path, local_path, g_strerror (errno)); 729 retval = FALSE; 730 } else { 731 retval = TRUE; 732 } 733 } else { 734 /* If album-space-md5.jpg isn't the same as buffer, make a 735 * new album-md5-md5.jpg */ 736 if (g_rename (temp, local_path) == -1) { 737 g_debug ("rename(%s, %s) error: %s", temp, local_path, g_strerror (errno)); 738 } 739 } 740 g_free (sum1); 741 } else { 742 /* Can't read temp file ... */ 743 g_unlink (temp); 744 } 745 746 g_free (temp); 747 } else { 748 gchar *sum1 = NULL; 749 750 sum1 = checksum_for_data (G_CHECKSUM_MD5, buffer, len); 751 /* If album-space-md5.jpg is the same as buffer, make a symlink 752 * to album-md5-md5.jpg */ 753 754 if (g_strcmp0 (sum1, sum2) == 0) { 755 if (symlink (album_path, local_path) != 0) { 756 g_debug ("symlink(%s, %s) error: %s", album_path, local_path, g_strerror (errno)); 757 retval = FALSE; 758 } else { 759 retval = TRUE; 760 } 761 } else { 762 /* If album-space-md5.jpg isn't the same as buffer, make a 763 * new album-md5-md5.jpg */ 764 retval = tracker_media_art_buffer_to_jpeg (buffer, len, mime, local_path); 765 } 766 g_free (sum1); 767 } 768 g_free (sum2); 769 } 770 g_free (album_path); 771 } 772 } 773 774 g_free (local_path); 775 776 return retval; 777 } 778 779 static void 780 media_art_request_download (TrackerStorage *storage, 781 TrackerMediaArtType type, 782 const gchar *album, 783 const gchar *artist, 784 const gchar *local_uri, 785 const gchar *art_path) 786 { 787 if (connection) { 788 GetFileInfo *info; 789 790 if (disable_requests) { 791 return; 792 } 793 794 if (type != TRACKER_MEDIA_ART_ALBUM) { 795 return; 796 } 797 798 info = g_slice_new (GetFileInfo); 799 800 info->storage = storage ? g_object_ref (storage) : NULL; 801 802 info->local_uri = g_strdup (local_uri); 803 info->art_path = g_strdup (art_path); 804 805 g_dbus_connection_call (connection, 806 ALBUMARTER_SERVICE, 807 ALBUMARTER_PATH, 808 ALBUMARTER_INTERFACE, 809 "Queue", 810 g_variant_new ("(sssu)", 811 artist ? artist : "", 812 album ? album : "", 813 "album", 814 0), 815 NULL, 816 G_DBUS_CALL_FLAGS_NONE, 817 -1, 818 NULL, 819 media_art_queue_cb, 820 info); 821 } 822 } 823 824 static void 825 media_art_copy_to_local (TrackerStorage *storage, 826 const gchar *filename, 827 const gchar *local_uri) 828 { 829 GSList *roots, *l; 830 gboolean on_removable_device = FALSE; 831 guint flen; 832 833 /* Determining if we are on a removable device */ 834 if (!storage) { 835 /* This is usually because we are running on the 836 * command line, so we don't error here with 837 * g_return_if_fail(). 838 */ 839 return; 840 } 841 842 roots = tracker_storage_get_device_roots (storage, TRACKER_STORAGE_REMOVABLE, FALSE); 843 flen = strlen (filename); 844 845 for (l = roots; l; l = l->next) { 846 guint len; 847 848 len = strlen (l->data); 849 850 if (flen >= len && strncmp (filename, l->data, len)) { 851 on_removable_device = TRUE; 852 break; 853 } 854 } 855 856 g_slist_foreach (roots, (GFunc) g_free, NULL); 857 g_slist_free (roots); 858 859 if (on_removable_device) { 860 GFile *local_file, *from; 861 862 from = g_file_new_for_path (filename); 863 local_file = g_file_new_for_uri (local_uri); 864 865 /* We don't try to overwrite, but we also ignore all errors. 866 * Such an error could be that the removable device is 867 * read-only. Well that's fine then ... ignore */ 868 869 if (!g_file_query_exists (local_file, NULL)) { 870 GFile *dirf; 871 872 dirf = g_file_get_parent (local_file); 873 if (dirf) { 874 /* Parent file may not exist, as if the file is in the 875 * root of a gvfs mount. In this case we won't try to 876 * create the parent directory, just try to copy the 877 * file there. */ 878 g_file_make_directory_with_parents (dirf, NULL, NULL); 879 g_object_unref (dirf); 880 } 881 882 g_debug ("Copying media art from:'%s' to:'%s'", 883 filename, local_uri); 884 885 g_file_copy_async (from, local_file, 0, 0, 886 NULL, NULL, NULL, NULL, NULL); 887 } 888 889 g_object_unref (local_file); 890 g_object_unref (from); 891 } 892 } 893 894 static void 895 media_art_queue_cb (GObject *source_object, 896 GAsyncResult *res, 897 gpointer user_data) 898 { 899 GError *error = NULL; 900 GetFileInfo *info; 901 GVariant *v; 902 903 info = user_data; 904 905 v = g_dbus_connection_call_finish ((GDBusConnection *) source_object, res, &error); 906 907 if (error) { 908 if (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) { 909 disable_requests = TRUE; 910 } else { 911 g_warning ("%s", error->message); 912 } 913 g_clear_error (&error); 914 } 915 916 if (v) { 917 g_variant_unref (v); 918 } 919 920 if (info->storage && info->art_path && 921 g_file_test (info->art_path, G_FILE_TEST_EXISTS)) { 922 923 media_art_copy_to_local (info->storage, 924 info->art_path, 925 info->local_uri); 926 } 927 928 g_free (info->art_path); 929 g_free (info->local_uri); 930 931 if (info->storage) { 932 g_object_unref (info->storage); 933 } 934 935 g_slice_free (GetFileInfo, info); 936 } 937 938 gboolean 939 tracker_media_art_init (void) 940 { 941 GError *error = NULL; 942 943 g_return_val_if_fail (initialized == FALSE, FALSE); 944 945 tracker_media_art_plugin_init (); 946 947 media_art_storage = tracker_storage_new (); 948 949 /* Cache to know if we have already handled uris */ 950 media_art_cache = g_hash_table_new_full (g_str_hash, 951 g_str_equal, 952 (GDestroyNotify) g_free, 953 NULL); 954 955 /* Signal handler for new album art from the extractor */ 956 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); 957 958 if (!connection) { 959 g_critical ("Could not connect to the D-Bus session bus, %s", 960 error ? error->message : "no error given."); 961 g_clear_error (&error); 962 return FALSE; 963 } 964 965 initialized = TRUE; 966 967 return TRUE; 968 } 969 970 void 971 tracker_media_art_shutdown (void) 972 { 973 g_return_if_fail (initialized == TRUE); 974 975 if (connection) { 976 g_object_unref (connection); 977 } 978 979 if (media_art_cache) { 980 g_hash_table_unref (media_art_cache); 981 } 982 983 if (media_art_storage) { 984 g_object_unref (media_art_storage); 985 } 986 987 tracker_media_art_plugin_shutdown (); 988 989 initialized = FALSE; 990 } 991 992 static void 993 set_mtime (const gchar *filename, guint64 mtime) 994 { 995 996 struct utimbuf buf; 997 998 buf.actime = buf.modtime = mtime; 999 utime (filename, &buf); 1000 } 1001 1002 gboolean 1003 tracker_media_art_process (const unsigned char *buffer, 1004 size_t len, 1005 const gchar *mime, 1006 TrackerMediaArtType type, 1007 const gchar *artist, 1008 const gchar *title, 1009 const gchar *uri) 1010 { 1011 gchar *art_path; 1012 gchar *local_art_uri = NULL; 1013 gboolean processed = TRUE, a_exists, created = FALSE; 1014 guint64 mtime, a_mtime = 0; 1015 1016 g_return_val_if_fail (type > TRACKER_MEDIA_ART_NONE && type < TRACKER_MEDIA_ART_TYPE_COUNT, FALSE); 1017 1018 g_debug ("Processing media art: artist:'%s', title:'%s', type:'%s', uri:'%s'. Buffer is %ld bytes, mime:'%s'", 1019 artist ? artist : "", 1020 title ? title : "", 1021 media_art_type_name[type], 1022 uri, 1023 (long int) len, 1024 mime); 1025 1026 /* TODO: We can definitely work with GFiles better here */ 1027 1028 mtime = tracker_file_get_mtime_uri (uri); 1029 1030 tracker_media_art_get_path (artist, 1031 title, 1032 media_art_type_name[type], 1033 uri, 1034 &art_path, 1035 &local_art_uri); 1036 1037 if (!art_path) { 1038 g_debug ("Album art path could not be obtained, not processing any further"); 1039 1040 g_free (local_art_uri); 1041 1042 return FALSE; 1043 } 1044 1045 a_exists = g_file_test (art_path, G_FILE_TEST_EXISTS); 1046 1047 if (a_exists) { 1048 a_mtime = tracker_file_get_mtime (art_path); 1049 } 1050 1051 if ((buffer && len > 0) && ((!a_exists) || (a_exists && mtime > a_mtime))) { 1052 processed = media_art_set (buffer, len, mime, type, artist, title, uri); 1053 set_mtime (art_path, mtime); 1054 created = TRUE; 1055 } 1056 1057 if ((!created) && ((!a_exists) || (a_exists && mtime > a_mtime))) { 1058 /* If not, we perform a heuristic on the dir */ 1059 gchar *key; 1060 gchar *dirname = NULL; 1061 GFile *file, *dirf; 1062 1063 file = g_file_new_for_uri (uri); 1064 dirf = g_file_get_parent (file); 1065 if (dirf) { 1066 dirname = g_file_get_path (dirf); 1067 g_object_unref (dirf); 1068 } 1069 g_object_unref (file); 1070 1071 key = g_strdup_printf ("%i-%s-%s-%s", 1072 type, 1073 artist ? artist : "", 1074 title ? title : "", 1075 dirname ? dirname : ""); 1076 1077 g_free (dirname); 1078 1079 if (!g_hash_table_lookup (media_art_cache, key)) { 1080 if (!media_art_heuristic (artist, 1081 title, 1082 type, 1083 uri, 1084 local_art_uri)) { 1085 /* If the heuristic failed, we 1086 * request the download the 1087 * media-art to the media-art 1088 * downloaders 1089 */ 1090 media_art_request_download (media_art_storage, 1091 type, 1092 artist, 1093 title, 1094 local_art_uri, 1095 art_path); 1096 } 1097 1098 set_mtime (art_path, mtime); 1099 1100 g_hash_table_insert (media_art_cache, 1101 key, 1102 GINT_TO_POINTER(TRUE)); 1103 } else { 1104 g_free (key); 1105 } 1106 } else { 1107 if (!created) { 1108 g_debug ("Album art already exists for uri:'%s' as '%s'", 1109 uri, 1110 art_path); 1111 } 1112 } 1113 1114 if (local_art_uri && !g_file_test (local_art_uri, G_FILE_TEST_EXISTS)) { 1115 /* We can't reuse art_exists here because the 1116 * situation might have changed 1117 */ 1118 if (g_file_test (art_path, G_FILE_TEST_EXISTS)) { 1119 media_art_copy_to_local (media_art_storage, 1120 art_path, 1121 local_art_uri); 1122 } 1123 } 1124 1125 g_free (art_path); 1126 g_free (local_art_uri); 1127 1128 return processed; 1129 }