1/* MiniDLNA media server
2 * Copyright (C) 2008-2009  Justin Maggard
3 *
4 * This file is part of MiniDLNA.
5 *
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * MiniDLNA 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
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 "upnpglobalvars.h"
32#include "log.h"
33
34inline int
35strcatf(struct string_s *str, const char *fmt, ...)
36{
37	int ret;
38	va_list ap;
39
40	va_start(ap, fmt);
41	ret = vsnprintf(str->data + str->off, str->size - str->off, fmt, ap);
42	str->off += ret;
43	va_end(ap);
44
45	return ret;
46}
47
48inline void
49strncpyt(char *dst, const char *src, size_t len)
50{
51	strncpy(dst, src, len);
52	dst[len-1] = '\0';
53}
54
55int
56ends_with(const char * haystack, const char * needle)
57{
58	const char * end;
59	int nlen = strlen(needle);
60	int hlen = strlen(haystack);
61
62	if( nlen > hlen )
63		return 0;
64 	end = haystack + hlen - nlen;
65
66	return (strcasecmp(end, needle) ? 0 : 1);
67}
68
69char *
70trim(char *str)
71{
72	int i;
73	int len;
74
75	if (!str)
76		return(NULL);
77
78	len = strlen(str);
79	for (i=len-1; i >= 0 && isspace(str[i]); i--)
80	{
81		str[i] = '\0';
82		len--;
83	}
84	while (isspace(*str))
85	{
86		str++;
87		len--;
88	}
89
90	if (str[0] == '"' && str[len-1] == '"')
91	{
92		str[0] = '\0';
93		str[len-1] = '\0';
94		str++;
95	}
96
97	return str;
98}
99
100/* Find the first occurrence of p in s, where s is terminated by t */
101char *
102strstrc(const char *s, const char *p, const char t)
103{
104	char *endptr;
105	size_t slen, plen;
106
107	endptr = strchr(s, t);
108	if (!endptr)
109		return strstr(s, p);
110
111	plen = strlen(p);
112	slen = endptr - s;
113	while (slen >= plen)
114	{
115		if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
116			return (char*)s;
117		s++;
118		slen--;
119	}
120
121	return NULL;
122}
123
124char *
125strcasestrc(const char *s, const char *p, const char t)
126{
127	char *endptr;
128	size_t slen, plen;
129
130	endptr = strchr(s, t);
131	if (!endptr)
132		return strcasestr(s, p);
133
134	plen = strlen(p);
135	slen = endptr - s;
136	while (slen >= plen)
137	{
138		if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0)
139			return (char*)s;
140		s++;
141		slen--;
142	}
143
144	return NULL;
145}
146
147char *
148modifyString(char * string, const char * before, const char * after, short like)
149{
150	int oldlen, newlen, chgcnt = 0;
151	char *s, *p, *t;
152
153	oldlen = strlen(before);
154	newlen = strlen(after);
155	if( newlen+like > oldlen )
156	{
157		s = string;
158		while( (p = strstr(s, before)) )
159		{
160			chgcnt++;
161			s = p+oldlen;
162		}
163		s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like);
164		/* If we failed to realloc, return the original alloc'd string */
165		if( s )
166			string = s;
167		else
168			return string;
169	}
170
171	s = string;
172	while( s )
173	{
174		p = strcasestr(s, before);
175		if( !p )
176			return string;
177		memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
178		memcpy(p, after, newlen);
179		if( like )
180		{
181			t = p+newlen;
182			while( isspace(*t) )
183				t++;
184			if( *t == '"' )
185			{
186				if( like == 2 )
187				{
188					memmove(t+2, t+1, strlen(t+1)+1);
189					*++t = '%';
190				}
191				while( *++t != '"' )
192					continue;
193				memmove(t+1, t, strlen(t)+1);
194				*t = '%';
195			}
196		}
197		s = p + newlen;
198	}
199
200	return string;
201}
202
203char *
204escape_tag(const char *tag, int force_alloc)
205{
206	char *esc_tag = NULL;
207
208	if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
209	{
210		esc_tag = strdup(tag);
211		esc_tag = modifyString(esc_tag, "&", "&amp;amp;", 0);
212		esc_tag = modifyString(esc_tag, "<", "&amp;lt;", 0);
213		esc_tag = modifyString(esc_tag, ">", "&amp;gt;", 0);
214		esc_tag = modifyString(esc_tag, "\"", "&amp;quot;", 0);
215	}
216	else if( force_alloc )
217		esc_tag = strdup(tag);
218
219	return esc_tag;
220}
221
222void
223strip_ext(char * name)
224{
225	char * period;
226
227	period = strrchr(name, '.');
228	if( period )
229		*period = '\0';
230}
231
232/* Code basically stolen from busybox */
233int
234make_dir(char * path, mode_t mode)
235{
236	char * s = path;
237	char c;
238	struct stat st;
239
240	do {
241		c = '\0';
242
243		/* Bypass leading non-'/'s and then subsequent '/'s. */
244		while (*s) {
245			if (*s == '/') {
246				do {
247					++s;
248				} while (*s == '/');
249				c = *s;     /* Save the current char */
250				*s = '\0';     /* and replace it with nul. */
251				break;
252			}
253			++s;
254		}
255
256		if (mkdir(path, mode) < 0) {
257			/* If we failed for any other reason than the directory
258			 * already exists, output a diagnostic and return -1.*/
259			if (errno != EEXIST || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
260				DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
261				if (c)
262					*s = c;
263				return -1;
264			}
265		}
266	        if (!c)
267			return 0;
268
269		/* Remove any inserted nul from the path. */
270		*s = c;
271
272	} while (1);
273}
274
275/* Simple, efficient hash function from Daniel J. Bernstein */
276unsigned int
277DJBHash(const char *str, int len)
278{
279	unsigned int hash = 5381;
280	unsigned int i = 0;
281
282	for(i = 0; i < len; str++, i++)
283	{
284		hash = ((hash << 5) + hash) + (*str);
285	}
286
287	return hash;
288}
289
290int
291is_video(const char * file)
292{
293	return (ends_with(file, ".mpg") || ends_with(file, ".mpeg")  ||
294		ends_with(file, ".avi") || ends_with(file, ".divx")  ||
295		ends_with(file, ".asf") || ends_with(file, ".wmv")   ||
296		ends_with(file, ".mp4") || ends_with(file, ".m4v")   ||
297		ends_with(file, ".mts") || ends_with(file, ".m2ts")  ||
298		ends_with(file, ".m2t") || ends_with(file, ".mkv")   ||
299		ends_with(file, ".vob") || ends_with(file, ".ts")    ||
300		ends_with(file, ".flv") || ends_with(file, ".xvid")  ||
301#ifdef TIVO_SUPPORT
302		ends_with(file, ".TiVo") ||
303#endif
304        ends_with(file, ".m2v") ||     /* Foxconn add by Hank to support .m2v, 2012/04/12 */
305		ends_with(file, ".mov") || ends_with(file, ".3gp"));
306}
307
308int
309is_audio(const char * file)
310{
311	return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
312		ends_with(file, ".wma") || ends_with(file, ".asf")  ||
313		ends_with(file, ".fla") || ends_with(file, ".flc")  ||
314		ends_with(file, ".m4a") || ends_with(file, ".aac")  ||
315		ends_with(file, ".mp4") || ends_with(file, ".m4p")  ||
316		ends_with(file, ".wav") || ends_with(file, ".ogg")  ||
317		ends_with(file, ".pcm") || ends_with(file, ".3gp"));
318}
319
320int
321is_image(const char * file)
322{
323	return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
324}
325
326int
327is_playlist(const char * file)
328{
329	return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
330}
331
332int
333is_album_art(const char * name)
334{
335	struct album_art_name_s * album_art_name;
336
337	/* Check if this file name matches one of the default album art names */
338	for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
339	{
340		if( album_art_name->wildcard )
341		{
342			if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
343				break;
344		}
345		else
346		{
347			if( strcmp(album_art_name->name, name) == 0 )
348				break;
349		}
350	}
351
352	return (album_art_name ? 1 : 0);
353}
354
355int
356resolve_unknown_type(const char * path, enum media_types dir_type)
357{
358	struct stat entry;
359	unsigned char type = TYPE_UNKNOWN;
360	char str_buf[PATH_MAX];
361	ssize_t len;
362
363	if( lstat(path, &entry) == 0 )
364	{
365		if( S_ISLNK(entry.st_mode) )
366		{
367			if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
368			{
369				str_buf[len] = '\0';
370				//DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
371				if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
372				{
373					DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
374					return type;
375				}
376			}
377			stat(path, &entry);
378		}
379
380		if( S_ISDIR(entry.st_mode) )
381		{
382			type = TYPE_DIR;
383		}
384		else if( S_ISREG(entry.st_mode) )
385		{
386			switch( dir_type )
387			{
388				case ALL_MEDIA:
389					if( is_image(path) ||
390					    is_audio(path) ||
391					    is_video(path) ||
392					    is_playlist(path) )
393						type = TYPE_FILE;
394					break;
395				case AUDIO_ONLY:
396					if( is_audio(path) ||
397					    is_playlist(path) )
398						type = TYPE_FILE;
399					break;
400				case VIDEO_ONLY:
401					if( is_video(path) )
402						type = TYPE_FILE;
403					break;
404				case IMAGES_ONLY:
405					if( is_image(path) )
406						type = TYPE_FILE;
407					break;
408				default:
409					break;
410			}
411		}
412	}
413	return type;
414}
415
416#if 1
417int is_disk_mounted()
418{
419  FILE *fp;
420  char buffer[128];
421  /* see if there is any pen drive mount */
422  system("df > /tmp/df");
423  fp=fopen("/tmp/df","r");
424
425  if(fp)
426  {
427      while(!feof(fp))
428      {
429          fgets(buffer,128,fp);
430          if(strncmp("/dev/sd",buffer,strlen("/dev/sd"))==0)
431          {
432              fclose(fp);
433              return 1;
434          }
435      }
436      fclose(fp);
437  }
438  return 0;
439
440}
441#endif
442
443
444