data.c
author nathan
Tue, 03 Feb 2009 20:33:04 +0800
branchtrunk
changeset 22 93aaf15c145a
parent 6 111ef8181229
child 29 640ce9201139
permissions -rw-r--r--
remove compatibility for VDR < 1.4.5
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <ctype.h>
    23 #include <dirent.h>
    24 #include <stdlib.h>
    25 #include <stdio.h>
    26 #include <errno.h>
    27 #include <sys/stat.h>
    28 #include <sys/types.h>
    29 #include <unistd.h>
    30 #include <dirent.h>
    31 #include <fnmatch.h>
    32 
    33 #include <vdr/tools.h>
    34 
    35 #include "common.h"
    36 #include "data.h"
    37 #include "data-src.h"
    38 
    39 // ----------------------------------------------------------------
    40 
    41 const char *mountscript = "mount.sh";
    42 
    43 char *Quote(const char *str)
    44 {
    45   char *nstr=MALLOC(char,strlen(str)*2);
    46   char *p=nstr;
    47   while(*str) {
    48     switch(*str) {
    49       case '$':  // dollar
    50       case '\\': // backslash
    51       case '\"': // double quote
    52       case '`':  // back tick
    53                  *p++='\\'; break;
    54       }
    55     *p++=*str++;
    56     }
    57   *p=0;
    58   return nstr;
    59 }
    60 
    61 char *AddPath(const char *dir, const char *filename)
    62 {
    63   char *name=0;
    64   asprintf(&name,"%s/%s",dir,filename);
    65   return name;
    66 }
    67 
    68 bool CheckVDRVersion(int Version, int Major, int Minor, const char *text)
    69 {
    70   static char vv[] = VDRVERSION;
    71   int version, major, minor;
    72   if(sscanf(vv,"%d.%d.%d",&version,&major,&minor)==3) {
    73     if(version<Version ||
    74        (version==Version && major<Major) ||
    75        (version==Version && major==Major && minor<Minor)) {
    76       if(text) {
    77         esyslog("ERROR: %s plugin needs at least VDR version %d.%d.%d",text,Version,Major,Minor);
    78         fprintf(stderr,"%s plugin needs at least VDR version %d.%d.%d\n",text,Version,Major,Minor);
    79         }
    80       return false;
    81       }
    82     }
    83   else esyslog("ERROR: cannot parse VDR version string '%s'",vv);
    84   return true;
    85 }
    86 
    87 // -- cScanDir --------------------------------------------------------------
    88 
    89 bool cScanDir::ScanDir(cFileSource *src, const char *subdir, eScanType type, const char * const *spec, const char * const *excl, bool recursiv)
    90 {
    91   bool res=true;
    92   char *dir, *f=0;
    93   asprintf(&dir,subdir ? "%s/%s":"%s",src->BaseDir(),subdir);
    94   DIR *d=opendir(dir);
    95   if(d) {
    96     struct dirent64 *e;
    97     while((e=readdir64(d))) {
    98       if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue;
    99       free(f);
   100       if(!(f=AddPath(dir,e->d_name))) continue;
   101       struct stat64 st;
   102       if(stat64(f,&st)<0) {
   103         esyslog("ERROR: stat(1) %s: %s",f,strerror(errno));
   104         continue;
   105         }
   106       if(S_ISLNK(st.st_mode)) {
   107         char *of=f; f=ReadLink(of); free(of);
   108         if(!f) continue;
   109         if(stat64(f,&st)<0) {
   110           esyslog("ERROR: stat(2) %s: %s",f,strerror(errno));
   111           continue;
   112           }
   113         }
   114       if(S_ISDIR(st.st_mode)) {
   115         if(type==stFile && recursiv) {
   116           char *s;
   117           asprintf(&s,subdir ? "%2$s/%1$s":"%s",e->d_name,subdir);
   118           res=ScanDir(src,s,type,spec,excl,recursiv);
   119           free(s);
   120           if(!res) break;
   121           continue;
   122           }
   123         if(type!=stDir) continue;
   124         }
   125       if(S_ISREG(st.st_mode)) {
   126         if(type!=stFile) continue;
   127         if(spec) {
   128           bool ok=false;
   129           for(const char * const *m=spec; *m; m++) {
   130             int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
   131             if(n==0) { ok=true; break; }
   132             if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno));
   133             }
   134           if(!ok) continue;
   135           }
   136         if(excl) {
   137           bool ok=true;
   138           for(const char * const *m=excl; *m; m++) {
   139             int n=fnmatch(*m,e->d_name,FNM_CASEFOLD);
   140             if(n==0) { ok=false; break; }
   141             if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno));
   142             }
   143           if(!ok) continue;
   144           }
   145         }
   146       DoItem(src,subdir,e->d_name);
   147       }
   148     closedir(d);
   149     }
   150   else {
   151     esyslog("ERROR: opendir %s: %s",dir,strerror(errno));
   152     res=false;
   153     }
   154   free(dir); free(f);
   155   return res;
   156 }
   157 
   158 // -- cFileObj --------------------------------------------------------------
   159 
   160 cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type)
   161 {
   162   path=fpath=0;
   163   source=Source;
   164   subdir=Subdir ? strdup(Subdir):0;
   165   name=Name ? strdup(Name):0;
   166   type=Type;
   167   Set();
   168 }
   169 
   170 cFileObj::cFileObj(const cFileObj *obj)
   171 {
   172   path=fpath=0;
   173   source=obj->source;
   174   subdir=obj->subdir ? strdup(obj->subdir):0;
   175   name=obj->name ? strdup(obj->name):0;
   176   type=obj->type;
   177   Set();
   178 }
   179 
   180 cFileObj::~cFileObj()
   181 {
   182   free(name);
   183   free(subdir);
   184   free(path);
   185   free(fpath);
   186 }
   187 
   188 int cFileObj::Compare(const cListObject &ListObject) const
   189 {
   190   cFileObj *obj=(cFileObj *)&ListObject;
   191   if(type==otParent) return obj->type==otParent ? 0:-1;
   192   if(obj->type==otParent) return 1;
   193   if(type==otBase) return obj->type==otBase ? 0:1;
   194   if(obj->type==otBase) return -1;
   195   if(type!=obj->type) {
   196     if(type==otFile) return 1;
   197     return -1;
   198     }
   199   return strcasecmp(path,obj->path);
   200 }
   201 
   202 void cFileObj::SplitAndSet(const char *Path)
   203 {
   204   free(subdir); subdir=0;
   205   const char *p=Path;
   206   if(Path[0]=='/') {
   207     int l=strlen(source->BaseDir());
   208     if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
   209     else {
   210       l=strlen(source->RealBaseDir());
   211       if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
   212       else {
   213         char buff[strlen(Path)+5];
   214         strcpy(buff,"/");
   215         p++;
   216         while(1) {
   217           char real[PATH_MAX+1];
   218           if(!realpath(buff,real)) {
   219             if(errno!=ENOENT && errno!=ENOTDIR)
   220               esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
   221             p=Path+1;
   222             break;
   223             }
   224           if(!strncasecmp(real,source->RealBaseDir(),l))
   225             break;
   226           const char *r=index(p,'/');
   227           if(!r) {
   228             esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
   229             p=Path+1;
   230             break;
   231             }
   232           strn0cpy(buff,Path,r-Path+1);
   233           p=r+1;
   234           }
   235         }
   236       }
   237     }
   238 
   239   const char *s=rindex(p,'/');
   240   if(s) {
   241     const int l=s-p+1;
   242     subdir=MALLOC(char,l);
   243     if(subdir) strn0cpy(subdir,p,l);
   244     SetName(s+1);
   245     }
   246   else
   247     SetName(p);
   248 }
   249 
   250 void cFileObj::SetName(const char *Name)
   251 {
   252   free(name);
   253   name=Name ? strdup(Name):0;
   254   Set();
   255 }
   256 
   257 void cFileObj::Set(void)
   258 {
   259   free(path); path=0;
   260   asprintf(&path,subdir ? "%2$s/%1$s":"%s",name,subdir);
   261   free(fpath); fpath=0;
   262   MakeFullName(&fpath,name);
   263 }
   264 
   265 void cFileObj::MakeFullName(char **fp, const char *Name)
   266 {
   267   asprintf(fp,subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
   268 }
   269 
   270 bool cFileObj::GuessType(void)
   271 {
   272   struct stat64 ds;
   273   if(!stat64(fpath,&ds)) {
   274     if(S_ISREG(ds.st_mode))      type=otFile;
   275     else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
   276     else return false;
   277     return true;
   278     }
   279   return false;
   280 }
   281 
   282 bool cFileObj::Exists(void)
   283 {
   284   if(type==otFile) {
   285     struct stat64 ds;
   286     if(!stat64(fpath,&ds) &&
   287        S_ISREG(ds.st_mode) &&
   288        !access(fpath,R_OK)) return true;
   289     }
   290   return false;
   291 }
   292 
   293 bool cFileObj::TestName(const char *newName)
   294 {
   295   bool r=false;
   296   if(type==otFile) {
   297     char *fname;
   298     MakeFullName(&fname,newName);
   299     if(access(fname,F_OK)==0) r=true;
   300     free(fname);
   301     }
   302   return r;
   303 }
   304 
   305 bool cFileObj::Rename(const char *newName)
   306 {
   307   bool r=false;
   308   if(type==otFile) {
   309     char *fname;
   310     MakeFullName(&fname,newName);
   311     if(access(fname,F_OK) && (!rename(fpath,fname))) {
   312       SetName(newName);
   313       r=true;
   314       }
   315     free(fname);
   316     }
   317   return r;
   318 }
   319 
   320 bool cFileObj::Create(const char *newName)
   321 {
   322   bool r=false;
   323   if(type==otFile) {
   324     char *fname;
   325     MakeFullName(&fname,newName);
   326     FILE *newf;
   327     if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
   328       fclose(newf);
   329       SetName(newName);
   330       r=true;
   331       }
   332     free(fname);
   333     }
   334   return r;
   335 }
   336 
   337 bool cFileObj::Delete(void)
   338 {
   339   if(type==otFile && !unlink(fpath)) return true;
   340   return false;
   341 }
   342 
   343 // -- cDirList --------------------------------------------------------------
   344 
   345 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
   346 {
   347   static const char *excl_s[] = { ".*",0 };
   348 
   349   bool res=false;
   350   Clear();
   351   if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
   352   otype=otDir;
   353   if(ScanDir(src,subdir,stDir,0,0,false)) {
   354     otype=otFile;
   355     if(!excl) excl=excl_s;
   356     if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
   357     }
   358   Sort();
   359   return res;
   360 }
   361 
   362 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
   363 {
   364   Add(new cFileObj(src,subdir,name,otype));
   365 }
   366 
   367 // -- cFileSource --------------------------------------------------------------
   368 
   369 cFileSource::cFileSource(void)
   370 {
   371   browsedir=browseparent=0;
   372   basedir=realbasedir=description=0; useCount=0;
   373   needsmount=false;
   374   include=0; incCount=0;
   375 }
   376 
   377 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
   378 {
   379   browsedir=browseparent=0;
   380   basedir=realbasedir=description=0; useCount=0;
   381   include=0; incCount=0;
   382   Set(Basedir,Description,NeedsMount,Include);
   383 }
   384 
   385 cFileSource::~cFileSource()
   386 {
   387   ClearRemember();
   388   Clear();
   389 }
   390 
   391 void cFileSource::Clear(void)
   392 {
   393   free(basedir); basedir=0;
   394   free(realbasedir); realbasedir=0;
   395   free(description); description=0;
   396   for(int i=0; i<incCount; i++) free(include[i]);
   397   free(include); include=0; incCount=0;
   398 }
   399 
   400 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
   401 {
   402   Clear();
   403   basedir=strdup(Basedir);
   404   description=strdup(Description);
   405   if(Include) {
   406     do {
   407       char *s=index(Include,'/');
   408       int l=s ? s-Include : strlen(Include);
   409       if(l) {
   410         char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
   411         if(s) {
   412           include=s;
   413           include[incCount]=strndup(Include,l);
   414           incCount++;
   415           include[incCount]=0;
   416           }
   417         }
   418       Include+=l+(s ? 1:0);
   419       } while(*Include>0);
   420     }
   421 #ifdef DEBUG
   422   if(include) {
   423     printf("sources: filesource %s includes (count=%d):",basedir,incCount);
   424     for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
   425     printf("\n");
   426     }
   427   else
   428     printf("sources: filesource %s has no includes set\n",basedir);
   429 #endif
   430   needsmount=NeedsMount;
   431 
   432   realbasedir=MALLOC(char,PATH_MAX+1);
   433   if(realpath(basedir,realbasedir)) {
   434     if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
   435     }
   436   else {
   437     switch(errno) {
   438       case EACCES:  esyslog("ERROR: source base %s permission denied",basedir); break;
   439       case ENOENT:  esyslog("ERROR: source base %s not found",basedir); break;
   440       case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
   441       default:      esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
   442       }
   443     strn0cpy(realbasedir,basedir,PATH_MAX);
   444     }
   445 }
   446 
   447 void cFileSource::SetRemember(const char *dir, const char *parent)
   448 {
   449   ClearRemember();
   450   if(dir) browsedir=strdup(dir);
   451   if(parent) browseparent=strdup(parent);
   452 }
   453 
   454 void cFileSource::ClearRemember(void)
   455 {
   456   free(browsedir); browsedir=0;
   457   free(browseparent); browseparent=0;
   458 }
   459 
   460 bool cFileSource::GetRemember(char * &dir, char * &parent)
   461 {
   462   dir=parent=0;
   463   if(browsedir) {
   464     if(browseparent) parent=strdup(browseparent);
   465     dir=strdup(browsedir);
   466     return true;
   467     }
   468   return false;
   469 }
   470 
   471 bool cFileSource::Parse(char *s)
   472 {
   473   char base[256], des[256], incl[256];
   474   int needsmount, n;
   475   if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
   476     char *base2=skipspace(stripspace(base));
   477     int l=strlen(base2);
   478     while(l>0 && base2[l-1]=='/') {
   479       esyslog("WARNING: removing trailing '/' from base %s",base2);
   480       base2[l-1]=0;
   481       l--;
   482       }
   483     Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
   484 
   485     // do some checking of the basedir and issue a warning if apropriate
   486     if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
   487     else {
   488       struct stat64 ds;
   489       if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
   490       else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
   491       }
   492     return true;
   493     }
   494   return false;
   495 }
   496 
   497 bool cFileSource::Action(eAction act)
   498 {
   499   static const char *str[] = { "mount","unmount","eject","status" };
   500   
   501   char *cmd=0;
   502   asprintf(&cmd,"%s %s %s",mountscript,str[act],basedir);
   503   bool res=(system(cmd)==0);
   504   free(cmd);
   505   return res;
   506 }
   507 
   508 bool cFileSource::Mount(void)
   509 {
   510   bool res=false;
   511   if(needsmount && (res=Action(acMount))) ClearRemember();
   512   return res;
   513 }
   514 
   515 bool cFileSource::Unmount(void)
   516 {
   517   bool res=false;
   518   if(needsmount) {
   519     if(!useCount && (res=Action(acUnmount))) ClearRemember();
   520     }
   521   return res;
   522 }
   523 
   524 bool cFileSource::Eject(void)
   525 {
   526   bool res=false;
   527   if(needsmount) {
   528     if(!useCount && (res=Action(acEject))) ClearRemember();
   529     }
   530   return res;
   531 }
   532 
   533 bool cFileSource::Status(void)
   534 {
   535   if(needsmount) return Action(acStatus);
   536   return true;
   537 }
   538 
   539 // -- cFileSources --------------------------------------------------------------
   540 
   541 bool cFileSources::Load(const char *filename, bool dummy)
   542 {
   543   if(cConfig<cFileSource>::Load(filename,true)) {
   544     SetSource(First());
   545     return true;
   546     }
   547   return false;
   548 }
   549 
   550 cFileSource *cFileSources::FindSource(const char *filename)
   551 {
   552   cFileSource *src=First();
   553   while(src) {
   554     if(startswith(filename,src->RealBaseDir())) return src;
   555     src=Next(src);
   556     }
   557   return 0;
   558 }