Wrap up version 1.3.3.
[minidlna.git] / utils.c
blob03ba8505a9713315982abd6746ba205217db482c
1 /* MiniDLNA media server
2 * Copyright (C) 2008-2017 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/>.
18 #include "config.h"
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>
33 #include "minidlnatypes.h"
34 #include "upnpglobalvars.h"
35 #include "utils.h"
36 #include "log.h"
38 int
39 xasprintf(char **strp, char *fmt, ...)
41 va_list args;
42 int ret;
44 va_start(args, fmt);
45 ret = vasprintf(strp, fmt, args);
46 va_end(args);
47 if( ret < 0 )
49 DPRINTF(E_WARN, L_GENERAL, "xasprintf: allocation failed\n");
50 *strp = NULL;
52 return ret;
55 int
56 ends_with(const char * haystack, const char * needle)
58 const char * end;
59 int nlen = strlen(needle);
60 int hlen = strlen(haystack);
62 if( nlen > hlen )
63 return 0;
64 end = haystack + hlen - nlen;
66 return (strcasecmp(end, needle) ? 0 : 1);
69 char *
70 trim(char *str)
72 int i;
73 int len;
75 if (!str)
76 return(NULL);
78 len = strlen(str);
79 for (i=len-1; i >= 0 && isspace(str[i]); i--)
81 str[i] = '\0';
82 len--;
84 while (isspace(*str))
86 str++;
87 len--;
90 if (str[0] == '"' && str[len-1] == '"')
92 str[0] = '\0';
93 str[len-1] = '\0';
94 str++;
97 return str;
100 /* Find the first occurrence of p in s, where s is terminated by t */
101 char *
102 strstrc(const char *s, const char *p, const char t)
104 char *endptr;
105 size_t slen, plen;
107 endptr = strchr(s, t);
108 if (!endptr)
109 return strstr(s, p);
111 plen = strlen(p);
112 slen = endptr - s;
113 while (slen >= plen)
115 if (*s == *p && strncmp(s+1, p+1, plen-1) == 0)
116 return (char*)s;
117 s++;
118 slen--;
121 return NULL;
124 char *
125 strcasestrc(const char *s, const char *p, const char t)
127 char *endptr;
128 size_t slen, plen;
130 endptr = strchr(s, t);
131 if (!endptr)
132 return strcasestr(s, p);
134 plen = strlen(p);
135 slen = endptr - s;
136 while (slen >= plen)
138 if (*s == *p && strncasecmp(s+1, p+1, plen-1) == 0)
139 return (char*)s;
140 s++;
141 slen--;
144 return NULL;
147 char *
148 modifyString(char *string, const char *before, const char *after, int noalloc)
150 int oldlen, newlen, chgcnt = 0;
151 char *s, *p;
153 /* If there is no match, just return */
154 s = strstr(string, before);
155 if (!s)
156 return string;
158 oldlen = strlen(before);
159 newlen = strlen(after);
160 if (newlen > oldlen)
162 if (noalloc)
163 return string;
165 while ((p = strstr(s, before)))
167 chgcnt++;
168 s = p + oldlen;
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;
178 s = string;
179 while (s)
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;
189 return string;
192 char *
193 unescape_tag(const char *tag, int force_alloc)
195 char *esc_tag = NULL;
197 if (strchr(tag, '&') &&
198 (strstr(tag, "&amp;") || strstr(tag, "&lt;") || strstr(tag, "&gt;") ||
199 strstr(tag, "&quot;") || strstr(tag, "&apos;")))
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);
208 else if( force_alloc )
209 esc_tag = strdup(tag);
211 return esc_tag;
214 char *
215 escape_tag(const char *tag, int force_alloc)
217 char *esc_tag = NULL;
219 if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') || strchr(tag, '"') )
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);
227 else if( force_alloc )
228 esc_tag = strdup(tag);
230 return esc_tag;
233 char *
234 duration_str(int msec)
236 char *str;
238 xasprintf(&str, "%d:%02d:%02d.%03d",
239 (msec / 3600000),
240 (msec / 60000 % 60),
241 (msec / 1000 % 60),
242 (msec % 1000));
244 return str;
247 char *
248 strip_ext(char *name)
250 char *period;
252 if (!name)
253 return NULL;
254 period = strrchr(name, '.');
255 if (period)
256 *period = '\0';
258 return period;
261 /* Code basically stolen from busybox */
263 make_dir(char * path, mode_t mode)
265 char * s = path;
266 char c;
267 struct stat st;
269 do {
270 c = '\0';
272 /* Before we do anything, skip leading /'s, so we don't bother
273 * trying to create /. */
274 while (*s == '/')
275 ++s;
277 /* Bypass leading non-'/'s and then subsequent '/'s. */
278 while (*s) {
279 if (*s == '/') {
280 do {
281 ++s;
282 } while (*s == '/');
283 c = *s; /* Save the current char */
284 *s = '\0'; /* and replace it with nul. */
285 break;
287 ++s;
290 if (mkdir(path, mode) < 0) {
291 /* If we failed for any other reason than the directory
292 * already exists, output a diagnostic and return -1.*/
293 if ((errno != EEXIST && errno != EISDIR)
294 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
295 DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path);
296 if (c)
297 *s = c;
298 return -1;
301 if (!c)
302 return 0;
304 /* Remove any inserted nul from the path. */
305 *s = c;
307 } while (1);
310 /* Simple, efficient hash function from Daniel J. Bernstein */
311 unsigned int
312 DJBHash(uint8_t *data, int len)
314 unsigned int hash = 5381;
315 unsigned int i = 0;
317 for(i = 0; i < len; data++, i++)
319 hash = ((hash << 5) + hash) + (*data);
322 return hash;
325 const char *
326 mime_to_ext(const char * mime)
328 switch( *mime )
330 /* Audio extensions */
331 case 'a':
332 if( strcmp(mime+6, "mpeg") == 0 )
333 return "mp3";
334 else if( strcmp(mime+6, "mp4") == 0 )
335 return "m4a";
336 else if( strcmp(mime+6, "x-ms-wma") == 0 )
337 return "wma";
338 else if( strcmp(mime+6, "x-flac") == 0 )
339 return "flac";
340 else if( strcmp(mime+6, "flac") == 0 )
341 return "flac";
342 else if( strcmp(mime+6, "x-wav") == 0 )
343 return "wav";
344 else if( strncmp(mime+6, "L16", 3) == 0 )
345 return "pcm";
346 else if( strcmp(mime+6, "3gpp") == 0 )
347 return "3gp";
348 else if( strcmp(mime, "application/ogg") == 0 )
349 return "ogg";
350 else if( strcmp(mime+6, "x-dsd") == 0 )
351 return "dsd";
352 break;
353 case 'v':
354 if( strcmp(mime+6, "avi") == 0 )
355 return "avi";
356 else if( strcmp(mime+6, "divx") == 0 )
357 return "avi";
358 else if( strcmp(mime+6, "x-msvideo") == 0 )
359 return "avi";
360 else if( strcmp(mime+6, "mpeg") == 0 )
361 return "mpg";
362 else if( strcmp(mime+6, "mp4") == 0 )
363 return "mp4";
364 else if( strcmp(mime+6, "x-ms-wmv") == 0 )
365 return "wmv";
366 else if( strcmp(mime+6, "x-matroska") == 0 )
367 return "mkv";
368 else if( strcmp(mime+6, "x-mkv") == 0 )
369 return "mkv";
370 else if( strcmp(mime+6, "x-flv") == 0 )
371 return "flv";
372 else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
373 return "mpg";
374 else if( strcmp(mime+6, "quicktime") == 0 )
375 return "mov";
376 else if( strcmp(mime+6, "3gpp") == 0 )
377 return "3gp";
378 else if( strncmp(mime+6, "x-tivo-mpeg", 11) == 0 )
379 return "TiVo";
380 break;
381 case 'i':
382 if( strcmp(mime+6, "jpeg") == 0 )
383 return "jpg";
384 else if( strcmp(mime+6, "png") == 0 )
385 return "png";
386 break;
387 default:
388 break;
390 return "dat";
394 is_video(const char * file)
396 return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
397 ends_with(file, ".avi") || ends_with(file, ".divx") ||
398 ends_with(file, ".asf") || ends_with(file, ".wmv") ||
399 ends_with(file, ".mp4") || ends_with(file, ".m4v") ||
400 ends_with(file, ".mts") || ends_with(file, ".m2ts") ||
401 ends_with(file, ".m2t") || ends_with(file, ".mkv") ||
402 ends_with(file, ".vob") || ends_with(file, ".ts") ||
403 ends_with(file, ".flv") || ends_with(file, ".xvid") ||
404 #ifdef TIVO_SUPPORT
405 ends_with(file, ".TiVo") ||
406 #endif
407 ends_with(file, ".mov") || ends_with(file, ".3gp") ||
408 ends_with(file, ".rm") || ends_with(file, ".rmvb") ||
409 ends_with(file, ".webm"));
413 is_audio(const char * file)
415 return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
416 ends_with(file, ".wma") || ends_with(file, ".asf") ||
417 ends_with(file, ".fla") || ends_with(file, ".flc") ||
418 ends_with(file, ".m4a") || ends_with(file, ".aac") ||
419 ends_with(file, ".mp4") || ends_with(file, ".m4p") ||
420 ends_with(file, ".wav") || ends_with(file, ".ogg") ||
421 ends_with(file, ".pcm") || ends_with(file, ".3gp") ||
422 ends_with(file, ".dsf") || ends_with(file, ".dff"));
426 is_image(const char * file)
428 return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
432 is_playlist(const char * file)
434 return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
438 is_caption(const char * file)
440 return (ends_with(file, ".srt") || ends_with(file, ".smi"));
443 media_types
444 get_media_type(const char *file)
446 const char *ext = strrchr(file, '.');
447 if (!ext)
448 return NO_MEDIA;
449 if (is_image(ext))
450 return TYPE_IMAGE;
451 if (is_video(ext))
452 return TYPE_VIDEO;
453 if (is_audio(ext))
454 return TYPE_AUDIO;
455 if (is_playlist(ext))
456 return TYPE_PLAYLIST;
457 if (is_caption(ext))
458 return TYPE_CAPTION;
459 if (is_nfo(ext))
460 return TYPE_NFO;
461 return NO_MEDIA;
465 is_album_art(const char * name)
467 struct album_art_name_s * album_art_name;
469 /* Check if this file name matches one of the default album art names */
470 for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
472 if( album_art_name->wildcard )
474 if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
475 break;
477 else
479 if( strcmp(album_art_name->name, name) == 0 )
480 break;
484 return (album_art_name ? 1 : 0);
488 resolve_unknown_type(const char * path, media_types dir_type)
490 struct stat entry;
491 enum file_types type = TYPE_UNKNOWN;
492 char str_buf[PATH_MAX];
493 ssize_t len;
495 if( lstat(path, &entry) == 0 )
497 if( S_ISLNK(entry.st_mode) )
499 if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
501 str_buf[len] = '\0';
502 //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
503 if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
505 DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
506 return type;
509 stat(path, &entry);
512 if( S_ISDIR(entry.st_mode) )
514 type = TYPE_DIR;
516 else if( S_ISREG(entry.st_mode) )
518 media_types mtype = get_media_type(path);
519 if (dir_type & mtype)
520 type = TYPE_FILE;
523 return type;
526 media_types
527 valid_media_types(const char *path)
529 struct media_dir_s *media_dir;
531 for (media_dir = media_dirs; media_dir; media_dir = media_dir->next)
533 if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0)
534 return media_dir->types;
537 return ALL_MEDIA;
541 * Add and subtract routines for timevals.
542 * N.B.: subtract routine doesn't deal with
543 * results which are before the beginning,
544 * it just gets very confused in this case.
545 * Caveat emptor.
547 static void timevalfix(struct timeval *);
548 void
549 timevaladd(struct timeval *t1, const struct timeval *t2)
552 t1->tv_sec += t2->tv_sec;
553 t1->tv_usec += t2->tv_usec;
554 timevalfix(t1);
557 void
558 timevalsub(struct timeval *t1, const struct timeval *t2)
561 t1->tv_sec -= t2->tv_sec;
562 t1->tv_usec -= t2->tv_usec;
563 timevalfix(t1);
566 static void
567 timevalfix(struct timeval *t1)
570 if (t1->tv_usec < 0) {
571 t1->tv_sec--;
572 t1->tv_usec += 1000000;
574 if (t1->tv_usec >= 1000000) {
575 t1->tv_sec++;
576 t1->tv_usec -= 1000000;