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 #include <string.h>
26 #include <stdlib.h>
27 #include <glib.h>
28 
29 #ifndef TOTEM_PL_PARSER_MINI
30 #include <glib/gi18n-lib.h>
31 
32 #include <gio/gio.h>
33 
34 #include "totem-pl-parser.h"
35 #include "totem-pl-parser-pls.h"
36 #endif /* !TOTEM_PL_PARSER_MINI */
37 
38 #include "totem-pl-parser-mini.h"
39 #include "totem-pl-parser-lines.h"
40 #include "totem-pl-parser-private.h"
41 
42 #ifndef TOTEM_PL_PARSER_MINI
43 
44 #define EXTINF "#EXTINF:"
45 #define EXTINF_HLS "#EXT-X-STREAM-INF"
46 #define EXTINF_HLS2 "#EXT-X-TARGETDURATION"
47 #define EXTVLCOPT_AUDIOTRACK "#EXTVLCOPT:audio-track-id="
48 
49 static char *
totem_pl_parser_uri_to_dos(const char * uri,GFile * output)50 totem_pl_parser_uri_to_dos (const char *uri, GFile *output)
51 {
52 	char *retval, *i;
53 
54 	/* Get a relative URI if there is one */
55 	retval = totem_pl_parser_relative (output, uri);
56 
57 	if (retval == NULL)
58 		retval = g_strdup (uri);
59 
60 	/* Don't change URIs, but change smb:// */
61 	if (g_str_has_prefix (retval, "smb://") != FALSE) {
62 		char *tmp;
63 		tmp = g_strdup (retval + strlen ("smb:"));
64 		g_free (retval);
65 		retval = tmp;
66 	}
67 
68 	if (strstr (retval, "://") != NULL)
69 		return retval;
70 
71 	i = retval;
72 	while (*i != '\0')
73 	{
74 		if (*i == '/')
75 			*i = '\\';
76 		i++;
77 	}
78 
79 	return retval;
80 }
81 
82 gboolean
totem_pl_parser_save_m3u(TotemPlParser * parser,TotemPlPlaylist * playlist,GFile * output,gboolean dos_compatible,GCancellable * cancellable,GError ** error)83 totem_pl_parser_save_m3u (TotemPlParser    *parser,
84                           TotemPlPlaylist  *playlist,
85                           GFile            *output,
86                           gboolean          dos_compatible,
87                           GCancellable     *cancellable,
88                           GError          **error)
89 {
90         TotemPlPlaylistIter iter;
91 	GFileOutputStream *stream;
92 	gboolean valid, success;
93 	char *buf;
94 	const char *cr;
95 
96 	stream = g_file_replace (output, NULL, FALSE, G_FILE_CREATE_NONE, cancellable, error);
97 	if (stream == NULL)
98 		return FALSE;
99 
100 	cr = dos_compatible ? "\r\n" : "\n";
101 
102 	buf = g_strdup_printf ("#EXTM3U%s", cr);
103 	success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
104 	g_free (buf);
105 	if (success == FALSE)
106 		return FALSE;
107 
108         valid = totem_pl_playlist_iter_first (playlist, &iter);
109 
110         while (valid) {
111 		char *uri, *title, *path2;
112 		GFile *file;
113 
114                 totem_pl_playlist_get (playlist, &iter,
115                                        TOTEM_PL_PARSER_FIELD_URI, &uri,
116                                        TOTEM_PL_PARSER_FIELD_TITLE, &title,
117                                        NULL);
118 
119                 valid = totem_pl_playlist_iter_next (playlist, &iter);
120 
121                 if (!uri) {
122                         g_free (title);
123                         continue;
124                 }
125 
126                 file = g_file_new_for_uri (uri);
127 
128 		if (totem_pl_parser_scheme_is_ignored (parser, file) != FALSE) {
129 			g_object_unref (file);
130 			g_free (uri);
131 			g_free (title);
132 			continue;
133 		}
134 		g_object_unref (file);
135 
136 		if (title) {
137 			buf = g_strdup_printf (EXTINF",%s%s", title, cr);
138 			success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
139 			g_free (buf);
140 			if (success == FALSE) {
141 				g_free (title);
142 				g_free (uri);
143 				return FALSE;
144 			}
145 		}
146 		g_free (title);
147 
148 		if (dos_compatible == FALSE) {
149 			char *tmp;
150 
151 			tmp = totem_pl_parser_relative (output, uri);
152 
153 			if (tmp == NULL && g_str_has_prefix (uri, "file:")) {
154 				path2 = g_filename_from_uri (uri, NULL, NULL);
155 			} else {
156 				path2 = tmp;
157 			}
158 		} else {
159 			path2 = totem_pl_parser_uri_to_dos (uri, output);
160 		}
161 
162 		buf = g_strdup_printf ("%s%s", path2 ? path2 : uri, cr);
163 		g_free (path2);
164 		g_free (uri);
165 
166 		success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
167 		g_free (buf);
168 
169 		if (success == FALSE)
170 			return FALSE;
171 	}
172 
173 	g_object_unref (stream);
174 
175 	return TRUE;
176 }
177 
178 static void
totem_pl_parser_parse_ram_uri(TotemPlParser * parser,const char * uri)179 totem_pl_parser_parse_ram_uri (TotemPlParser *parser, const char *uri)
180 {
181 	char *mark, **params;
182 	GString *str;
183 	guint i, num_params;
184 	char *title, *author, *copyright, *abstract, *screensize, *mode, *start, *end;
185 
186 	if (g_str_has_prefix (uri, "rtsp://") == FALSE
187 	    && g_str_has_prefix (uri, "pnm://") == FALSE) {
188 		totem_pl_parser_add_one_uri (parser, uri, NULL);
189 		return;
190 	}
191 
192 	/* Look for "?" */
193 	mark = strstr (uri, "?");
194 	if (mark == NULL) {
195 		totem_pl_parser_add_one_uri (parser, uri, NULL);
196 		return;
197 	}
198 
199 	if (mark[1] == '\0') {
200 		char *new_uri;
201 
202 		new_uri = g_strndup (uri, mark + 1 - uri);
203 		totem_pl_parser_add_one_uri (parser, new_uri, NULL);
204 		g_free (new_uri);
205 		return;
206 	}
207 
208 	title = author = copyright = abstract = screensize = mode = end = start = NULL;
209 	num_params = 0;
210 
211 	str = g_string_new_len (uri, mark - uri);
212 	params = g_strsplit (mark + 1, "&", -1);
213 	for (i = 0; params[i] != NULL; i++) {
214 		if (g_str_has_prefix (params[i], "title=") != FALSE) {
215 			title = params[i] + strlen ("title=");
216 		} else if (g_str_has_prefix (params[i], "author=") != FALSE) {
217 			author = params[i] + strlen ("author=");
218 		} else if (g_str_has_prefix (params[i], "copyright=") != FALSE) {
219 			copyright = params[i] + strlen ("copyright=");
220 		} else if (g_str_has_prefix (params[i], "abstract=") != FALSE) {
221 			abstract = params[i] + strlen ("abstract=");
222 		} else if (g_str_has_prefix (params[i], "screensize=") != FALSE) {
223 			screensize = params[i] + strlen ("screensize=");
224 		} else if (g_str_has_prefix (params[i], "mode=") != FALSE) {
225 			mode = params[i] + strlen ("mode=");
226 		} else if (g_str_has_prefix (params[i], "end=") != FALSE) {
227 			end = params[i] + strlen ("end=");
228 		} else if (g_str_has_prefix (params[i], "start=") != FALSE) {
229 			start = params[i] + strlen ("start=");
230 		} else {
231 			if (num_params == 0)
232 				g_string_append_c (str, '?');
233 			else
234 				g_string_append_c (str, '&');
235 			g_string_append (str, params[i]);
236 			num_params++;
237 		}
238 	}
239 
240 	totem_pl_parser_add_uri (parser,
241 				 TOTEM_PL_PARSER_FIELD_URI, str->str,
242 				 TOTEM_PL_PARSER_FIELD_TITLE, title,
243 				 TOTEM_PL_PARSER_FIELD_AUTHOR, author,
244 				 TOTEM_PL_PARSER_FIELD_COPYRIGHT, copyright,
245 				 TOTEM_PL_PARSER_FIELD_ABSTRACT, abstract,
246 				 TOTEM_PL_PARSER_FIELD_SCREENSIZE, screensize,
247 				 TOTEM_PL_PARSER_FIELD_UI_MODE, mode,
248 				 TOTEM_PL_PARSER_FIELD_STARTTIME, start,
249 				 TOTEM_PL_PARSER_FIELD_ENDTIME, end,
250 				 NULL);
251 
252 	g_string_free (str, TRUE);
253 	g_strfreev (params);
254 }
255 
256 TotemPlParserResult
totem_pl_parser_add_ram(TotemPlParser * parser,GFile * file,TotemPlParseData * parse_data,gpointer data)257 totem_pl_parser_add_ram (TotemPlParser *parser, GFile *file, TotemPlParseData *parse_data, gpointer data)
258 {
259 	gboolean retval = TOTEM_PL_PARSER_RESULT_UNHANDLED;
260 	char *contents, **lines;
261 	gsize size;
262 	guint i;
263 
264 	if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE)
265 		return TOTEM_PL_PARSER_RESULT_ERROR;
266 
267 	lines = g_strsplit_set (contents, "\r\n", 0);
268 	g_free (contents);
269 
270 	for (i = 0; lines[i] != NULL; i++) {
271 		/* Empty line */
272 		if (totem_pl_parser_line_is_empty (lines[i]) != FALSE)
273 			continue;
274 
275 		retval = TOTEM_PL_PARSER_RESULT_SUCCESS;
276 
277 		/* Either it's a URI, or it has a proper path ... */
278 		if (strstr(lines[i], "://") != NULL
279 				|| lines[i][0] == G_DIR_SEPARATOR) {
280 			GFile *line_file;
281 
282 			line_file = g_file_new_for_uri (lines[i]);
283 			/* .ram files can contain .smil entries */
284 			if (totem_pl_parser_parse_internal (parser, line_file, NULL, parse_data) != TOTEM_PL_PARSER_RESULT_SUCCESS)
285 				totem_pl_parser_parse_ram_uri (parser, lines[i]);
286 			g_object_unref (line_file);
287 		} else if (strcmp (lines[i], "--stop--") == 0) {
288 			/* For Real Media playlists, handle the stop command */
289 			break;
290 		} else {
291 			//FIXME
292 #if 0
293 			char *base;
294 
295 			/* Try with a base */
296 			base = totem_pl_parser_base_uri (uri);
297 
298 			if (totem_pl_parser_parse_internal (parser, lines[i], base) != TOTEM_PL_PARSER_RESULT_SUCCESS)
299 			{
300 				char *fullpath;
301 				fullpath = g_strdup_printf ("%s/%s", base, lines[i]);
302 				totem_pl_parser_parse_ram_uri (parser, fullpath);
303 				g_free (fullpath);
304 			}
305 			g_free (base);
306 #endif
307 		}
308 	}
309 
310 	g_strfreev (lines);
311 
312 	return retval;
313 }
314 
315 static const char *
totem_pl_parser_get_extinfo_title(const char * extinfo)316 totem_pl_parser_get_extinfo_title (const char *extinfo)
317 {
318 	const char *res, *sep;
319 
320 	if (extinfo == NULL)
321 		return NULL;
322 
323 	/* It's bound to have an EXTINF if we have extinfo */
324 	res = extinfo + strlen(EXTINF);
325 	if (res[0] == '\0')
326 		return NULL;
327 
328 	/* Handle ',' as a field separator */
329 	sep = strstr (res, ",");
330 	if (sep == NULL || sep[1] == '\0') {
331 		if (res[1] == '\0')
332 			return NULL;
333 		return res;
334 	}
335 
336 	sep++;
337 	return sep;
338 }
339 
340 static char *
totem_pl_parser_get_extinfo_length(const char * extinfo)341 totem_pl_parser_get_extinfo_length (const char *extinfo)
342 {
343 	char *res, **items;
344 
345 	if (extinfo == NULL)
346 		return NULL;
347 
348 	/* It's bound to have an EXTINF if we have extinfo */
349 	res = (char *) extinfo + strlen(EXTINF);
350 	if (res[0] == '\0')
351 		return NULL;
352 
353 	/* Handle ',' as a field separator */
354 	items = g_strsplit (res, ",", 2);
355 	if (!items || !items[0] || *items[0] == '\0') {
356 		g_strfreev (items);
357 		return NULL;
358 	}
359 	res = g_strdup (items[0]);
360 	g_strfreev (items);
361 	return res;
362 }
363 
364 static char *
totem_pl_parser_get_extvlcopt_audio_track(const char * line)365 totem_pl_parser_get_extvlcopt_audio_track (const char *line)
366 {
367 	int id;
368 	char *end;
369 
370 	if (line == NULL)
371 		return NULL;
372 	id = strtol (line + strlen (EXTVLCOPT_AUDIOTRACK), &end, 10);
373 	if (*end != '\0')
374 		return NULL;
375 	/* Bizarre VLC quirk? */
376 	if (id > 1000)
377 		id = id - 1000;
378 	return g_strdup_printf ("%d", id);
379 }
380 
381 TotemPlParserResult
totem_pl_parser_add_m3u(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)382 totem_pl_parser_add_m3u (TotemPlParser *parser,
383 			 GFile *file,
384 			 GFile *base_file,
385 			 TotemPlParseData *parse_data,
386 			 gpointer data)
387 {
388 	TotemPlParserResult retval = TOTEM_PL_PARSER_RESULT_UNHANDLED;
389 	char *contents, **lines;
390 	gsize size;
391 	guint i, num_lines;
392 	gboolean dos_mode = FALSE;
393 	const char *extinfo, *extvlcopt_audiotrack;
394 	char *pl_uri;
395 
396 	if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE) {
397 		DEBUG (file, g_print ("Failed to load '%s'\n", uri));
398 		return TOTEM_PL_PARSER_RESULT_ERROR;
399 	}
400 
401 	/* .pls files with a .m3u extension, the nasties */
402 	if (g_str_has_prefix (contents, "[playlist]") != FALSE
403 			|| g_str_has_prefix (contents, "[Playlist]") != FALSE
404 			|| g_str_has_prefix (contents, "[PLAYLIST]") != FALSE) {
405 		DEBUG (file, g_print ("Parsing '%s' playlist as PLS\n", uri));
406 		retval = totem_pl_parser_add_pls_with_contents (parser, file, base_file, contents, parse_data);
407 		g_free (contents);
408 		return retval;
409 	}
410 
411 	if (strstr (contents, EXTINF_HLS) ||
412 	    strstr (contents, EXTINF_HLS2)) {
413 		DEBUG (file, g_print ("Unhandled HLS playlist '%s', should be passed to player\n", uri));
414 		g_free (contents);
415 		return retval;
416 	}
417 
418 	/* Try to use ISO-8859-1 if we don't have valid UTF-8,
419 	 * try to parse anyway if it's not ISO-8859-1 */
420 	if (g_utf8_validate (contents, -1, NULL) == FALSE) {
421 		char *fixed;
422 		fixed = g_convert (contents, -1, "UTF-8", "ISO8859-1", NULL, NULL, NULL);
423 		if (fixed != NULL) {
424 			g_free (contents);
425 			contents = fixed;
426 		}
427 	}
428 
429 	/* is non-NULL if there's an EXTINF on a preceding line */
430 	extinfo = NULL;
431 	extvlcopt_audiotrack = NULL;
432 
433 	/* figure out whether we're a unix m3u or dos m3u */
434 	if (strstr(contents, "\x0d")) {
435 		dos_mode = TRUE;
436 	}
437 
438 	lines = g_strsplit_set (contents, "\r\n", 0);
439 	g_free (contents);
440 	num_lines = g_strv_length (lines);
441 	/* We don't count the terminating NULL */
442 	num_lines--;
443 
444 	/* Send out the playlist start and get crackin' */
445 	pl_uri = g_file_get_uri (file);
446 	totem_pl_parser_add_uri (parser,
447 				 TOTEM_PL_PARSER_FIELD_IS_PLAYLIST, TRUE,
448 				 TOTEM_PL_PARSER_FIELD_URI, pl_uri,
449 				 TOTEM_PL_PARSER_FIELD_CONTENT_TYPE, "audio/x-mpegurl",
450 				 NULL);
451 
452 	for (i = 0; lines[i] != NULL; i++) {
453 		const char *line;
454 		char *length;
455 		gint64 length_num = 0;
456 		char *audio_track;
457 
458 		line = lines[i];
459 
460 		if (line[0] == '\0')
461 			continue;
462 
463 		retval = TOTEM_PL_PARSER_RESULT_SUCCESS;
464 
465 		/* Ignore leading spaces */
466 		for (; g_ascii_isspace (line[0]); line++)
467 			;
468 
469 		/* Ignore comments, but mark it if we have extra info */
470 		if (line[0] == '#') {
471 			if (extinfo == NULL && g_str_has_prefix (line, EXTINF) != FALSE)
472 				extinfo = line;
473 			if (extvlcopt_audiotrack == NULL && g_str_has_prefix (line, EXTVLCOPT_AUDIOTRACK) != FALSE)
474 				extvlcopt_audiotrack = line;
475 			continue;
476 		}
477 
478 		length = totem_pl_parser_get_extinfo_length (extinfo);
479 		if (length != NULL)
480 			length_num = totem_pl_parser_parse_duration (length, totem_pl_parser_is_debugging_enabled (parser));
481 		g_free (length);
482 
483 		audio_track = totem_pl_parser_get_extvlcopt_audio_track (extvlcopt_audiotrack);
484 
485 		/* Either it's a URI, or it has a proper path ... */
486 		if (strstr(line, "://") != NULL
487 				|| line[0] == G_DIR_SEPARATOR) {
488 			GFile *uri;
489 
490 			uri = g_file_new_for_commandline_arg (line);
491 			if (length_num < 0 ||
492 			    totem_pl_parser_parse_internal (parser, uri, NULL, parse_data) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
493 				totem_pl_parser_add_uri (parser,
494 							 TOTEM_PL_PARSER_FIELD_URI, line,
495 							 TOTEM_PL_PARSER_FIELD_TITLE, totem_pl_parser_get_extinfo_title (extinfo),
496 							 TOTEM_PL_PARSER_FIELD_AUDIO_TRACK, audio_track,
497 							 NULL);
498 			}
499 			g_object_unref (uri);
500 		} else if (g_ascii_isalpha (line[0]) != FALSE
501 			   && g_str_has_prefix (line + 1, ":\\")) {
502 			/* Path relative to a drive on Windows, we need to use
503 			 * the base that was passed to us */
504 			GFile *uri;
505 
506 			lines[i] = g_strdelimit (lines[i], "\\", '/');
507 			/* + 2, skip drive letter */
508 			uri = g_file_get_child (base_file, line + 2);
509 			totem_pl_parser_add_uri (parser,
510 						 TOTEM_PL_PARSER_FIELD_FILE, uri,
511 						 TOTEM_PL_PARSER_FIELD_TITLE, totem_pl_parser_get_extinfo_title (extinfo),
512 						 TOTEM_PL_PARSER_FIELD_AUDIO_TRACK, audio_track,
513 						 NULL);
514 			g_object_unref (uri);
515 		} else if (line[0] == '\\' && line[1] == '\\') {
516 			/* ... Or it's in the windows smb form
517 			 * (\\machine\share\filename), Note drive names
518 			 * (C:\ D:\ etc) are unhandled (unknown base for
519 			 * drive letters) */
520 		        char *tmpuri;
521 
522 			lines[i] = g_strdelimit (lines[i], "\\", '/');
523 			tmpuri = g_strjoin (NULL, "smb:", line, NULL);
524 
525 			totem_pl_parser_add_uri (parser,
526 						 TOTEM_PL_PARSER_FIELD_URI, tmpuri,
527 						 TOTEM_PL_PARSER_FIELD_TITLE, totem_pl_parser_get_extinfo_title (extinfo),
528 						 TOTEM_PL_PARSER_FIELD_AUDIO_TRACK, audio_track,
529 						 NULL);
530 
531 			g_free (tmpuri);
532 		} else {
533 			/* Try with a base */
534 			GFile *uri, *_base_file;
535 			char sep;
536 
537 			_base_file = g_file_get_parent (file);
538 			sep = (dos_mode ? '\\' : '/');
539 			if (sep == '\\')
540 				lines[i] = g_strdelimit (lines[i], "\\", '/');
541 			uri = g_file_get_child (_base_file, line);
542 			g_object_unref (_base_file);
543 			totem_pl_parser_add_uri (parser,
544 						 TOTEM_PL_PARSER_FIELD_FILE, uri,
545 						 TOTEM_PL_PARSER_FIELD_TITLE, totem_pl_parser_get_extinfo_title (extinfo),
546 						 TOTEM_PL_PARSER_FIELD_AUDIO_TRACK, audio_track,
547 						 NULL);
548 			g_object_unref (uri);
549 		}
550 		extinfo = NULL;
551 		extvlcopt_audiotrack = NULL;
552 
553 		g_free (audio_track);
554 	}
555 
556 	g_strfreev (lines);
557 
558 	totem_pl_parser_playlist_end (parser, pl_uri);
559 	g_free (pl_uri);
560 
561 	return retval;
562 }
563 
564 TotemPlParserResult
totem_pl_parser_add_m4u(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)565 totem_pl_parser_add_m4u (TotemPlParser *parser,
566 			 GFile *file,
567 			 GFile *base_file,
568 			 TotemPlParseData *parse_data,
569 			 gpointer data)
570 {
571 	return totem_pl_parser_add_m3u (parser, file,
572 					base_file, parse_data, data);
573 }
574 
575 TotemPlParserResult
totem_pl_parser_add_ra(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)576 totem_pl_parser_add_ra (TotemPlParser *parser,
577 			GFile *file,
578 			GFile *base_file,
579 			TotemPlParseData *parse_data,
580 			gpointer data)
581 {
582 	if (data == NULL || totem_pl_parser_is_uri_list (data, strlen (data)) == NULL) {
583 		totem_pl_parser_add_one_file (parser, file, NULL);
584 		return TOTEM_PL_PARSER_RESULT_SUCCESS;
585 	}
586 
587 	return totem_pl_parser_add_ram (parser, file, parse_data, NULL);
588 }
589 
590 #endif /* !TOTEM_PL_PARSER_MINI */
591 
592 #define CHECK_LEN if (i >= len) { return NULL; }
593 
594 const char *
totem_pl_parser_is_uri_list(const char * data,gsize len)595 totem_pl_parser_is_uri_list (const char *data, gsize len)
596 {
597 	guint i = 0;
598 
599 	/* Find the first bits of text */
600 	while (data[i] == '\n' || data[i] == '\t' || data[i] == ' ') {
601 		i++;
602 		CHECK_LEN;
603 	}
604 	CHECK_LEN;
605 
606 	/* scheme always starts with a letter */
607 	if (g_ascii_isalpha (data[i]) == FALSE)
608 		return FALSE;
609 	while (g_ascii_isalnum (data[i]) != FALSE) {
610 		i++;
611 		CHECK_LEN;
612 	}
613 
614 	CHECK_LEN;
615 
616 	/* First non-alphanum character should be a ':' */
617 	if (data[i] != ':')
618 		return FALSE;
619 	i++;
620 	CHECK_LEN;
621 
622 	if (data[i] != '/')
623 		return NULL;
624 	i++;
625 	CHECK_LEN;
626 
627 	if (data[i] != '/')
628 		return NULL;
629 
630 	return TEXT_URI_TYPE;
631 }
632 
633