1/* MiniDLNA media server 2 * Copyright (C) 2008-2009 Justin Maggard 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 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 "log.h" 32 33int 34ends_with(const char * haystack, const char * needle) 35{ 36 const char * end; 37 int nlen = strlen(needle); 38 int hlen = strlen(haystack); 39 40 if( nlen > hlen ) 41 return 0; 42 end = haystack + hlen - nlen; 43 44 return (strcasecmp(end, needle) ? 0 : 1); 45} 46 47char * 48trim(char *str) 49{ 50 if (!str) 51 return(NULL); 52 int i; 53 for (i=0; i <= strlen(str) && (isspace(str[i]) || str[i] == '"'); i++) { 54 str++; 55 } 56 for (i=(strlen(str)-1); i >= 0 && (isspace(str[i]) || str[i] == '"'); i--) { 57 str[i] = '\0'; 58 } 59 return str; 60} 61 62/* Find the first occurrence of p in s, where s is terminated by t */ 63char * 64strstrc(const char *s, const char *p, const char t) 65{ 66 char *endptr; 67 size_t slen, plen; 68 69 endptr = strchr(s, t); 70 if (!endptr) 71 return NULL; 72 73 plen = strlen(p); 74 slen = endptr - s; 75 while (slen >= plen) 76 { 77 if (*s == *p && strncmp(s+1, p+1, plen-1) == 0) 78 return (char*)s; 79 s++; 80 slen--; 81 } 82 83 return NULL; 84} 85 86char * 87modifyString(char * string, const char * before, const char * after, short like) 88{ 89 int oldlen, newlen, chgcnt = 0; 90 char *s, *p, *t; 91 92 oldlen = strlen(before); 93 newlen = strlen(after); 94 if( newlen+like > oldlen ) 95 { 96 s = string; 97 while( (p = strstr(s, before)) ) 98 { 99 chgcnt++; 100 s = p+oldlen; 101 } 102 string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like); 103 } 104 105 s = string; 106 while( s ) 107 { 108 p = strcasestr(s, before); 109 if( !p ) 110 return string; 111 memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1); 112 memcpy(p, after, newlen); 113 if( like ) 114 { 115 t = p+newlen; 116 while( isspace(*t) ) 117 t++; 118 if( *t == '"' ) 119 { 120 if( like == 2 ) 121 { 122 memmove(t+2, t+1, strlen(t+1)+1); 123 *++t = '%'; 124 } 125 while( *++t != '"' ) 126 continue; 127 memmove(t+1, t, strlen(t)+1); 128 *t = '%'; 129 } 130 } 131 s = p + newlen; 132 } 133 134 return string; 135} 136 137char * 138escape_tag(const char *tag) 139{ 140 char *esc_tag = NULL; 141 142 if( strchr(tag, '&') || strchr(tag, '<') || strchr(tag, '>') ) 143 { 144 esc_tag = strdup(tag); 145 esc_tag = modifyString(esc_tag, "&", "&amp;", 0); 146 esc_tag = modifyString(esc_tag, "<", "&lt;", 0); 147 esc_tag = modifyString(esc_tag, ">", "&gt;", 0); 148 } 149 150 return esc_tag; 151} 152 153void 154strip_ext(char * name) 155{ 156 char * period; 157 158 period = strrchr(name, '.'); 159 if( period ) 160 *period = '\0'; 161} 162 163/* Code basically stolen from busybox */ 164int 165make_dir(char * path, mode_t mode) 166{ 167 char * s = path; 168 char c; 169 struct stat st; 170 171 do { 172 c = 0; 173 174 /* Bypass leading non-'/'s and then subsequent '/'s. */ 175 while (*s) { 176 if (*s == '/') { 177 do { 178 ++s; 179 } while (*s == '/'); 180 c = *s; /* Save the current char */ 181 *s = 0; /* and replace it with nul. */ 182 break; 183 } 184 ++s; 185 } 186 187 if (mkdir(path, mode) < 0) { 188 /* If we failed for any other reason than the directory 189 * already exists, output a diagnostic and return -1.*/ 190 if (errno != EEXIST 191 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) { 192 break; 193 } 194 } 195 if (!c) 196 return 0; 197 198 /* Remove any inserted nul from the path. */ 199 *s = c; 200 201 } while (1); 202 203 DPRINTF(E_WARN, L_GENERAL, "make_dir: cannot create directory '%s'\n", path); 204 return -1; 205} 206 207int 208is_video(const char * file) 209{ 210 return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") || 211 ends_with(file, ".avi") || ends_with(file, ".divx") || 212 ends_with(file, ".asf") || ends_with(file, ".wmv") || 213 ends_with(file, ".mp4") || ends_with(file, ".m4v") || 214 ends_with(file, ".mts") || ends_with(file, ".m2ts") || 215 ends_with(file, ".m2t") || ends_with(file, ".mkv") || 216 ends_with(file, ".vob") || ends_with(file, ".ts") || 217 ends_with(file, ".flv") || ends_with(file, ".xvid") || 218#ifdef TIVO_SUPPORT 219 ends_with(file, ".TiVo") || 220#endif 221 ends_with(file, ".m2v") || /* Foxconn add by Silver to support .m2v, 2012/01/03 */ 222 ends_with(file, ".mov") || ends_with(file, ".3gp")); 223} 224 225int 226is_audio(const char * file) 227{ 228 return (ends_with(file, ".mp3") || ends_with(file, ".flac") || 229 ends_with(file, ".wma") || ends_with(file, ".asf") || 230 ends_with(file, ".fla") || ends_with(file, ".flc") || 231 ends_with(file, ".m4a") || ends_with(file, ".aac") || 232 ends_with(file, ".mp4") || ends_with(file, ".m4p") || 233 ends_with(file, ".wav") || ends_with(file, ".ogg") || 234 ends_with(file, ".pcm") || ends_with(file, ".3gp")); 235} 236 237int 238is_image(const char * file) 239{ 240 return (ends_with(file, ".jpg") || ends_with(file, ".jpeg")); 241} 242 243int 244is_playlist(const char * file) 245{ 246 return (ends_with(file, ".m3u") || ends_with(file, ".pls")); 247} 248 249int 250resolve_unknown_type(const char * path, enum media_types dir_type) 251{ 252 struct stat entry; 253 unsigned char type = TYPE_UNKNOWN; 254 char str_buf[PATH_MAX]; 255 ssize_t len; 256 257 if( lstat(path, &entry) == 0 ) 258 { 259 if( S_ISLNK(entry.st_mode) ) 260 { 261 if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 ) 262 { 263 str_buf[len] = '\0'; 264 //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf); 265 if( strncmp(path, str_buf, strlen(str_buf)) == 0 ) 266 { 267 DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf); 268 return type; 269 } 270 } 271 stat(path, &entry); 272 } 273 274 if( S_ISDIR(entry.st_mode) ) 275 { 276 type = TYPE_DIR; 277 } 278 else if( S_ISREG(entry.st_mode) ) 279 { 280 switch( dir_type ) 281 { 282 case ALL_MEDIA: 283 if( is_image(path) || 284 is_audio(path) || 285 is_video(path) || 286 is_playlist(path) ) 287 type = TYPE_FILE; 288 break; 289 case AUDIO_ONLY: 290 if( is_audio(path) || 291 is_playlist(path) ) 292 type = TYPE_FILE; 293 break; 294 case VIDEO_ONLY: 295 if( is_video(path) ) 296 type = TYPE_FILE; 297 break; 298 case IMAGES_ONLY: 299 if( is_image(path) ) 300 type = TYPE_FILE; 301 break; 302 default: 303 break; 304 } 305 } 306 } 307 return type; 308} 309 310