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;", 0); 212 esc_tag = modifyString(esc_tag, "<", "&lt;", 0); 213 esc_tag = modifyString(esc_tag, ">", "&gt;", 0); 214 esc_tag = modifyString(esc_tag, "\"", "&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