1 /*
2    Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Bastien Nocera
3    Copyright (C) 2003, 2004 Colin Walters <walters@rhythmbox.org>
4 
5    The Gnome Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9 
10    The Gnome 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    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with the Gnome Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301  USA.
19 
20    Author: Bastien Nocera <hadess@hadess.net>
21  */
22 
23 #include "config.h"
24 
25 #ifndef TOTEM_PL_PARSER_MINI
26 #include <string.h>
27 #include <glib.h>
28 #include <stdio.h>
29 
30 #include "totem-pl-parser.h"
31 #include "totem-disc.h"
32 #endif /* !TOTEM_PL_PARSER_MINI */
33 
34 #include "totem-pl-parser-mini.h"
35 #include "totem-pl-parser-media.h"
36 #include "totem-pl-parser-private.h"
37 
38 /* Files that start with these characters sort after files that don't. */
39 #define SORT_LAST_CHAR1 '.'
40 #define SORT_LAST_CHAR2 '#'
41 
42 #ifndef TOTEM_PL_PARSER_MINI
43 /* Returns NULL if we don't have an ISO image,
44  * or an empty string if it's non-UTF-8 data */
45 static char *
totem_pl_parser_iso_get_title(GFile * _file)46 totem_pl_parser_iso_get_title (GFile *_file)
47 {
48 	char *fname;
49 	FILE  *file;
50 #define BUFFER_SIZE 128
51 	char buf [BUFFER_SIZE+1];
52 	int res;
53 	char *str;
54 
55 	fname = g_file_get_path (_file);
56 	if (fname == NULL)
57 		return NULL;
58 
59 	file = fopen (fname, "rb");
60 	g_free (fname);
61 	if (file == NULL)
62 		return NULL;
63 
64 	/* Verify we have an ISO image */
65 	/* This check is for the raw sector images */
66 	res = fseek (file, 37633L, SEEK_SET);
67 	if (res != 0) {
68 		fclose (file);
69 		return NULL;
70 	}
71 
72 	res = fread (buf, sizeof (char), 5, file);
73 	if (res != 5 || strncmp (buf, "CD001", 5) != 0) {
74 		/* Standard ISO images */
75 		res = fseek (file, 32769L, SEEK_SET);
76 		if (res != 0) {
77 			fclose (file);
78 			return NULL;
79 		}
80 		res = fread (buf, sizeof (char), 5, file);
81 		if (res != 5 || strncmp (buf, "CD001", 5) != 0) {
82 			/* High Sierra images */
83 			res = fseek (file, 32776L, SEEK_SET);
84 			if (res != 0) {
85 				fclose (file);
86 				return NULL;
87 			}
88 			res = fread (buf, sizeof (char), 5, file);
89 			if (res != 5 || strncmp (buf, "CDROM", 5) != 0) {
90 				fclose (file);
91 				return NULL;
92 			}
93 		}
94 	}
95 	/* Extract the volume label from the image */
96 	res = fseek (file, 32808L, SEEK_SET);
97 	if (res != 0) {
98 		fclose (file);
99 		return NULL;
100 	}
101 	res = fread (buf, sizeof(char), BUFFER_SIZE, file);
102 	fclose (file);
103 	if (res != BUFFER_SIZE)
104 		return NULL;
105 
106 	buf [BUFFER_SIZE] = '\0';
107 	str = g_strdup (g_strstrip (buf));
108 	if (!g_utf8_validate (str, -1, NULL)) {
109 		g_free (str);
110 		return g_strdup ("");
111 	}
112 
113 	return str;
114 }
115 
116 TotemPlParserResult
totem_pl_parser_add_iso(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)117 totem_pl_parser_add_iso (TotemPlParser *parser,
118 			 GFile *file,
119 			 GFile *base_file,
120 			 TotemPlParseData *parse_data,
121 			 gpointer data)
122 {
123 	TotemDiscMediaType type;
124 	char *uri, *retval;
125 
126 	uri = g_file_get_uri (file);
127 	type = totem_cd_detect_type_with_url (uri, &retval, NULL);
128 	g_free (uri);
129 	if (type == MEDIA_TYPE_DVD || type == MEDIA_TYPE_VCD) {
130 		char *label;
131 
132 		label = totem_pl_parser_iso_get_title (file);
133 		totem_pl_parser_add_one_uri (parser, retval, label);
134 		g_free (label);
135 		g_free (retval);
136 		return TOTEM_PL_PARSER_RESULT_SUCCESS;
137 	}
138 
139 	return TOTEM_PL_PARSER_RESULT_IGNORED;
140 }
141 
142 TotemPlParserResult
totem_pl_parser_add_cue(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)143 totem_pl_parser_add_cue (TotemPlParser *parser,
144 			 GFile *file,
145 			 GFile *base_file,
146 			 TotemPlParseData *parse_data,
147 			 gpointer data)
148 {
149 	char *vcduri, *path;
150 
151 	path = g_file_get_path (file);
152 	if (path == NULL)
153 		return TOTEM_PL_PARSER_RESULT_IGNORED;
154 
155 	vcduri = totem_cd_mrl_from_type ("vcd", path);
156 	g_free (path);
157 	totem_pl_parser_add_one_uri (parser, vcduri, NULL);
158 	g_free (vcduri);
159 
160 	return TOTEM_PL_PARSER_RESULT_SUCCESS;
161 }
162 
163 static int
totem_pl_parser_dir_compare(GFileInfo * a,GFileInfo * b)164 totem_pl_parser_dir_compare (GFileInfo *a, GFileInfo *b)
165 {
166 	const char *name_1, *name_2;
167 	char *key_1, *key_2;
168 	gboolean sort_last_1, sort_last_2;
169 	int compare;
170 
171 	name_1 = g_file_info_get_name (a);
172 	name_2 = g_file_info_get_name (b);
173 
174 	if (name_1 == NULL) {
175 		if (name_2 == NULL)
176 			compare = 0;
177 		else
178 			compare = -1;
179 	} else {
180 		sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
181 		sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
182 
183 		if (sort_last_1 && !sort_last_2) {
184 			compare = +1;
185 		} else if (!sort_last_1 && sort_last_2) {
186 			compare = -1;
187 		} else {
188 			key_1 = g_utf8_collate_key_for_filename (name_1, -1);
189 			key_2 = g_utf8_collate_key_for_filename (name_2, -1);
190 			compare = strcmp (key_1, key_2);
191 			g_free (key_1);
192 			g_free (key_2);
193 		}
194 	}
195 
196 	return compare;
197 }
198 
199 static gboolean
totem_pl_parser_load_directory(GFile * file,GList ** list,gboolean * unhandled)200 totem_pl_parser_load_directory (GFile *file, GList **list, gboolean *unhandled)
201 {
202 	GFileEnumerator *e;
203 	GFileInfo *info;
204 	GError *err = NULL;
205 
206 	*list = NULL;
207 	*unhandled = FALSE;
208 
209 	e = g_file_enumerate_children (file,
210 				       G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
211 				       G_FILE_QUERY_INFO_NONE,
212 				       NULL, &err);
213 	if (e == NULL) {
214 		if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED) != FALSE)
215 			*unhandled = TRUE;
216 		g_error_free (err);
217 		return FALSE;
218 	}
219 
220 	while ((info = g_file_enumerator_next_file (e, NULL, NULL)) != NULL)
221 		*list = g_list_prepend (*list, info);
222 
223 	g_file_enumerator_close (e, NULL, NULL);
224 	g_object_unref (e);
225 	return TRUE;
226 }
227 
228 TotemPlParserResult
totem_pl_parser_add_directory(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)229 totem_pl_parser_add_directory (TotemPlParser *parser,
230 			       GFile *file,
231 			       GFile *base_file,
232 			       TotemPlParseData *parse_data,
233 			       gpointer data)
234 {
235 	TotemDiscMediaType type;
236 	GList *list, *l;
237 	char *media_uri, *uri;
238 	gboolean unhandled;
239 
240 	uri = g_file_get_uri (file);
241 	media_uri = NULL;
242 	type = totem_cd_detect_type_from_dir (uri, &media_uri, NULL);
243 	g_free (uri);
244 
245 	if (type != MEDIA_TYPE_DATA && type != MEDIA_TYPE_ERROR && media_uri != NULL) {
246 		char *base_name = NULL, *fname;
247 
248 		fname = g_file_get_path (file);
249 		if (fname != NULL) {
250 			base_name = g_filename_display_basename (fname);
251 			g_free (fname);
252 		}
253 		totem_pl_parser_add_one_uri (parser, media_uri, base_name);
254 		g_free (base_name);
255 		g_free (media_uri);
256 		return TOTEM_PL_PARSER_RESULT_SUCCESS;
257 	}
258 	g_free (media_uri);
259 
260 	if (totem_pl_parser_load_directory (file, &list, &unhandled) == FALSE) {
261 		if (unhandled != FALSE)
262 			return TOTEM_PL_PARSER_RESULT_UNHANDLED;
263 		return TOTEM_PL_PARSER_RESULT_ERROR;
264 	}
265 
266 	list = g_list_sort (list, (GCompareFunc) totem_pl_parser_dir_compare);
267 	l = list;
268 
269 	while (l != NULL) {
270 		GFileInfo *info = l->data;
271 		GFile *item;
272 		TotemPlParserResult ret;
273 		const char *content_type;
274 
275 		item = g_file_get_child (file, g_file_info_get_name (info));
276 
277 		/* Ignore partial files */
278 		content_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
279 		if (g_strcmp0 ("application/x-partial-download", content_type) == 0)
280 			ret = TOTEM_PL_PARSER_RESULT_IGNORED;
281 		else
282 			ret = totem_pl_parser_parse_internal (parser, item, NULL, parse_data);
283 
284 		if (ret != TOTEM_PL_PARSER_RESULT_SUCCESS &&
285 		    ret != TOTEM_PL_PARSER_RESULT_IGNORED &&
286 		    ret != TOTEM_PL_PARSER_RESULT_ERROR) {
287 			char *item_uri;
288 
289 			item_uri = g_file_get_uri (item);
290 			totem_pl_parser_add_one_uri (parser, item_uri, NULL);
291 			g_free (item_uri);
292 		}
293 
294 		g_object_unref (item);
295 		g_object_unref (info);
296 
297 		l = l->next;
298 	}
299 
300 	g_list_free (list);
301 
302 	return TOTEM_PL_PARSER_RESULT_SUCCESS;
303 }
304 
305 TotemPlParserResult
totem_pl_parser_add_block(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)306 totem_pl_parser_add_block (TotemPlParser *parser,
307 			   GFile *file,
308 			   GFile *base_file,
309 			   TotemPlParseData *parse_data,
310 			   gpointer data)
311 {
312 	TotemDiscMediaType type;
313 	char *media_uri, *path;
314 	GError *err = NULL;
315 
316 	path = g_file_get_path (file);
317 	if (path == NULL)
318 		return TOTEM_PL_PARSER_RESULT_UNHANDLED;
319 
320 	type = totem_cd_detect_type_with_url (path, &media_uri, &err);
321 	g_free (path);
322 	if (err != NULL) {
323 		DEBUG(file, g_print ("Couldn't get CD type for URI '%s': %s\n", uri, err->message));
324 		g_error_free (err);
325 	}
326 	if (media_uri == NULL)
327 		return TOTEM_PL_PARSER_RESULT_UNHANDLED;
328 	else if (type == MEDIA_TYPE_ERROR)
329 		return TOTEM_PL_PARSER_RESULT_ERROR;
330 
331 	totem_pl_parser_add_one_uri (parser, media_uri, NULL);
332 	g_free (media_uri);
333 	return TOTEM_PL_PARSER_RESULT_SUCCESS;
334 }
335 
336 #endif /* !TOTEM_PL_PARSER_MINI */
337 
338 
339