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, "&") || strstr(tag, "<") || strstr(tag, ">") || 199 strstr(tag, """) || strstr(tag, "'"))) 200 { 201 esc_tag = strdup(tag); 202 esc_tag = modifyString(esc_tag, "&", "&", 1); 203 esc_tag = modifyString(esc_tag, "<", "<", 1); 204 esc_tag = modifyString(esc_tag, ">", ">", 1); 205 esc_tag = modifyString(esc_tag, """, "\"", 1); 206 esc_tag = modifyString(esc_tag, "'", "'", 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;", 0); 223 esc_tag = modifyString(esc_tag, "<", "&lt;", 0); 224 esc_tag = modifyString(esc_tag, ">", "&gt;", 0); 225 esc_tag = modifyString(esc_tag, "\"", "&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