1 /*
2  * MOC - music on console
3  * Copyright (C) 2004 Damian Pietras <daper@daper.net>
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  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 
16 #include <stdio.h>
17 #include <assert.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 
25 #ifdef HAVE_LIBMAGIC
26 #include <magic.h>
27 #include <pthread.h>
28 #endif
29 
30 /* Include dirent for various systems */
31 #ifdef HAVE_DIRENT_H
32 # include <dirent.h>
33 #else
34 # define dirent direct
35 # if HAVE_SYS_NDIR_H
36 #  include <sys/ndir.h>
37 # endif
38 #endif
39 
40 #define DEBUG
41 
42 #include "common.h"
43 #include "playlist.h"
44 #include "lists.h"
45 #include "interface.h"
46 #include "decoder.h"
47 #include "options.h"
48 #include "files.h"
49 #include "playlist_file.h"
50 #include "log.h"
51 #include "utf8.h"
52 
53 #define READ_LINE_INIT_SIZE	256
54 
55 #ifdef HAVE_LIBMAGIC
56 static magic_t cookie = NULL;
57 static char *cached_file = NULL;
58 static char *cached_result = NULL;
59 #endif
60 
files_init()61 void files_init ()
62 {
63 #ifdef HAVE_LIBMAGIC
64 	assert (cookie == NULL);
65 
66 	cookie = magic_open (MAGIC_SYMLINK | MAGIC_MIME | MAGIC_ERROR |
67 	                     MAGIC_NO_CHECK_COMPRESS | MAGIC_NO_CHECK_ELF |
68 	                     MAGIC_NO_CHECK_TAR | MAGIC_NO_CHECK_TOKENS |
69 	                     MAGIC_NO_CHECK_FORTRAN | MAGIC_NO_CHECK_TROFF);
70 	if (cookie == NULL)
71 		logit ("Error allocating magic cookie: %s", strerror (errno));
72 	else if (magic_load (cookie, NULL) != 0) {
73 		logit ("Error loading magic database: %s", magic_error (cookie));
74 		magic_close (cookie);
75 		cookie = NULL;
76 	}
77 #endif
78 }
79 
files_cleanup()80 void files_cleanup ()
81 {
82 #ifdef HAVE_LIBMAGIC
83 	free (cached_file);
84 	cached_file = NULL;
85 	free (cached_result);
86 	cached_result = NULL;
87 	magic_close (cookie);
88 	cookie = NULL;
89 #endif
90 }
91 
92 /* Is the string a URL? */
is_url(const char * str)93 inline int is_url (const char *str)
94 {
95 	return !strncasecmp (str, "http://", sizeof ("http://") - 1)
96 		|| !strncasecmp (str, "ftp://", sizeof ("ftp://") - 1);
97 }
98 
99 /* Return 1 if the file is a directory, 0 if not, -1 on error. */
is_dir(const char * file)100 int is_dir (const char *file)
101 {
102 	struct stat file_stat;
103 
104 	if (is_url (file))
105 		return 0;
106 
107 	if (stat(file, &file_stat) == -1) {
108 		error ("Can't stat %s: %s", file, strerror(errno));
109 		return -1;
110 	}
111 	return S_ISDIR(file_stat.st_mode) ? 1 : 0;
112 }
113 
114 /* Return 1 if the file can be read by this user, 0 if not */
can_read_file(const char * file)115 int can_read_file (const char *file)
116 {
117 	return access(file, R_OK) == 0;
118 }
119 
file_type(const char * file)120 enum file_type file_type (const char *file)
121 {
122 	struct stat file_stat;
123 
124 	assert (file != NULL);
125 
126 	if (is_url(file))
127 		return F_URL;
128 	if (stat(file, &file_stat) == -1)
129 		return F_OTHER; /* Ignore the file if stat() failed */
130 	if (S_ISDIR(file_stat.st_mode))
131 		return F_DIR;
132 	if (is_sound_file(file))
133 		return F_SOUND;
134 	if (is_plist_file(file))
135 		return F_PLAYLIST;
136 	return F_OTHER;
137 }
138 
139 /* Given a file name, return the mime type or NULL. */
file_mime_type(const char * file ATTR_UNUSED)140 char *file_mime_type (const char *file ATTR_UNUSED)
141 {
142 	char *result = NULL;
143 
144 	assert (file != NULL);
145 
146 #ifdef HAVE_LIBMAGIC
147 	static pthread_mutex_t magic_mutex = PTHREAD_MUTEX_INITIALIZER;
148 
149 	if (cookie != NULL) {
150 		LOCK(magic_mutex);
151 		if (cached_file && !strcmp (cached_file, file))
152 			result = xstrdup (cached_result);
153 		else {
154 			free (cached_file);
155 			free (cached_result);
156 			cached_file = cached_result = NULL;
157 			result = xstrdup (magic_file (cookie, file));
158 			if (result == NULL)
159 				logit ("Error interrogating file: %s", magic_error (cookie));
160 			else {
161 				cached_file = xstrdup (file);
162 				cached_result = xstrdup (result);
163 			}
164 		}
165 		UNLOCK(magic_mutex);
166 	}
167 #endif
168 
169 	return result;
170 }
171 
172 /* Make a title from the file name for the item.  If hide_extn != 0,
173  * strip the file name from extension. */
make_file_title(struct plist * plist,const int num,const int hide_extension)174 void make_file_title (struct plist *plist, const int num,
175 		const int hide_extension)
176 {
177 	assert (plist != NULL);
178 	assert (LIMIT(num, plist->num));
179 	assert (!plist_deleted (plist, num));
180 
181 	if (file_type (plist->items[num].file) != F_URL) {
182 		char *file = xstrdup (plist->items[num].file);
183 
184 		if (hide_extension) {
185 			char *extn;
186 
187 			extn = ext_pos (file);
188 			if (extn)
189 				*(extn - 1) = 0;
190 		}
191 
192 		if (options_get_int ("FileNamesIconv"))
193 		{
194 			char *old_title = file;
195 			file = files_iconv_str (file);
196 			free (old_title);
197 		}
198 
199 		plist_set_title_file (plist, num, file);
200 		free (file);
201 	}
202 	else
203 		plist_set_title_file (plist, num, plist->items[num].file);
204 }
205 
206 /* Make a title from the tags for the item. */
make_tags_title(struct plist * plist,const int num)207 void make_tags_title (struct plist *plist, const int num)
208 {
209 	int hide_extn;
210 	char *title;
211 
212 	assert (plist != NULL);
213 	assert (LIMIT(num, plist->num));
214 	assert (!plist_deleted (plist, num));
215 
216 	if (file_type (plist->items[num].file) == F_URL) {
217 		make_file_title (plist, num, 0);
218 		return;
219 	}
220 
221 	if (plist->items[num].title_tags)
222 		return;
223 
224 	assert (plist->items[num].file != NULL);
225 
226 	if (plist->items[num].tags->title) {
227 		title = build_title (plist->items[num].tags);
228 		plist_set_title_tags (plist, num, title);
229 		free (title);
230 		return;
231 	}
232 
233 	hide_extn = options_get_int ("HideFileExtension");
234 	make_file_title (plist, num, hide_extn);
235 }
236 
237 /* Switch playlist titles to title_file */
switch_titles_file(struct plist * plist)238 void switch_titles_file (struct plist *plist)
239 {
240 	int i, hide_extn;
241 
242 	hide_extn = options_get_int ("HideFileExtension");
243 
244 	for (i = 0; i < plist->num; i++) {
245 		if (plist_deleted (plist, i))
246 			continue;
247 
248 		if (!plist->items[i].title_file)
249 			make_file_title (plist, i, hide_extn);
250 
251 		assert (plist->items[i].title_file != NULL);
252 	}
253 }
254 
255 /* Switch playlist titles to title_tags */
switch_titles_tags(struct plist * plist)256 void switch_titles_tags (struct plist *plist)
257 {
258 	int i, hide_extn;
259 
260 	hide_extn = options_get_int ("HideFileExtension");
261 
262 	for (i = 0; i < plist->num; i++) {
263 		if (plist_deleted (plist, i))
264 			continue;
265 
266 		if (!plist->items[i].title_tags && !plist->items[i].title_file)
267 			make_file_title (plist, i, hide_extn);
268 	}
269 }
270 
271 /* Add file to the directory path in buf resolving '../' and removing './'. */
272 /* buf must be absolute path. */
resolve_path(char * buf,const int size,const char * file)273 void resolve_path (char *buf, const int size, const char *file)
274 {
275 	char *f; /* points to the char in *file we process */
276 	char path[2*PATH_MAX]; /* temporary path */
277 	int len = 0; /* number of characters in the buffer */
278 
279 	assert (buf[0] == '/');
280 
281 	if (snprintf(path, sizeof(path), "%s/%s/", buf, file)
282 			>= (int)sizeof(path))
283 		fatal ("Path too long!");
284 
285 	f = path;
286 	while (*f) {
287 		if (!strncmp(f, "/../", 4)) {
288 			char *slash = strrchr (buf, '/');
289 
290 			assert (slash != NULL);
291 
292 			if (slash == buf) {
293 
294 				/* make '/' from '/directory' */
295 				buf[1] = 0;
296 				len = 1;
297 			}
298 			else {
299 
300 				/* strip one element */
301 				*(slash) = 0;
302 				len -= len - (slash - buf);
303 				buf[len] = 0;
304 			}
305 
306 			f+= 3;
307 		}
308 		else if (!strncmp(f, "/./", 3))
309 
310 			/* skip '/.' */
311 			f += 2;
312 		else if (!strncmp(f, "//", 2))
313 
314 			/* remove double slash */
315 			f++;
316 		else if (len == size - 1)
317 			fatal ("Path too long!");
318 		else  {
319 			buf[len++] = *(f++);
320 			buf[len] = 0;
321 		}
322 	}
323 
324 	/* remove dot from '/dir/.' */
325 	if (len >= 2 && buf[len-1] == '.' && buf[len-2] == '/')
326 		buf[--len] = 0;
327 
328 	/* strip trailing slash */
329 	if (len > 1 && buf[len-1] == '/')
330 		buf[--len] = 0;
331 }
332 
333 /* Read selected tags for a file into tags structure (or create it if NULL).
334  * If some tags are already present, don't read them.
335  * If present_tags is NULL, allocate new tags. */
read_file_tags(const char * file,struct file_tags * tags,const int tags_sel)336 struct file_tags *read_file_tags (const char *file,
337 		struct file_tags *tags, const int tags_sel)
338 {
339 	struct decoder *df;
340 	int needed_tags;
341 
342 	assert (file != NULL);
343 
344 	if (tags == NULL)
345 		tags = tags_new ();
346 
347 	if (file_type (file) == F_URL)
348 		return tags;
349 
350 	needed_tags = ~tags->filled & tags_sel;
351 	if (!needed_tags) {
352 		debug ("No need to read any tags");
353 		return tags;
354 	}
355 
356 	df = get_decoder (file);
357 	if (!df) {
358 		logit ("Can't find decoder functions for %s", file);
359 		return tags;
360 	}
361 
362 	/* This makes sure that we don't cause a memory leak */
363 	assert (!((needed_tags & TAGS_COMMENTS) &&
364 	          (tags->title || tags->artist || tags->album)));
365 
366 	df->info (file, tags, needed_tags);
367 	tags->filled |= tags_sel;
368 
369 	return tags;
370 }
371 
372 /* Read the content of the directory, make an array of absolute paths for
373  * all recognized files. Put directories, playlists and sound files
374  * in proper structures. Return 0 on error.*/
read_directory(const char * directory,lists_t_strs * dirs,lists_t_strs * playlists,struct plist * plist)375 int read_directory (const char *directory, lists_t_strs *dirs,
376 		lists_t_strs *playlists, struct plist *plist)
377 {
378 	DIR *dir;
379 	struct dirent *entry;
380 	int show_hidden = options_get_int ("ShowHiddenFiles");
381 	int dir_is_root;
382 
383 	assert (directory != NULL);
384 	assert (*directory == '/');
385 	assert (dirs != NULL);
386 	assert (playlists != NULL);
387 	assert (plist != NULL);
388 
389 	if (!(dir = opendir(directory))) {
390 		error ("Can't read directory: %s", strerror(errno));
391 		return 0;
392 	}
393 
394 	if (!strcmp(directory, "/"))
395 		dir_is_root = 1;
396 	else
397 		dir_is_root = 0;
398 
399 	while ((entry = readdir(dir))) {
400 		char file[PATH_MAX];
401 		enum file_type type;
402 
403 		if (user_wants_interrupt()) {
404 			error ("Interrupted! Not all files read!");
405 			break;
406 		}
407 
408 		if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
409 			continue;
410 		if (!show_hidden && entry->d_name[0] == '.')
411 			continue;
412 		if (snprintf(file, sizeof(file), "%s/%s", dir_is_root ?
413 					"" : directory,	entry->d_name)
414 				>= (int)sizeof(file)) {
415 			error ("Path too long!");
416 			closedir (dir);
417 			return 0;
418 		}
419 		type = file_type (file);
420 		if (type == F_SOUND)
421 			plist_add (plist, file);
422 		else if (type == F_DIR)
423 			lists_strs_append (dirs, file);
424 		else if (type == F_PLAYLIST)
425 			lists_strs_append (playlists, file);
426 	}
427 
428 	closedir (dir);
429 
430 	return 1;
431 }
432 
dir_symlink_loop(const ino_t inode_no,const ino_t * dir_stack,const int depth)433 static int dir_symlink_loop (const ino_t inode_no, const ino_t *dir_stack,
434 		const int depth)
435 {
436 	int i;
437 
438 	for (i = 0; i < depth; i++)
439 		if (dir_stack[i] == inode_no)
440 			return 1;
441 
442 	return 0;
443 }
444 
445 /* Recursively add files from the directory to the playlist.
446  * Return 1 if OK (and even some errors), 0 if the user interrupted. */
read_directory_recurr_internal(const char * directory,struct plist * plist,ino_t ** dir_stack,int * depth)447 static int read_directory_recurr_internal (const char *directory, struct plist *plist,
448 		ino_t **dir_stack, int *depth)
449 {
450 	DIR *dir;
451 	struct dirent *entry;
452 	struct stat st;
453 
454 	if (stat(directory, &st)) {
455 		error ("Can't stat %s: %s", directory, strerror(errno));
456 		return 0;
457 	}
458 
459 	assert (plist != NULL);
460 	assert (directory != NULL);
461 
462 	if (*dir_stack && dir_symlink_loop(st.st_ino, *dir_stack, *depth)) {
463 		logit ("Detected symlink loop on %s", directory);
464 		return 1;
465 	}
466 
467 	if (!(dir = opendir(directory))) {
468 		error ("Can't read directory: %s", strerror(errno));
469 		return 1;
470 	}
471 
472 	(*depth)++;
473 	*dir_stack = (ino_t *)xrealloc (*dir_stack, sizeof(ino_t) * (*depth));
474 	(*dir_stack)[*depth - 1] = st.st_ino;
475 
476 	while ((entry = readdir(dir))) {
477 		char file[PATH_MAX];
478 		enum file_type type;
479 
480 		if (user_wants_interrupt()) {
481 			error ("Interrupted! Not all files read!");
482 			break;
483 		}
484 
485 		if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
486 			continue;
487 		if (snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name)
488 				>= (int)sizeof(file)) {
489 			error ("Path too long!");
490 			continue;
491 		}
492 		type = file_type (file);
493 		if (type == F_DIR)
494 			read_directory_recurr_internal(file, plist, dir_stack, depth);
495 		else if (type == F_SOUND && plist_find_fname(plist, file) == -1)
496 			plist_add (plist, file);
497 	}
498 
499 	(*depth)--;
500 	*dir_stack = (ino_t *)xrealloc (*dir_stack, sizeof(ino_t) * (*depth));
501 
502 	closedir (dir);
503 	return 1;
504 }
505 
read_directory_recurr(const char * directory,struct plist * plist)506 int read_directory_recurr (const char *directory, struct plist *plist)
507 {
508 	int ret;
509 	int depth = 0;
510 	ino_t *dir_stack = NULL;
511 
512 	ret = read_directory_recurr_internal (directory, plist, &dir_stack,
513 			&depth);
514 
515 	if (dir_stack)
516 		free (dir_stack);
517 
518 	return ret;
519 }
520 
521 /* Return the file extension position or NULL if the file has no extension. */
ext_pos(const char * file)522 char *ext_pos (const char *file)
523 {
524 	char *ext = strrchr (file, '.');
525 	char *slash = strrchr (file, '/');
526 
527 	/* don't treat dot in ./file or /.file as a dot before extension */
528 	if (ext && (!slash || slash < ext) && ext != file && *(ext-1) != '/')
529 		ext++;
530 	else
531 		ext = NULL;
532 
533 	return ext;
534 }
535 
536 /* Read one line from a file, strip trailing end of line chars.
537  * Returned memory is malloc()ed.  Return NULL on error or EOF. */
read_line(FILE * file)538 char *read_line (FILE *file)
539 {
540 	int line_alloc = READ_LINE_INIT_SIZE;
541 	int len = 0;
542 	char *line = (char *)xmalloc (sizeof(char) * line_alloc);
543 
544 	while (1) {
545 		if (!fgets(line + len, line_alloc - len, file))
546 			break;
547 		len = strlen(line);
548 
549 		if (line[len-1] == '\n')
550 			break;
551 
552 		/* If we are here, it means that line is longer than the buffer. */
553 		line_alloc *= 2;
554 		line = (char *)xrealloc (line, sizeof(char) * line_alloc);
555 	}
556 
557 	if (len == 0) {
558 		free (line);
559 		return NULL;
560 	}
561 
562 	if (line[len-1] == '\n')
563 		line[--len] = 0;
564 	if (len > 0 && line[len-1] == '\r')
565 		line[--len] = 0;
566 
567 	return line;
568 }
569 
570 /* Return malloc()ed string in form "base/name". */
add_dir_file(const char * base,const char * name)571 static char *add_dir_file (const char *base, const char *name)
572 {
573 	char *path;
574 	int base_is_root;
575 
576 	base_is_root = !strcmp (base, "/") ? 1 : 0;
577 	path = (char *)xmalloc (sizeof(char) *
578 			(strlen(base) + strlen(name) + 2));
579 
580 	sprintf (path, "%s/%s", base_is_root ? "" : base, name);
581 
582 	return path;
583 }
584 
585 /* Find directories having a prefix of 'pattern'.
586  * - If there are no matches, NULL is returned.
587  * - If there is one such directory, it is returned with a trailing '/'.
588  * - Otherwise the longest common prefix is returned (with no trailing '/').
589  * (This is used for directory auto-completion.)
590  * Returned memory is malloc()ed.
591  * 'pattern' is temporarily modified! */
find_match_dir(char * pattern)592 char *find_match_dir (char *pattern)
593 {
594 	char *slash;
595 	DIR *dir;
596 	struct dirent *entry;
597 	int name_len;
598 	char *name;
599 	char *matching_dir = NULL;
600 	char *search_dir;
601 	int unambiguous = 1;
602 
603 	if (!pattern[0])
604 		return NULL;
605 
606 	/* strip the last directory */
607 	slash = strrchr (pattern, '/');
608 	if (!slash)
609 		return NULL;
610 	if (slash == pattern) {
611 		/* only '/dir' */
612 		search_dir = xstrdup ("/");
613 	}
614 	else {
615 		*slash = 0;
616 		search_dir = xstrdup (pattern);
617 		*slash = '/';
618 	}
619 
620 	name = slash + 1;
621 	name_len = strlen (name);
622 
623 	if (!(dir = opendir(search_dir)))
624 		return NULL;
625 
626 	while ((entry = readdir(dir))) {
627 		if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")
628 				&& !strncmp(entry->d_name, name, name_len)) {
629 			char *path = add_dir_file (search_dir, entry->d_name);
630 
631 			if (is_dir(path) == 1) {
632 				if (matching_dir) {
633 
634 					/* More matching directories - strip
635 					 * matching_dir to the part that is
636 					 * common to both paths */
637 					int i = 0;
638 
639 					while (matching_dir[i] == path[i]
640 							&& path[i])
641 						i++;
642 					matching_dir[i] = 0;
643 					free (path);
644 					unambiguous = 0;
645 				}
646 				else
647 					matching_dir = path;
648 			}
649 			else
650 				free (path);
651 		}
652 	}
653 
654 	closedir (dir);
655 	free (search_dir);
656 
657 	if (matching_dir && unambiguous) {
658 		matching_dir = (char *)xrealloc (matching_dir,
659 				sizeof(char) * (strlen(matching_dir) + 2));
660 		strcat (matching_dir, "/");
661 	}
662 
663 	return matching_dir;
664 }
665 
666 /* Return != 0 if the file exists. */
file_exists(const char * file)667 int file_exists (const char *file)
668 {
669 	struct stat file_stat;
670 
671 	if (!stat(file, &file_stat))
672 		return 1;
673 
674 	/* Log any error other than non-existence. */
675 	if (errno != ENOENT)
676 		logit ("Error : %s", strerror (errno));
677 
678 	return 0;
679 }
680 
681 /* Get the modification time of a file. Return (time_t)-1 on error */
get_mtime(const char * file)682 time_t get_mtime (const char *file)
683 {
684 	struct stat stat_buf;
685 
686 	if (stat(file, &stat_buf) != -1)
687 		return stat_buf.st_mtime;
688 
689 	return (time_t)-1;
690 }
691 
692 /* Convert file path to absolute path;
693  * resulting string is allocated and must be freed afterwards. */
absolute_path(const char * path,const char * cwd)694 char *absolute_path (const char *path, const char *cwd)
695 {
696 	char tmp[2*PATH_MAX];
697 	char *result;
698 
699 	assert (path);
700 	assert (cwd);
701 
702 	if(path[0] != '/' && !is_url(path)) {
703 		strncpy (tmp, cwd, sizeof(tmp));
704 		tmp[sizeof(tmp)-1] = 0;
705 
706 		resolve_path (tmp, sizeof(tmp), path);
707 
708 		result = (char *)xmalloc (sizeof(char) * (strlen(tmp)+1));
709 		strcpy (result, tmp);
710 	}
711 	else {
712 		result = (char *)xmalloc (sizeof(char) * (strlen(path)+1));
713 		strcpy (result, path);
714 	}
715 
716 	return result;
717 }
718 
719 /* Check that a file which may cause other applications to be invoked
720  * is secure against tampering. */
is_secure(const char * file)721 bool is_secure (const char *file)
722 {
723     struct stat sb;
724 
725 	assert (file && file[0]);
726 
727 	if (stat (file, &sb) == -1)
728 		return true;
729 	if (!S_ISREG(sb.st_mode))
730 		return false;
731 	if (sb.st_mode & (S_IWGRP|S_IWOTH))
732 		return false;
733 	if (sb.st_uid != 0 && sb.st_uid != geteuid ())
734 		return false;
735 
736 	return true;
737 }
738