1 /* MiniDLNA media server
2 * Copyright (C) 2008-2009 Justin Maggard
4 * This file is part of MiniDLNA.
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.
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.
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/>.
24 #include <sys/param.h>
27 #include <sys/types.h>
33 #include "minidlnatypes.h"
34 #include "upnpglobalvars.h"
39 xasprintf(char **strp
, char *fmt
, ...)
45 ret
= vasprintf(strp
, fmt
, args
);
49 DPRINTF(E_WARN
, L_GENERAL
, "xasprintf: allocation failed\n");
56 ends_with(const char * haystack
, const char * needle
)
59 int nlen
= strlen(needle
);
60 int hlen
= strlen(haystack
);
64 end
= haystack
+ hlen
- nlen
;
66 return (strcasecmp(end
, needle
) ? 0 : 1);
79 for (i
=len
-1; i
>= 0 && isspace(str
[i
]); i
--)
90 if (str
[0] == '"' && str
[len
-1] == '"')
100 /* Find the first occurrence of p in s, where s is terminated by t */
102 strstrc(const char *s
, const char *p
, const char t
)
107 endptr
= strchr(s
, t
);
115 if (*s
== *p
&& strncmp(s
+1, p
+1, plen
-1) == 0)
125 strcasestrc(const char *s
, const char *p
, const char t
)
130 endptr
= strchr(s
, t
);
132 return strcasestr(s
, p
);
138 if (*s
== *p
&& strncasecmp(s
+1, p
+1, plen
-1) == 0)
148 modifyString(char *string
, const char *before
, const char *after
, int noalloc
)
150 int oldlen
, newlen
, chgcnt
= 0;
153 /* If there is no match, just return */
154 s
= strstr(string
, before
);
158 oldlen
= strlen(before
);
159 newlen
= strlen(after
);
165 while ((p
= strstr(s
, before
)))
170 s
= realloc(string
, strlen(string
)+((newlen
-oldlen
)*chgcnt
)+1);
171 /* If we failed to realloc, return the original alloc'd string */
181 p
= strstr(s
, before
);
184 memmove(p
+ newlen
, p
+ oldlen
, strlen(p
+ oldlen
) + 1);
185 memcpy(p
, after
, newlen
);
193 unescape_tag(const char *tag
, int force_alloc
)
195 char *esc_tag
= NULL
;
197 if( strstr(tag
, "&") || strstr(tag
, "<") || strstr(tag
, ">")
198 || strstr(tag
, """) )
200 esc_tag
= strdup(tag
);
201 esc_tag
= modifyString(esc_tag
, "&", "&", 1);
202 esc_tag
= modifyString(esc_tag
, "<", "<", 1);
203 esc_tag
= modifyString(esc_tag
, ">", ">", 1);
204 esc_tag
= modifyString(esc_tag
, """, "\"", 1);
206 else if( force_alloc
)
207 esc_tag
= strdup(tag
);
213 escape_tag(const char *tag
, int force_alloc
)
215 char *esc_tag
= NULL
;
217 if( strchr(tag
, '&') || strchr(tag
, '<') || strchr(tag
, '>') || strchr(tag
, '"') )
219 esc_tag
= strdup(tag
);
220 esc_tag
= modifyString(esc_tag
, "&", "&amp;", 0);
221 esc_tag
= modifyString(esc_tag
, "<", "&lt;", 0);
222 esc_tag
= modifyString(esc_tag
, ">", "&gt;", 0);
223 esc_tag
= modifyString(esc_tag
, "\"", "&quot;", 0);
225 else if( force_alloc
)
226 esc_tag
= strdup(tag
);
232 strip_ext(char *name
)
236 period
= strrchr(name
, '.');
243 /* Code basically stolen from busybox */
245 make_dir(char * path
, mode_t mode
)
254 /* Before we do anything, skip leading /'s, so we don't bother
255 * trying to create /. */
259 /* Bypass leading non-'/'s and then subsequent '/'s. */
265 c
= *s
; /* Save the current char */
266 *s
= '\0'; /* and replace it with nul. */
272 if (mkdir(path
, mode
) < 0) {
273 /* If we failed for any other reason than the directory
274 * already exists, output a diagnostic and return -1.*/
275 if ((errno
!= EEXIST
&& errno
!= EISDIR
)
276 || (stat(path
, &st
) < 0 || !S_ISDIR(st
.st_mode
))) {
277 DPRINTF(E_WARN
, L_GENERAL
, "make_dir: cannot create directory '%s'\n", path
);
286 /* Remove any inserted nul from the path. */
292 /* Simple, efficient hash function from Daniel J. Bernstein */
294 DJBHash(uint8_t *data
, int len
)
296 unsigned int hash
= 5381;
299 for(i
= 0; i
< len
; data
++, i
++)
301 hash
= ((hash
<< 5) + hash
) + (*data
);
308 mime_to_ext(const char * mime
)
312 /* Audio extensions */
314 if( strcmp(mime
+6, "mpeg") == 0 )
316 else if( strcmp(mime
+6, "mp4") == 0 )
318 else if( strcmp(mime
+6, "x-ms-wma") == 0 )
320 else if( strcmp(mime
+6, "x-flac") == 0 )
322 else if( strcmp(mime
+6, "flac") == 0 )
324 else if( strcmp(mime
+6, "x-wav") == 0 )
326 else if( strncmp(mime
+6, "L16", 3) == 0 )
328 else if( strcmp(mime
+6, "3gpp") == 0 )
330 else if( strcmp(mime
, "application/ogg") == 0 )
332 else if( strcmp(mime
+6, "x-dsd") == 0 )
336 if( strcmp(mime
+6, "avi") == 0 )
338 else if( strcmp(mime
+6, "divx") == 0 )
340 else if( strcmp(mime
+6, "x-msvideo") == 0 )
342 else if( strcmp(mime
+6, "mpeg") == 0 )
344 else if( strcmp(mime
+6, "mp4") == 0 )
346 else if( strcmp(mime
+6, "x-ms-wmv") == 0 )
348 else if( strcmp(mime
+6, "x-matroska") == 0 )
350 else if( strcmp(mime
+6, "x-mkv") == 0 )
352 else if( strcmp(mime
+6, "x-flv") == 0 )
354 else if( strcmp(mime
+6, "vnd.dlna.mpeg-tts") == 0 )
356 else if( strcmp(mime
+6, "quicktime") == 0 )
358 else if( strcmp(mime
+6, "3gpp") == 0 )
360 else if( strncmp(mime
+6, "x-tivo-mpeg", 11) == 0 )
364 if( strcmp(mime
+6, "jpeg") == 0 )
366 else if( strcmp(mime
+6, "png") == 0 )
376 is_video(const char * file
)
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") ||
387 ends_with(file
, ".TiVo") ||
389 ends_with(file
, ".mov") || ends_with(file
, ".3gp"));
393 is_audio(const char * file
)
395 return (ends_with(file
, ".mp3") || ends_with(file
, ".flac") ||
396 ends_with(file
, ".wma") || ends_with(file
, ".asf") ||
397 ends_with(file
, ".fla") || ends_with(file
, ".flc") ||
398 ends_with(file
, ".m4a") || ends_with(file
, ".aac") ||
399 ends_with(file
, ".mp4") || ends_with(file
, ".m4p") ||
400 ends_with(file
, ".wav") || ends_with(file
, ".ogg") ||
401 ends_with(file
, ".pcm") || ends_with(file
, ".3gp") ||
402 ends_with(file
, ".dsf") || ends_with(file
, ".dff"));
406 is_image(const char * file
)
408 return (ends_with(file
, ".jpg") || ends_with(file
, ".jpeg"));
412 is_playlist(const char * file
)
414 return (ends_with(file
, ".m3u") || ends_with(file
, ".pls"));
418 is_caption(const char * file
)
420 return (ends_with(file
, ".srt") || ends_with(file
, ".smi"));
424 is_album_art(const char * name
)
426 struct album_art_name_s
* album_art_name
;
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
)
431 if( album_art_name
->wildcard
)
433 if( strncmp(album_art_name
->name
, name
, strlen(album_art_name
->name
)) == 0 )
438 if( strcmp(album_art_name
->name
, name
) == 0 )
443 return (album_art_name
? 1 : 0);
447 resolve_unknown_type(const char * path
, media_types dir_type
)
450 unsigned char type
= TYPE_UNKNOWN
;
451 char str_buf
[PATH_MAX
];
454 if( lstat(path
, &entry
) == 0 )
456 if( S_ISLNK(entry
.st_mode
) )
458 if( (len
= readlink(path
, str_buf
, PATH_MAX
-1)) > 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 )
464 DPRINTF(E_DEBUG
, L_GENERAL
, "Ignoring recursive symbolic link: %s (%s)\n", path
, str_buf
);
471 if( S_ISDIR(entry
.st_mode
) )
475 else if( S_ISREG(entry
.st_mode
) )
480 if( is_image(path
) ||
487 if( is_audio(path
) ||
513 flag
= fopen("/ramfs/.upnp-av_scan", "w");
517 mkdir("/var/notice", 0755);
518 flag
= fopen("/var/notice/dlna", "w");
521 fprintf(flag
, "Scan in progress");
531 if( access("/ramfs/.rescan_done", F_OK
) == 0 )
532 system("/bin/sh /ramfs/.rescan_done");
533 unlink("/ramfs/.upnp-av_scan");
535 unlink("/var/notice/dlna");