1 /*
  2  * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
  3  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
  4  *
  5  * This library is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU Lesser General Public
  7  * License as published by the Free Software Foundation; either
  8  * version 2.1 of the License, or (at your option) any later version.
  9  *
 10  * This library is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * Lesser General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU Lesser General Public
 16  * License along with this library; if not, write to the
 17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 18  * Boston, MA  02110-1301, USA.
 19  */
 20 
 21 #include "config.h"
 22 
 23 #ifndef _GNU_SOURCE
 24 #define _GNU_SOURCE
 25 #endif
 26 
 27 #include <errno.h>
 28 #include <fcntl.h>
 29 #include <string.h>
 30 #include <sys/types.h>
 31 #include <sys/stat.h>
 32 #include <unistd.h>
 33 
 34 #include <gif_lib.h>
 35 
 36 #include <libtracker-common/tracker-common.h>
 37 
 38 #include <libtracker-extract/tracker-extract.h>
 39 
 40 #define XMP_MAGIC_TRAILER_LENGTH 256
 41 #define EXTENSION_RECORD_COMMENT_BLOCK_CODE 0xFE
 42 
 43 typedef struct {
 44 	const gchar *title;
 45 	const gchar *date;
 46 	const gchar *artist;
 47 } MergeData;
 48 
 49 typedef struct {
 50 	gchar *width;
 51 	gchar *height;
 52 	gchar *comment;
 53 } GifData;
 54 
 55 typedef struct {
 56 	unsigned int   byteCount;
 57 	char          *bytes;
 58 } ExtBlock;
 59 
 60 static int
 61 ext_block_append(ExtBlock *extBlock,
 62 		 unsigned int len,
 63 		 unsigned char extData[])
 64 {
 65 	extBlock->bytes = realloc(extBlock->bytes,extBlock->byteCount+len);
 66 	if (extBlock->bytes == NULL) {
 67 		return (GIF_ERROR);
 68 	}
 69 
 70 	memcpy(&(extBlock->bytes[extBlock->byteCount]), &extData[0], len);
 71 	extBlock->byteCount += len;
 72 
 73 	return (GIF_OK);
 74 }
 75 
 76 #if GIFLIB_MAJOR >= 5
 77 static inline void
 78 gif_error (const gchar *action, int err)
 79 {
 80 	const char *str = GifErrorString (err);
 81 	if (str != NULL) {
 82 		g_message ("%s, error: '%s'", action, str);
 83 	} else {
 84 		g_message ("%s, undefined error %d", action, err);
 85 	}
 86 }
 87 #endif /* GIFLIB_MAJOR >= 5 */
 88 
 89 static void
 90 read_metadata (TrackerSparqlBuilder *preupdate,
 91                TrackerSparqlBuilder *metadata,
 92                GString              *where,
 93                GifFileType          *gifFile,
 94                const gchar          *uri,
 95                const gchar          *graph)
 96 {
 97 	GifRecordType RecordType;
 98 	int frameheight;
 99 	int framewidth;
100 	unsigned char *framedata = NULL;
101 	GPtrArray *keywords;
102 	guint i;
103 	int status;
104 	MergeData md = { 0 };
105 	GifData   gd = { 0 };
106 	TrackerXmpData *xd = NULL;
107 
108 	do {
109 		GifByteType *ExtData;
110 		int ExtCode;
111 		ExtBlock extBlock;
112 
113 		if (DGifGetRecordType(gifFile, &RecordType) == GIF_ERROR) {
114 #if GIFLIB_MAJOR < 5
115 			PrintGifError ();
116 #else  /* GIFLIB_MAJOR < 5 */
117 			gif_error ("Could not read next GIF record type", gifFile->Error);
118 #endif /* GIFLIB_MAJOR < 5 */
119 			return;
120 		}
121 
122 		switch (RecordType) {
123 			case IMAGE_DESC_RECORD_TYPE:
124 			if (DGifGetImageDesc(gifFile) == GIF_ERROR) {
125 #if GIFLIB_MAJOR < 5
126 				PrintGifError();
127 #else  /* GIFLIB_MAJOR < 5 */
128 				gif_error ("Could not get GIF record information", gifFile->Error);
129 #endif /* GIFLIB_MAJOR < 5 */
130 				return;
131 			}
132 
133 			framewidth  = gifFile->Image.Width;
134 			frameheight = gifFile->Image.Height;
135 
136 			framedata = g_malloc (framewidth*frameheight);
137 
138 			if (DGifGetLine(gifFile, framedata, framewidth*frameheight)==GIF_ERROR) {
139 #if GIFLIB_MAJOR < 5
140 				PrintGifError();
141 #else  /* GIFLIB_MAJOR < 5 */
142 				gif_error ("Could not load a block of GIF pixes", gifFile->Error);
143 #endif /* GIFLIB_MAJOR < 5 */
144 				return;
145 			}
146 
147 			gd.width  = g_strdup_printf ("%d", framewidth);
148 			gd.height = g_strdup_printf ("%d", frameheight);
149 
150 
151 			g_free (framedata);
152 
153 		break;
154 		case EXTENSION_RECORD_TYPE:
155 			extBlock.bytes = NULL;
156 			extBlock.byteCount = 0;
157 
158 			if ((status = DGifGetExtension (gifFile, &ExtCode, &ExtData)) != GIF_OK) {
159 				g_warning ("Problem getting the extension");
160 				return;
161 			}
162 #if defined(HAVE_EXEMPI)
163 			if (ExtData && *ExtData &&
164 			    strncmp (&ExtData[1],"XMP Data",8) == 0) {
   pointer targets in passing argument 1 of 'strlen' differ in signedness
   (emitted by gcc)
 165 				while (ExtData != NULL && status == GIF_OK ) {
166 					if ((status = DGifGetExtensionNext (gifFile, &ExtData)) == GIF_OK) {
167 						if (ExtData != NULL) {
168 							if (ext_block_append (&extBlock, ExtData[0]+1, (char *) &(ExtData[0])) != GIF_OK) {
169 								g_warning ("Problem with extension data");
170 								return;
171 							}
172 						}
173 					}
174 				}
175 
176 				xd = tracker_xmp_new (extBlock.bytes,
177 				                      extBlock.byteCount-XMP_MAGIC_TRAILER_LENGTH,
178 				                      uri);
179 
180 				g_free (extBlock.bytes);
181 			} else
182 #endif
183 			/* See Section 24. Comment Extension. in the GIF format definition */
184 			if (ExtCode == EXTENSION_RECORD_COMMENT_BLOCK_CODE &&
185 			    ExtData && *ExtData) {
186 				guint block_count = 0;
187 
188 				/* Merge all blocks */
189 				do {
190 					block_count++;
191 
192 					g_debug ("Comment Extension block found (#%u, %u bytes)",
193 					         block_count,
194 					         ExtData[0]);
195 					if (ext_block_append (&extBlock, ExtData[0], (char *) &(ExtData[1])) != GIF_OK) {
196 						g_warning ("Problem with Comment extension data");
197 						return;
198 					}
199 				} while (((status = DGifGetExtensionNext(gifFile, &ExtData)) == GIF_OK) &&
200 				         ExtData != NULL);
201 
202 				/* Add last NUL byte */
203 				g_debug ("Comment Extension blocks found (%u) with %u bytes",
204 				         block_count,
205 				         extBlock.byteCount);
206 				extBlock.bytes = g_realloc (extBlock.bytes, extBlock.byteCount + 1);
207 				extBlock.bytes[extBlock.byteCount] = '\0';
208 
209 				/* Set commentt */
210 				gd.comment = extBlock.bytes;
211 			} else {
212 				do {
213 					status = DGifGetExtensionNext(gifFile, &ExtData);
214 				} while ( status == GIF_OK && ExtData != NULL);
215 			}
216 		break;
217 		case TERMINATE_RECORD_TYPE:
218 			break;
219 		default:
220 			break;
221 		}
222 	} while (RecordType != TERMINATE_RECORD_TYPE);
223 
224 
225 	if (!xd) {
226 		xd = g_new0 (TrackerXmpData, 1);
227 	}
228 
229 	md.title = tracker_coalesce_strip (3, xd->title, xd->title2, xd->pdf_title);
230 	md.date = tracker_coalesce_strip (2, xd->date, xd->time_original);
231 	md.artist = tracker_coalesce_strip (2, xd->artist, xd->contributor);
232 
233 	if (xd->license) {
234 		tracker_sparql_builder_predicate (metadata, "nie:license");
235 		tracker_sparql_builder_object_unvalidated (metadata, xd->license);
236 	}
237 
238 	if (xd->creator) {
239 		gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", xd->creator);
240 
241 		tracker_sparql_builder_insert_open (preupdate, NULL);
242 		if (graph) {
243 			tracker_sparql_builder_graph_open (preupdate, graph);
244 		}
245 
246 		tracker_sparql_builder_subject_iri (preupdate, uri);
247 		tracker_sparql_builder_predicate (preupdate, "a");
248 		tracker_sparql_builder_object (preupdate, "nco:Contact");
249 		tracker_sparql_builder_predicate (preupdate, "nco:fullname");
250 		tracker_sparql_builder_object_unvalidated (preupdate, xd->creator);
251 
252 		if (graph) {
253 			tracker_sparql_builder_graph_close (preupdate);
254 		}
255 		tracker_sparql_builder_insert_close (preupdate);
256 
257 		tracker_sparql_builder_predicate (metadata, "nco:creator");
258 		tracker_sparql_builder_object_iri (metadata, uri);
259 		g_free (uri);
260 	}
261 
262 	tracker_guarantee_date_from_file_mtime (metadata,
263 	                                        "nie:contentCreated",
264 	                                        md.date,
265 	                                        uri);
266 
267 	if (xd->description) {
268 		tracker_sparql_builder_predicate (metadata, "nie:description");
269 		tracker_sparql_builder_object_unvalidated (metadata, xd->description);
270 	}
271 
272 	if (xd->copyright) {
273 		tracker_sparql_builder_predicate (metadata, "nie:copyright");
274 		tracker_sparql_builder_object_unvalidated (metadata, xd->copyright);
275 	}
276 
277 	if (xd->make || xd->model) {
278 		gchar *equip_uri;
279 
280 		equip_uri = tracker_sparql_escape_uri_printf ("urn:equipment:%s:%s:",
281 		                                              xd->make ? xd->make : "",
282 		                                              xd->model ? xd->model : "");
283 
284 		tracker_sparql_builder_insert_open (preupdate, NULL);
285 		if (graph) {
286 			tracker_sparql_builder_graph_open (preupdate, graph);
287 		}
288 
289 		tracker_sparql_builder_subject_iri (preupdate, equip_uri);
290 		tracker_sparql_builder_predicate (preupdate, "a");
291 		tracker_sparql_builder_object (preupdate, "nfo:Equipment");
292 
293 		if (xd->make) {
294 			tracker_sparql_builder_predicate (preupdate, "nfo:manufacturer");
295 			tracker_sparql_builder_object_unvalidated (preupdate, xd->make);
296 		}
297 		if (xd->model) {
298 			tracker_sparql_builder_predicate (preupdate, "nfo:model");
299 			tracker_sparql_builder_object_unvalidated (preupdate, xd->model);
300 		}
301 
302 		if (graph) {
303 			tracker_sparql_builder_graph_close (preupdate);
304 		}
305 		tracker_sparql_builder_insert_close (preupdate);
306 
307 		tracker_sparql_builder_predicate (metadata, "nfo:equipment");
308 		tracker_sparql_builder_object_iri (metadata, equip_uri);
309 		g_free (equip_uri);
310 	}
311 
312 	tracker_guarantee_title_from_file (metadata,
313 	                                   "nie:title",
314 	                                   md.title,
315 	                                   uri,
316 	                                   NULL);
317 
318 	if (md.artist) {
319 		gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", md.artist);
320 
321 		tracker_sparql_builder_insert_open (preupdate, NULL);
322 		if (graph) {
323 			tracker_sparql_builder_graph_open (preupdate, graph);
324 		}
325 
326 		tracker_sparql_builder_subject_iri (preupdate, uri);
327 		tracker_sparql_builder_predicate (preupdate, "a");
328 		tracker_sparql_builder_object (preupdate, "nco:Contact");
329 		tracker_sparql_builder_predicate (preupdate, "nco:fullname");
330 		tracker_sparql_builder_object_unvalidated (preupdate, md.artist);
331 
332 		if (graph) {
333 			tracker_sparql_builder_graph_close (preupdate);
334 		}
335 		tracker_sparql_builder_insert_close (preupdate);
336 
337 		tracker_sparql_builder_predicate (metadata, "nco:contributor");
338 		tracker_sparql_builder_object_iri (metadata, uri);
339 		g_free (uri);
340 	}
341 
342 	if (xd->orientation) {
343 		tracker_sparql_builder_predicate (metadata, "nfo:orientation");
344 		tracker_sparql_builder_object_unvalidated (metadata, xd->orientation);
345 	}
346 
347 	if (xd->exposure_time) {
348 		tracker_sparql_builder_predicate (metadata, "nmm:exposureTime");
349 		tracker_sparql_builder_object_unvalidated (metadata, xd->exposure_time);
350 	}
351 
352 	if (xd->iso_speed_ratings) {
353 		tracker_sparql_builder_predicate (metadata, "nmm:isoSpeed");
354 		tracker_sparql_builder_object_unvalidated (metadata, xd->iso_speed_ratings);
355 	}
356 
357 	if (xd->white_balance) {
358 		tracker_sparql_builder_predicate (metadata, "nmm:whiteBalance");
359 		tracker_sparql_builder_object_unvalidated (metadata, xd->white_balance);
360 	}
361 
362 	if (xd->fnumber) {
363 		tracker_sparql_builder_predicate (metadata, "nmm:fnumber");
364 		tracker_sparql_builder_object_unvalidated (metadata, xd->fnumber);
365 	}
366 
367 	if (xd->flash) {
368 		tracker_sparql_builder_predicate (metadata, "nmm:flash");
369 		tracker_sparql_builder_object_unvalidated (metadata, xd->flash);
370 	}
371 
372 	if (xd->focal_length) {
373 		tracker_sparql_builder_predicate (metadata, "nmm:focalLength");
374 		tracker_sparql_builder_object_unvalidated (metadata, xd->focal_length);
375 	}
376 
377 	if (xd->metering_mode) {
378 		tracker_sparql_builder_predicate (metadata, "nmm:meteringMode");
379 		tracker_sparql_builder_object_unvalidated (metadata, xd->metering_mode);
380 	}
381 
382 	keywords = g_ptr_array_new ();
383 
384 	if (xd->keywords) {
385 		tracker_keywords_parse (keywords, xd->keywords);
386 	}
387 
388 	if (xd->pdf_keywords) {
389 		tracker_keywords_parse (keywords, xd->pdf_keywords);
390 	}
391 
392 	if (xd->rating) {
393 		tracker_sparql_builder_predicate (metadata, "nao:numericRating");
394 		tracker_sparql_builder_object_unvalidated (metadata, xd->rating);
395 	}
396 
397 	if (xd->subject) {
398 		tracker_keywords_parse (keywords, xd->subject);
399 	}
400 
401         if (xd->regions) {
402                 tracker_xmp_apply_regions (preupdate, metadata, graph, xd);
403         }
404 
405 	for (i = 0; i < keywords->len; i++) {
406 		gchar *p, *escaped, *var;
407 
408 		p = g_ptr_array_index (keywords, i);
409 		escaped = tracker_sparql_escape_string (p);
410 		var = g_strdup_printf ("tag%d", i + 1);
411 
412 		/* ensure tag with specified label exists */
413 		tracker_sparql_builder_append (preupdate, "INSERT { ");
414 
415 		if (graph) {
416 			tracker_sparql_builder_append (preupdate, "GRAPH <");
417 			tracker_sparql_builder_append (preupdate, graph);
418 			tracker_sparql_builder_append (preupdate, "> { ");
419 		}
420 
421 		tracker_sparql_builder_append (preupdate,
422 		                               "_:tag a nao:Tag ; nao:prefLabel \"");
423 		tracker_sparql_builder_append (preupdate, escaped);
424 		tracker_sparql_builder_append (preupdate, "\"");
425 
426 		if (graph) {
427 			tracker_sparql_builder_append (preupdate, " } ");
428 		}
429 
430 		tracker_sparql_builder_append (preupdate, " }\n");
431 		tracker_sparql_builder_append (preupdate,
432 		                               "WHERE { FILTER (NOT EXISTS { "
433 		                               "?tag a nao:Tag ; nao:prefLabel \"");
434 		tracker_sparql_builder_append (preupdate, escaped);
435 		tracker_sparql_builder_append (preupdate,
436 		                               "\" }) }\n");
437 
438 		/* associate file with tag */
439 		tracker_sparql_builder_predicate (metadata, "nao:hasTag");
440 		tracker_sparql_builder_object_variable (metadata, var);
441 
442 		g_string_append_printf (where, "?%s a nao:Tag ; nao:prefLabel \"%s\" .\n", var, escaped);
443 
444 		g_free (var);
445 		g_free (escaped);
446 		g_free (p);
447 	}
448 	g_ptr_array_free (keywords, TRUE);
449 
450 	if (xd->publisher) {
451 		gchar *uri = tracker_sparql_escape_uri_printf ("urn:contact:%s", xd->publisher);
452 
453 		tracker_sparql_builder_insert_open (preupdate, NULL);
454 		if (graph) {
455 			tracker_sparql_builder_graph_open (preupdate, graph);
456 		}
457 
458 		tracker_sparql_builder_subject_iri (preupdate, uri);
459 		tracker_sparql_builder_predicate (preupdate, "a");
460 		tracker_sparql_builder_object (preupdate, "nco:Contact");
461 		tracker_sparql_builder_predicate (preupdate, "nco:fullname");
462 		tracker_sparql_builder_object_unvalidated (preupdate, xd->publisher);
463 
464 		if (graph) {
465 			tracker_sparql_builder_graph_close (preupdate);
466 		}
467 		tracker_sparql_builder_insert_close (preupdate);
468 
469 		tracker_sparql_builder_predicate (metadata, "nco:creator");
470 		tracker_sparql_builder_object_iri (metadata, uri);
471 		g_free (uri);
472 	}
473 
474 	if (xd->type) {
475 		tracker_sparql_builder_predicate (metadata, "dc:type");
476 		tracker_sparql_builder_object_unvalidated (metadata, xd->type);
477 	}
478 
479 	if (xd->format) {
480 		tracker_sparql_builder_predicate (metadata, "dc:format");
481 		tracker_sparql_builder_object_unvalidated (metadata, xd->format);
482 	}
483 
484 	if (xd->identifier) {
485 		tracker_sparql_builder_predicate (metadata, "dc:identifier");
486 		tracker_sparql_builder_object_unvalidated (metadata, xd->identifier);
487 	}
488 
489 	if (xd->source) {
490 		tracker_sparql_builder_predicate (metadata, "dc:source");
491 		tracker_sparql_builder_object_unvalidated (metadata, xd->source);
492 	}
493 
494 	if (xd->language) {
495 		tracker_sparql_builder_predicate (metadata, "dc:language");
496 		tracker_sparql_builder_object_unvalidated (metadata, xd->language);
497 	}
498 
499 	if (xd->relation) {
500 		tracker_sparql_builder_predicate (metadata, "dc:relation");
501 		tracker_sparql_builder_object_unvalidated (metadata, xd->relation);
502 	}
503 
504 	if (xd->coverage) {
505 		tracker_sparql_builder_predicate (metadata, "dc:coverage");
506 		tracker_sparql_builder_object_unvalidated (metadata, xd->coverage);
507 	}
508 
509 	if (xd->address || xd->state || xd->country || xd->city ||
510 	    xd->gps_altitude || xd->gps_latitude || xd-> gps_longitude) {
511 
512 		tracker_sparql_builder_predicate (metadata, "slo:location");
513 
514 		tracker_sparql_builder_object_blank_open (metadata); /* GeoLocation */
515 		tracker_sparql_builder_predicate (metadata, "a");
516 		tracker_sparql_builder_object (metadata, "slo:GeoLocation");
517 
518 		if (xd->address || xd->state || xd->country || xd->city)  {
519 			gchar *addruri;
520 			addruri = tracker_sparql_get_uuid_urn ();
521 
522 			tracker_sparql_builder_predicate (metadata, "slo:postalAddress");
523 			tracker_sparql_builder_object_iri (metadata, addruri);
524 
525 			tracker_sparql_builder_insert_open (preupdate, NULL);
526 			if (graph) {
527 				tracker_sparql_builder_graph_open (preupdate, graph);
528 			}
529 
530 			tracker_sparql_builder_subject_iri (preupdate, addruri);
531 
532 			g_free (addruri);
533 
534 			tracker_sparql_builder_predicate (preupdate, "a");
535 			tracker_sparql_builder_object (preupdate, "nco:PostalAddress");
536 
537 			if (xd->address) {
538 				tracker_sparql_builder_predicate (preupdate, "nco:streetAddress");
539 				tracker_sparql_builder_object_unvalidated (preupdate, xd->address);
540 			}
541 
542 			if (xd->state) {
543 				tracker_sparql_builder_predicate (preupdate, "nco:region");
544 				tracker_sparql_builder_object_unvalidated (preupdate, xd->state);
545 			}
546 
547 			if (xd->city) {
548 				tracker_sparql_builder_predicate (preupdate, "nco:locality");
549 				tracker_sparql_builder_object_unvalidated (preupdate, xd->city);
550 			}
551 
552 			if (xd->country) {
553 				tracker_sparql_builder_predicate (preupdate, "nco:country");
554 				tracker_sparql_builder_object_unvalidated (preupdate, xd->country);
555 			}
556 
557 			if (graph) {
558 				tracker_sparql_builder_graph_close (preupdate);
559 			}
560 			tracker_sparql_builder_insert_close (preupdate);
561 		}
562 
563 		if (xd->gps_altitude) {
564 			tracker_sparql_builder_predicate (metadata, "slo:altitude");
565 			tracker_sparql_builder_object_unvalidated (metadata, xd->gps_altitude);
566 		}
567 
568 		if (xd->gps_latitude) {
569 			tracker_sparql_builder_predicate (metadata, "slo:latitude");
570 			tracker_sparql_builder_object_unvalidated (metadata, xd->gps_latitude);
571 		}
572 
573 		if (xd->gps_longitude) {
574 			tracker_sparql_builder_predicate (metadata, "slo:longitude");
575 			tracker_sparql_builder_object_unvalidated (metadata, xd->gps_longitude);
576 		}
577 
578 		tracker_sparql_builder_object_blank_close (metadata); /* GeoLocation */
579 	}
580 
581 	if (xd->gps_direction) {
582 		tracker_sparql_builder_predicate (metadata, "nfo:heading");
583 		tracker_sparql_builder_object_unvalidated (metadata, xd->gps_direction);
584 	}
585 
586 	if (gd.width) {
587 		tracker_sparql_builder_predicate (metadata, "nfo:width");
588 		tracker_sparql_builder_object_unvalidated (metadata, gd.width);
589 		g_free (gd.width);
590 	}
591 
592 	if (gd.height) {
593 		tracker_sparql_builder_predicate (metadata, "nfo:height");
594 		tracker_sparql_builder_object_unvalidated (metadata, gd.height);
595 		g_free (gd.height);
596 	}
597 
598 	if (gd.comment) {
599 		tracker_sparql_builder_predicate (metadata, "nie:comment");
600 		tracker_sparql_builder_object_unvalidated (metadata, gd.comment);
601 		g_free (gd.comment);
602 	}
603 
604 	tracker_xmp_free (xd);
605 }
606 
607 
608 G_MODULE_EXPORT gboolean
609 tracker_extract_get_metadata (TrackerExtractInfo *info)
610 {
611 	TrackerSparqlBuilder *preupdate, *metadata;
612 	goffset size;
613 	GifFileType *gifFile = NULL;
614 	GString *where;
615 	const gchar *graph;
616 	gchar *filename, *uri;
617 	GFile *file;
618 	int fd;
619 #if GIFLIB_MAJOR >= 5
620 	int err;
621 #endif
622 
623 	preupdate = tracker_extract_info_get_preupdate_builder (info);
624 	metadata = tracker_extract_info_get_metadata_builder (info);
625 	graph = tracker_extract_info_get_graph (info);
626 
627 	file = tracker_extract_info_get_file (info);
628 	filename = g_file_get_path (file);
629 	size = tracker_file_get_size (filename);
630 
631 	if (size < 64) {
632 		g_free (filename);
633 		return FALSE;
634 	}
635 
636 	fd = tracker_file_open_fd (filename);
637 
638 	if (fd == -1) {
639 		g_warning ("Could not open GIF file '%s': %s\n",
640 		           filename,
641 		           g_strerror (errno));
642 		g_free (filename);
643 		return FALSE;
644 	}	
645 
646 #if GIFLIB_MAJOR < 5
647 	if ((gifFile = DGifOpenFileHandle (fd)) == NULL) {
648 		PrintGifError ();
649 #else   /* GIFLIB_MAJOR < 5 */
650 	if ((gifFile = DGifOpenFileHandle (fd, &err)) == NULL) {
651 		gif_error ("Could not open GIF file with handle", err);
652 #endif /* GIFLIB_MAJOR < 5 */
653 		close (fd);
654 		return FALSE;
655 	}
656 
657 	g_free (filename);
658 
659 	tracker_sparql_builder_predicate (metadata, "a");
660 	tracker_sparql_builder_object (metadata, "nfo:Image");
661 	tracker_sparql_builder_object (metadata, "nmm:Photo");
662 
663 	where = g_string_new ("");
664 	uri = g_file_get_uri (file);
665 
666 	read_metadata (preupdate, metadata, where, gifFile, uri, graph);
667 	tracker_extract_info_set_where_clause (info, where->str);
668 	g_string_free (where, TRUE);
669 
670 	g_free (uri);
671 
672 	if (DGifCloseFile (gifFile) != GIF_OK) {
673 #if GIFLIB_MAJOR < 5
674 		PrintGifError ();
675 #else  /* GIFLIB_MAJOR < 5 */
676 		gif_error ("Could not close GIF file", gifFile->Error);
677 #endif /* GIFLIB_MAJOR < 5 */
678 	}
679 
680 	return TRUE;
681 }