1/*  MiniDLNA media server
2 *  Copyright (C) 2008-2009  Justin Maggard
3 *
4 *  This program is free software; you can redistribute it and/or modify
5 *  it under the terms of the GNU General Public License as published by
6 *  the Free Software Foundation; either version 2 of the License, or
7 *  (at your option) any later version.
8 *
9 *  This program 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
12 *  GNU General Public License for more details.
13 *
14 *  You should have received a copy of the GNU General Public License
15 *  along with this program; if not, write to the Free Software
16 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18#include <stdio.h>
19#include <ctype.h>
20#include <string.h>
21#include <stdlib.h>
22#include <linux/limits.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <errno.h>
29
30#include "minidlnatypes.h"
31#include "log.h"
32
33int
34ends_with(const char * haystack, const char * needle)
35{
36	const char * end;
37	int nlen = strlen(needle);
38	int hlen = strlen(haystack);
39
40	if( nlen > hlen )
41		return 0;
42 	end = haystack + hlen - nlen;
43
44	return (strcasecmp(end, needle) ? 0 : 1);
45}
46
47char *
48trim(char *str)
49{
50        if (!str)
51                return(NULL);
52        int i;
53        for (i=0; i <= strlen(str) && (isspace(str[i]) || str[i] == '"'); i++) {
54		str++;
55	}
56        for (i=(strlen(str)-1); i >= 0 && (isspace(str[i]) || str[i] == '"'); i--) {
57                str[i] = '\0';
58        }
59        return str;
60}
61
62/* Find the first occurrence of p in s, where s is terminated by t */
63char *
64strstrc(const char *s, const char *p, const char t)
65{
66	char *endptr;
67	size_t slen, plen;
68
69	endptr = strchr(s, t);
70	if (!endptr)
71		return NULL;
72
73	plen = strlen(p);
74	slen = endptr - s;
75	while (slen >= plen)
76	{
77		if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
78			return (char*)s;
79		s++;
80		slen--;
81	}
82
83	return NULL;
84}
85
86char *
87modifyString(char * string, const char * before, const char * after, short like)
88{
89	int oldlen, newlen, chgcnt = 0;
90	char *s, *p, *t;
91
92	oldlen = strlen(before);
93	newlen = strlen(after);
94	if( newlen+like > oldlen )
95	{
96		s = string;
97		while( (p = strstr(s, before)) )
98		{
99			chgcnt++;
100			s = p+oldlen;
101		}
102		string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like);
103	}
104
105	s = string;
106	while( s )
107	{
108		p = strcasestr(s, before);
109		if( !p )
110			return string;
111		memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
112		memcpy(p, after, newlen);
113		if( like )
114		{
115			t = p+newlen;
116			while( isspace(*t) )
117				t++;
118			if( *t == '"' )
119			{
120				if( like == 2 )
121				{
122					memmove(t+2, t+1, strlen(t+1)+1);
123					*++t = '%';
124				}
125				while( *++t != '"' )
126					continue;
127				memmove(t+1, t, strlen(t)+1);
128				*t = '%';
129			}
130		}
131		s = p + newlen;
132	}
133
134	return string;
135}
136
137char *
138escape_tag(const char *tag)
139{
140	char *esc_tag = NULL;
141
142	if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') )
143	{
144		esc_tag = strdup(tag);
145		esc_tag = modifyString(esc_tag, "&", "&amp;amp;", 0);
146		esc_tag = modifyString(esc_tag, "<", "&amp;lt;", 0);
147		esc_tag = modifyString(esc_tag, ">", "&amp;gt;", 0);
148	}
149
150	return esc_tag;
151}
152
153void
154strip_ext(char * name)
155{
156	char * period;
157
158	period = strrchr(name, '.');
159	if( period )
160		*period = '\0';
161}
162
163/* Code basically stolen from busybox */
164int
165make_dir(char * path, mode_t mode)
166{
167	char * s = path;
168	char c;
169	struct stat st;
170
171	do {
172		c = 0;
173
174		/* Bypass leading non-'/'s and then subsequent '/'s. */
175		while (*s) {
176			if (*s == '/') {
177				do {
178					++s;
179				} while (*s == '/');
180				c = *s;     /* Save the current char */
181				*s = 0;     /* and replace it with nul. */
182				break;
183			}
184			++s;
185		}
186
187		if (mkdir(path, mode) < 0) {
188			/* If we failed for any other reason than the directory
189			 * already exists, output a diagnostic and return -1.*/
190			if (errno != EEXIST
191			    || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
192				break;
193			}
194		}
195	        if (!c)
196			return 0;
197
198		/* Remove any inserted nul from the path. */
199		*s = c;
200
201	} while (1);
202
203	DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
204	return -1;
205}
206
207int
208is_video(const char * file)
209{
210	return (ends_with(file, ".mpg") || ends_with(file, ".mpeg")  ||
211		ends_with(file, ".avi") || ends_with(file, ".divx")  ||
212		ends_with(file, ".asf") || ends_with(file, ".wmv")   ||
213		ends_with(file, ".mp4") || ends_with(file, ".m4v")   ||
214		ends_with(file, ".mts") || ends_with(file, ".m2ts")  ||
215		ends_with(file, ".m2t") || ends_with(file, ".mkv")   ||
216		ends_with(file, ".vob") || ends_with(file, ".ts")    ||
217		ends_with(file, ".flv") || ends_with(file, ".xvid")  ||
218#ifdef TIVO_SUPPORT
219		ends_with(file, ".TiVo") ||
220#endif
221		ends_with(file, ".mov") || ends_with(file, ".3gp"));
222}
223
224int
225is_audio(const char * file)
226{
227	return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
228		ends_with(file, ".wma") || ends_with(file, ".asf")  ||
229		ends_with(file, ".fla") || ends_with(file, ".flc")  ||
230		ends_with(file, ".m4a") || ends_with(file, ".aac")  ||
231		ends_with(file, ".mp4") || ends_with(file, ".m4p")  ||
232		ends_with(file, ".wav") || ends_with(file, ".ogg")  ||
233		ends_with(file, ".pcm") || ends_with(file, ".3gp"));
234}
235
236int
237is_image(const char * file)
238{
239	return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
240}
241
242int
243is_playlist(const char * file)
244{
245	return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
246}
247
248int
249resolve_unknown_type(const char * path, enum media_types dir_type)
250{
251	struct stat entry;
252	unsigned char type = TYPE_UNKNOWN;
253
254	if( stat(path, &entry) == 0 )
255	{
256		if( S_ISDIR(entry.st_mode) )
257		{
258			type = TYPE_DIR;
259		}
260		else if( S_ISREG(entry.st_mode) )
261		{
262			switch( dir_type )
263			{
264				case ALL_MEDIA:
265					if( is_image(path) ||
266					    is_audio(path) ||
267					    is_video(path) ||
268					    is_playlist(path) )
269						type = TYPE_FILE;
270					break;
271				case AUDIO_ONLY:
272					if( is_audio(path) ||
273					    is_playlist(path) )
274						type = TYPE_FILE;
275					break;
276				case VIDEO_ONLY:
277					if( is_video(path) )
278						type = TYPE_FILE;
279					break;
280				case IMAGES_ONLY:
281					if( is_image(path) )
282						type = TYPE_FILE;
283					break;
284				default:
285					break;
286			}
287		}
288	}
289	return type;
290}
291
292