"Fossies" - the Fresh Open Source Software Archive

Member "minidlna-1.3.0/utils.c" (24 Nov 2020, 12046 Bytes) of package /linux/privat/minidlna-1.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "utils.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.1_vs_1.3.0.

    1 /* MiniDLNA media server
    2  * Copyright (C) 2008-2017  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 
   38 int
   39 xasprintf(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 
   55 int
   56 ends_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 
   69 char *
   70 trim(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 */
  101 char *
  102 strstrc(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 
  124 char *
  125 strcasestrc(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 
  147 char *
  148 modifyString(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 
  192 char *
  193 unescape_tag(const char *tag, int force_alloc)
  194 {
  195     char *esc_tag = NULL;
  196 
  197     if (strchr(tag, '&') &&
  198         (strstr(tag, "&amp;") || strstr(tag, "&lt;") || strstr(tag, "&gt;") ||
  199          strstr(tag, "&quot;") || strstr(tag, "&apos;")))
  200     {
  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);
  207     }
  208     else if( force_alloc )
  209         esc_tag = strdup(tag);
  210 
  211     return esc_tag;
  212 }
  213 
  214 char *
  215 escape_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;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);
  226     }
  227     else if( force_alloc )
  228         esc_tag = strdup(tag);
  229 
  230     return esc_tag;
  231 }
  232 
  233 char *
  234 duration_str(int msec)
  235 {
  236     char *str;
  237 
  238     xasprintf(&str, "%d:%02d:%02d.%03d",
  239             (msec / 3600000),
  240             (msec / 60000 % 60),
  241             (msec / 1000 % 60),
  242             (msec % 1000));
  243 
  244     return str;
  245 }
  246 
  247 char *
  248 strip_ext(char *name)
  249 {
  250     char *period;
  251 
  252     if (!name)
  253         return NULL;
  254     period = strrchr(name, '.');
  255     if (period)
  256         *period = '\0';
  257 
  258     return period;
  259 }
  260 
  261 /* Code basically stolen from busybox */
  262 int
  263 make_dir(char * path, mode_t mode)
  264 {
  265     char * s = path;
  266     char c;
  267     struct stat st;
  268 
  269     do {
  270         c = '\0';
  271 
  272         /* Before we do anything, skip leading /'s, so we don't bother
  273          * trying to create /. */
  274         while (*s == '/')
  275             ++s;
  276 
  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;
  286             }
  287             ++s;
  288         }
  289 
  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;
  299             }
  300         }
  301         if (!c)
  302             return 0;
  303 
  304         /* Remove any inserted nul from the path. */
  305         *s = c;
  306 
  307     } while (1);
  308 }
  309 
  310 /* Simple, efficient hash function from Daniel J. Bernstein */
  311 unsigned int
  312 DJBHash(uint8_t *data, int len)
  313 {
  314     unsigned int hash = 5381;
  315     unsigned int i = 0;
  316 
  317     for(i = 0; i < len; data++, i++)
  318     {
  319         hash = ((hash << 5) + hash) + (*data);
  320     }
  321 
  322     return hash;
  323 }
  324 
  325 const char *
  326 mime_to_ext(const char * mime)
  327 {
  328     switch( *mime )
  329     {
  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;
  389     }
  390     return "dat";
  391 }
  392 
  393 int
  394 is_video(const char * file)
  395 {
  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 }
  409 
  410 int
  411 is_audio(const char * file)
  412 {
  413     return (ends_with(file, ".mp3") || ends_with(file, ".flac") ||
  414         ends_with(file, ".wma") || ends_with(file, ".asf")  ||
  415         ends_with(file, ".fla") || ends_with(file, ".flc")  ||
  416         ends_with(file, ".m4a") || ends_with(file, ".aac")  ||
  417         ends_with(file, ".mp4") || ends_with(file, ".m4p")  ||
  418         ends_with(file, ".wav") || ends_with(file, ".ogg")  ||
  419         ends_with(file, ".pcm") || ends_with(file, ".3gp")  ||
  420         ends_with(file, ".dsf") || ends_with(file, ".dff"));
  421 }
  422 
  423 int
  424 is_image(const char * file)
  425 {
  426     return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
  427 }
  428 
  429 int
  430 is_playlist(const char * file)
  431 {
  432     return (ends_with(file, ".m3u") || ends_with(file, ".pls"));
  433 }
  434 
  435 int
  436 is_caption(const char * file)
  437 {
  438     return (ends_with(file, ".srt") || ends_with(file, ".smi"));
  439 }
  440 
  441 media_types
  442 get_media_type(const char *file)
  443 {
  444     const char *ext = strrchr(file, '.');
  445     if (!ext)
  446         return NO_MEDIA;
  447     if (is_image(ext))
  448         return TYPE_IMAGE;
  449     if (is_video(ext))
  450         return TYPE_VIDEO;
  451     if (is_audio(ext))
  452         return TYPE_AUDIO;
  453     if (is_playlist(ext))
  454         return TYPE_PLAYLIST;
  455     if (is_caption(ext))
  456         return TYPE_CAPTION;
  457     if (is_nfo(ext))
  458         return TYPE_NFO;
  459     return NO_MEDIA;
  460 }
  461 
  462 int
  463 is_album_art(const char * name)
  464 {
  465     struct album_art_name_s * album_art_name;
  466 
  467     /* Check if this file name matches one of the default album art names */
  468     for( album_art_name = album_art_names; album_art_name; album_art_name = album_art_name->next )
  469     {
  470         if( album_art_name->wildcard )
  471         {
  472             if( strncmp(album_art_name->name, name, strlen(album_art_name->name)) == 0 )
  473                 break;
  474         }
  475         else
  476         {
  477             if( strcmp(album_art_name->name, name) == 0 )
  478                 break;
  479         }
  480     }
  481 
  482     return (album_art_name ? 1 : 0);
  483 }
  484 
  485 int
  486 resolve_unknown_type(const char * path, media_types dir_type)
  487 {
  488     struct stat entry;
  489     enum file_types type = TYPE_UNKNOWN;
  490     char str_buf[PATH_MAX];
  491     ssize_t len;
  492 
  493     if( lstat(path, &entry) == 0 )
  494     {
  495         if( S_ISLNK(entry.st_mode) )
  496         {
  497             if( (len = readlink(path, str_buf, PATH_MAX-1)) > 0 )
  498             {
  499                 str_buf[len] = '\0';
  500                 //DEBUG DPRINTF(E_DEBUG, L_GENERAL, "Checking for recursive symbolic link: %s (%s)\n", path, str_buf);
  501                 if( strncmp(path, str_buf, strlen(str_buf)) == 0 )
  502                 {
  503                     DPRINTF(E_DEBUG, L_GENERAL, "Ignoring recursive symbolic link: %s (%s)\n", path, str_buf);
  504                     return type;
  505                 }
  506             }
  507             stat(path, &entry);
  508         }
  509 
  510         if( S_ISDIR(entry.st_mode) )
  511         {
  512             type = TYPE_DIR;
  513         }
  514         else if( S_ISREG(entry.st_mode) )
  515         {
  516             media_types mtype = get_media_type(path);
  517             if (dir_type & mtype)
  518                 type = TYPE_FILE;
  519         }
  520     }
  521     return type;
  522 }
  523 
  524 media_types
  525 valid_media_types(const char *path)
  526 {
  527     struct media_dir_s *media_dir;
  528 
  529     for (media_dir = media_dirs; media_dir; media_dir = media_dir->next)
  530     {
  531         if (strncmp(path, media_dir->path, strlen(media_dir->path)) == 0)
  532             return media_dir->types;
  533     }
  534 
  535     return ALL_MEDIA;
  536 }
  537 
  538 /*
  539  * Add and subtract routines for timevals.
  540  * N.B.: subtract routine doesn't deal with
  541  * results which are before the beginning,
  542  * it just gets very confused in this case.
  543  * Caveat emptor.
  544  */
  545 static void timevalfix(struct timeval *);
  546 void
  547 timevaladd(struct timeval *t1, const struct timeval *t2)
  548 {
  549 
  550     t1->tv_sec += t2->tv_sec;
  551     t1->tv_usec += t2->tv_usec;
  552     timevalfix(t1);
  553 }
  554 
  555 void
  556 timevalsub(struct timeval *t1, const struct timeval *t2)
  557 {
  558 
  559     t1->tv_sec -= t2->tv_sec;
  560     t1->tv_usec -= t2->tv_usec;
  561     timevalfix(t1);
  562 }
  563 
  564 static void
  565 timevalfix(struct timeval *t1)
  566 {
  567 
  568     if (t1->tv_usec < 0) {
  569         t1->tv_sec--;
  570         t1->tv_usec += 1000000;
  571     }
  572     if (t1->tv_usec >= 1000000) {
  573         t1->tv_sec++;
  574         t1->tv_usec -= 1000000;
  575     }
  576 }