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 "config.h"
19
20#include <stdio.h>
21#include <ctype.h>
22#include <string.h>
23#include <stdlib.h>
24#include <sys/param.h>
25#include <sys/stat.h>
26#include <unistd.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <limits.h>
30#include <fcntl.h>
31#include <errno.h>
32
33#include "minidlnatypes.h"
34#include "upnpglobalvars.h"
35#include "utils.h"
36#include "log.h"
37
38int
39xasprintf(char **strp, char *fmt, ...)
40{
41	va_list args;
42	int ret;
43
44	va_start(args, fmt);
45	ret = vasprintf(strp, fmt, args);
46	va_end(args);
47	if( ret < 0 )
48	{
49		DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n");
50		*strp = NULL;
51	}
52	return ret;
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, int noalloc)
149{
150	int oldlen, newlen, chgcnt = 0;
151	char *s, *p;
152
153	/* If there is no match, just return */
154	s = strstr(string, before);
155	if (!s)
156		return string;
157
158	oldlen = strlen(before);
159	newlen = strlen(after);
160	if (newlen > oldlen)
161	{
162		if (noalloc)
163			return string;
164
165		while ((p = strstr(s, before)))
166		{
167			chgcnt++;
168			s = p + oldlen;
169		}
170		s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
171		/* If we failed to realloc, return the original alloc'd string */
172		if( s )
173			string = s;
174		else
175			return string;
176	}
177
178	s = string;
179	while (s)
180	{
181		p = strstr(s, before);
182		if (!p)
183			return string;
184		memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
185		memcpy(p, after, newlen);
186		s = p + newlen;
187	}
188
189	return string;
190}
191
192char *
193unescape_tag(const char *tag, int force_alloc)
194{
195	char *esc_tag = NULL;
196
197	if (strchr(tag, '&') &&
198	    (strstr(tag, "&amp;") || strstr(tag, "&lt;") || strstr(tag, "&gt;") ||
199	     strstr(tag, "&quot;") || strstr(tag, "&apos;")))
200	{
201		esc_tag = strdup(tag);
202		esc_tag = modifyString(esc_tag, "&amp;", "&", 1);
203		esc_tag = modifyString(esc_tag, "&lt;", "<", 1);
204		esc_tag = modifyString(esc_tag, "&gt;", ">", 1);
205		esc_tag = modifyString(esc_tag, "&quot;", "\"", 1);
206		esc_tag = modifyString(esc_tag, "&apos;", "'", 1);
207	}
208	else if( force_alloc )
209		esc_tag = strdup(tag);
210
211	return esc_tag;
212}
213
214char *
215escape_tag(const char *tag, int force_alloc)
216{
217	char *esc_tag = NULL;
218
219	if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
220	{
221		esc_tag = strdup(tag);
222		esc_tag = modifyString(esc_tag, "&", "&amp;amp;", 0);
223		esc_tag = modifyString(esc_tag, "<", "&amp;lt;", 0);
224		esc_tag = modifyString(esc_tag, ">", "&amp;gt;", 0);
225		esc_tag = modifyString(esc_tag, "\"", "&amp;quot;", 0);
226	}
227	else if( force_alloc )
228		esc_tag = strdup(tag);
229
230	return esc_tag;
231}
232
233char *
234strip_ext(char *name)
235{
236	char *period;
237
238	period = strrchr(name, '.');
239	if (period)
240		*period = '\0';
241
242	return period;
243}
244
245/* Code basically stolen from busybox */
246int
247make_dir(char * path, mode_t mode)
248{
249	char * s = path;
250	char c;
251	struct stat st;
252
253	do {
254		c = '\0';
255
256		/* Before we do anything, skip leading /'s, so we don't bother
257		 * trying to create /. */
258		while (*s == '/')
259			++s;
260
261		/* Bypass leading non-'/'s and then subsequent '/'s. */
262		while (*s) {
263			if (*s == '/') {
264				do {
265					++s;
266				} while (*s == '/');
267				c = *s;     /* Save the current char */
268				*s = '\0';     /* and replace it with nul. */
269				break;
270			}
271			++s;
272		}
273
274		if (mkdir(path, mode) < 0) {
275			/* If we failed for any other reason than the directory
276			 * already exists, output a diagnostic and return -1.*/
277			if ((errno != EEXIST && errno != EISDIR)
278			    || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
279				DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
280				if (c)
281					*s = c;
282				return -1;
283			}
284		}
285	        if (!c)
286			return 0;
287
288		/* Remove any inserted nul from the path. */
289		*s = c;
290
291	} while (1);
292}
293
294/* Simple, efficient hash function from Daniel J. Bernstein */
295unsigned int
296DJBHash(uint8_t *data, int len)
297{
298	unsigned int hash = 5381;
299	unsigned int i = 0;
300
301	for(i = 0; i < len; data++, i++)
302	{
303		hash = ((hash << 5) + hash) + (*data);
304	}
305
306	return hash;
307}
308
309const char *
310mime_to_ext(const char * mime)
311{
312	switch( *mime )
313	{
314		/* Audio extensions */
315		case 'a':
316			if( strcmp(mime+6, "mpeg") == 0 )
317				return "mp3";
318			else if( strcmp(mime+6, "mp4") == 0 )
319				return "m4a";
320			else if( strcmp(mime+6, "x-ms-wma") == 0 )
321				return "wma";
322			else if( strcmp(mime+6, "x-flac") == 0 )
323				return "flac";
324			else if( strcmp(mime+6, "flac") == 0 )
325				return "flac";
326			else if( strcmp(mime+6, "x-wav") == 0 )
327				return "wav";
328			else if( strncmp(mime+6, "L16", 3) == 0 )
329				return "pcm";
330			else if( strcmp(mime+6, "3gpp") == 0 )
331				return "3gp";
332			else if( strcmp(mime, "application/ogg") == 0 )
333				return "ogg";
334			break;
335		case 'v':
336			if( strcmp(mime+6, "avi") == 0 )
337				return "avi";
338			else if( strcmp(mime+6, "divx") == 0 )
339				return "avi";
340			else if( strcmp(mime+6, "x-msvideo") == 0 )
341				return "avi";
342			else if( strcmp(mime+6, "mpeg") == 0 )
343				return "mpg";
344			else if( strcmp(mime+6, "mp4") == 0 )
345				return "mp4";
346			else if( strcmp(mime+6, "x-ms-wmv") == 0 )
347				return "wmv";
348			else if( strcmp(mime+6, "x-matroska") == 0 )
349				return "mkv";
350			else if( strcmp(mime+6, "x-mkv") == 0 )
351				return "mkv";
352			else if( strcmp(mime+6, "x-flv") == 0 )
353				return "flv";
354			else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
355				return "mpg";
356			else if( strcmp(mime+6, "quicktime") == 0 )
357				return "mov";
358			else if( strcmp(mime+6, "3gpp") == 0 )
359				return "3gp";
360			else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 )
361				return "TiVo";
362			break;
363		case 'i':
364			if( strcmp(mime+6, "jpeg") == 0 )
365				return "jpg";
366			else if( strcmp(mime+6, "png") == 0 )
367				return "png";
368			break;
369		default:
370			break;
371	}
372	return "dat";
373}
374
375int
376is_video(const char * file)
377{
378	return (ends_with(file, ".mpg") || ends_with(file, ".mpeg")  ||
379		ends_with(file, ".avi") || ends_with(file, ".divx")  ||
380		ends_with(file, ".asf") || ends_with(file, ".wmv")   ||
381		ends_with(file, ".mp4") || ends_with(file, ".m4v")   ||
382		ends_with(file, ".mts") || ends_with(file, ".m2ts")  ||
383		ends_with(file, ".m2t") || ends_with(file, ".mkv")   ||
384		ends_with(file, ".vob") || ends_with(file, ".ts")    ||
385		ends_with(file, ".flv") || ends_with(file, ".xvid")  ||
386#ifdef TIVO_SUPPORT
387		ends_with(file, ".TiVo") ||
388#endif
389        ends_with(file, ".m2v") ||     /* Foxconn add by Hank to support .m2v, 2012/04/12 */
390		ends_with(file, ".mov") || ends_with(file, ".3gp"));
391}
392
393int
394is_audio(const char * file)
395{
396	return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
397		ends_with(file, ".wma") || ends_with(file, ".asf")  ||
398		ends_with(file, ".fla") || ends_with(file, ".flc")  ||
399		ends_with(file, ".m4a") || ends_with(file, ".aac")  ||
400		ends_with(file, ".mp4") || ends_with(file, ".m4p")  ||
401		ends_with(file, ".wav") || ends_with(file, ".ogg")  ||
402		ends_with(file, ".pcm") || ends_with(file, ".3gp"));
403}
404
405int
406is_image(const char * file)
407{
408	return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
409}
410
411int
412is_playlist(const char * file)
413{
414	return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
415}
416
417int
418is_caption(const char * file)
419{
420	return (ends_with(file, ".srt") || ends_with(file, ".smi"));
421}
422
423int
424is_album_art(const char * name)
425{
426	struct album_art_name_s * album_art_name;
427
428	/* Check if this file name matches one of the default album art names */
429	for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
430	{
431		if( album_art_name->wildcard )
432		{
433			if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
434				break;
435		}
436		else
437		{
438			if( strcmp(album_art_name->name, name) == 0 )
439				break;
440		}
441	}
442
443	return (album_art_name ? 1 : 0);
444}
445
446int
447resolve_unknown_type(const char * path, media_types dir_type)
448{
449	struct stat entry;
450	unsigned char type = TYPE_UNKNOWN;
451	char str_buf[PATH_MAX];
452	ssize_t len;
453
454	if( lstat(path, &entry) == 0 )
455	{
456		if( S_ISLNK(entry.st_mode) )
457		{
458			if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
459			{
460				str_buf[len] = '\0';
461				//DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
462				if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
463				{
464					DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
465					return type;
466				}
467			}
468			stat(path, &entry);
469/* Foxconn modify start, Bernie 06/01/2016 */
470			type = TYPE_LINK;
471			return type;
472/* Foxconn modify end, Bernie 06/01/2016 */
473		}
474
475		if( S_ISDIR(entry.st_mode) )
476		{
477			type = TYPE_DIR;
478		}
479		else if( S_ISREG(entry.st_mode) )
480		{
481			switch( dir_type )
482			{
483				case ALL_MEDIA:
484					if( is_image(path) ||
485					    is_audio(path) ||
486					    is_video(path) ||
487					    is_playlist(path) )
488						type = TYPE_FILE;
489					break;
490				case TYPE_AUDIO:
491					if( is_audio(path) ||
492					    is_playlist(path) )
493						type = TYPE_FILE;
494					break;
495				case TYPE_VIDEO:
496					if( is_video(path) )
497						type = TYPE_FILE;
498					break;
499				case TYPE_IMAGES:
500					if( is_image(path) )
501						type = TYPE_FILE;
502					break;
503				default:
504					break;
505			}
506		}
507	}
508	return type;
509}
510
511#if 1
512int is_disk_mounted()
513{
514  FILE *fp;
515  char buffer[128];
516  /* see if there is any pen drive mount */
517  system("df > /tmp/df");
518  fp=fopen("/tmp/df","r");
519
520  if(fp)
521  {
522      while(!feof(fp))
523      {
524          fgets(buffer,128,fp);
525          if(strncmp("/dev/sd",buffer,strlen("/dev/sd"))==0)
526          {
527              fclose(fp);
528              return 1;
529          }
530      }
531      fclose(fp);
532  }
533  return 0;
534
535}
536#endif