data.c
branchtrunk
changeset 0 474a1293c3c0
child 6 111ef8181229
equal deleted inserted replaced
-1:000000000000 0:474a1293c3c0
       
     1 /*
       
     2  * MP3/MPlayer plugin to VDR (C++)
       
     3  *
       
     4  * (C) 2001-2006 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 #if APIVERSNUM >= 10315
       
   189 int cFileObj::Compare(const cListObject &ListObject) const
       
   190 {
       
   191   cFileObj *obj=(cFileObj *)&ListObject;
       
   192   if(type==otParent) return obj->type==otParent ? 0:-1;
       
   193   if(obj->type==otParent) return 1;
       
   194   if(type==otBase) return obj->type==otBase ? 0:1;
       
   195   if(obj->type==otBase) return -1;
       
   196   if(type!=obj->type) {
       
   197     if(type==otFile) return 1;
       
   198     return -1;
       
   199     }
       
   200   return strcasecmp(path,obj->path);
       
   201 }
       
   202 #else
       
   203 bool cFileObj::operator<(const cListObject &ListObject)
       
   204 {
       
   205   cFileObj *obj=(cFileObj *)&ListObject;
       
   206   if(type==otParent) return obj->type==otParent ? false:true;
       
   207   if(obj->type==otParent) return false;
       
   208   if(type==otBase) return false;
       
   209   if(obj->type==otBase) return true;
       
   210   if(type!=obj->type) {
       
   211     if(type==otFile) return false;
       
   212     return true;
       
   213     }
       
   214   return strcasecmp(path,obj->path)<0;
       
   215 }
       
   216 #endif
       
   217 
       
   218 void cFileObj::SplitAndSet(const char *Path)
       
   219 {
       
   220   free(subdir); subdir=0;
       
   221   const char *p=Path;
       
   222   if(Path[0]=='/') {
       
   223     int l=strlen(source->BaseDir());
       
   224     if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1;
       
   225     else {
       
   226       l=strlen(source->RealBaseDir());
       
   227       if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1;
       
   228       else {
       
   229         char buff[strlen(Path)+5];
       
   230         strcpy(buff,"/");
       
   231         p++;
       
   232         while(1) {
       
   233           char real[PATH_MAX+1];
       
   234           if(!realpath(buff,real)) {
       
   235             if(errno!=ENOENT && errno!=ENOTDIR)
       
   236               esyslog("ERROR: realpath: %s: %s",buff,strerror(errno));
       
   237             p=Path+1;
       
   238             break;
       
   239             }
       
   240           if(!strncasecmp(real,source->RealBaseDir(),l))
       
   241             break;
       
   242           const char *r=index(p,'/');
       
   243           if(!r) {
       
   244             esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path);
       
   245             p=Path+1;
       
   246             break;
       
   247             }
       
   248           strn0cpy(buff,Path,r-Path+1);
       
   249           p=r+1;
       
   250           }
       
   251         }
       
   252       }
       
   253     }
       
   254 
       
   255   const char *s=rindex(p,'/');
       
   256   if(s) {
       
   257     const int l=s-p+1;
       
   258     subdir=MALLOC(char,l);
       
   259     if(subdir) strn0cpy(subdir,p,l);
       
   260     SetName(s+1);
       
   261     }
       
   262   else
       
   263     SetName(p);
       
   264 }
       
   265 
       
   266 void cFileObj::SetName(const char *Name)
       
   267 {
       
   268   free(name);
       
   269   name=Name ? strdup(Name):0;
       
   270   Set();
       
   271 }
       
   272 
       
   273 void cFileObj::Set(void)
       
   274 {
       
   275   free(path); path=0;
       
   276   asprintf(&path,subdir ? "%2$s/%1$s":"%s",name,subdir);
       
   277   free(fpath); fpath=0;
       
   278   MakeFullName(&fpath,name);
       
   279 }
       
   280 
       
   281 void cFileObj::MakeFullName(char **fp, const char *Name)
       
   282 {
       
   283   asprintf(fp,subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir);
       
   284 }
       
   285 
       
   286 bool cFileObj::GuessType(void)
       
   287 {
       
   288   struct stat64 ds;
       
   289   if(!stat64(fpath,&ds)) {
       
   290     if(S_ISREG(ds.st_mode))      type=otFile;
       
   291     else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase;
       
   292     else return false;
       
   293     return true;
       
   294     }
       
   295   return false;
       
   296 }
       
   297 
       
   298 bool cFileObj::Exists(void)
       
   299 {
       
   300   if(type==otFile) {
       
   301     struct stat64 ds;
       
   302     if(!stat64(fpath,&ds) &&
       
   303        S_ISREG(ds.st_mode) &&
       
   304        !access(fpath,R_OK)) return true;
       
   305     }
       
   306   return false;
       
   307 }
       
   308 
       
   309 bool cFileObj::TestName(const char *newName)
       
   310 {
       
   311   bool r=false;
       
   312   if(type==otFile) {
       
   313     char *fname;
       
   314     MakeFullName(&fname,newName);
       
   315     if(access(fname,F_OK)==0) r=true;
       
   316     free(fname);
       
   317     }
       
   318   return r;
       
   319 }
       
   320 
       
   321 bool cFileObj::Rename(const char *newName)
       
   322 {
       
   323   bool r=false;
       
   324   if(type==otFile) {
       
   325     char *fname;
       
   326     MakeFullName(&fname,newName);
       
   327     if(access(fname,F_OK) && (!rename(fpath,fname))) {
       
   328       SetName(newName);
       
   329       r=true;
       
   330       }
       
   331     free(fname);
       
   332     }
       
   333   return r;
       
   334 }
       
   335 
       
   336 bool cFileObj::Create(const char *newName)
       
   337 {
       
   338   bool r=false;
       
   339   if(type==otFile) {
       
   340     char *fname;
       
   341     MakeFullName(&fname,newName);
       
   342     FILE *newf;
       
   343     if(access(fname,F_OK) && (newf=fopen(fname,"w"))) {
       
   344       fclose(newf);
       
   345       SetName(newName);
       
   346       r=true;
       
   347       }
       
   348     free(fname);
       
   349     }
       
   350   return r;
       
   351 }
       
   352 
       
   353 bool cFileObj::Delete(void)
       
   354 {
       
   355   if(type==otFile && !unlink(fpath)) return true;
       
   356   return false;
       
   357 }
       
   358 
       
   359 // -- cDirList --------------------------------------------------------------
       
   360 
       
   361 bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl)
       
   362 {
       
   363   static const char *excl_s[] = { ".*",0 };
       
   364 
       
   365   bool res=false;
       
   366   Clear();
       
   367   if(subdir) Add(new cFileObj(src,subdir,"..",otParent));
       
   368   otype=otDir;
       
   369   if(ScanDir(src,subdir,stDir,0,0,false)) {
       
   370     otype=otFile;
       
   371     if(!excl) excl=excl_s;
       
   372     if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true;
       
   373     }
       
   374   Sort();
       
   375   return res;
       
   376 }
       
   377 
       
   378 void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name)
       
   379 {
       
   380   Add(new cFileObj(src,subdir,name,otype));
       
   381 }
       
   382 
       
   383 // -- cFileSource --------------------------------------------------------------
       
   384 
       
   385 cFileSource::cFileSource(void)
       
   386 {
       
   387   browsedir=browseparent=0;
       
   388   basedir=realbasedir=description=0; useCount=0;
       
   389   needsmount=false;
       
   390   include=0; incCount=0;
       
   391 }
       
   392 
       
   393 cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
       
   394 {
       
   395   browsedir=browseparent=0;
       
   396   basedir=realbasedir=description=0; useCount=0;
       
   397   include=0; incCount=0;
       
   398   Set(Basedir,Description,NeedsMount,Include);
       
   399 }
       
   400 
       
   401 cFileSource::~cFileSource()
       
   402 {
       
   403   ClearRemember();
       
   404   Clear();
       
   405 }
       
   406 
       
   407 void cFileSource::Clear(void)
       
   408 {
       
   409   free(basedir); basedir=0;
       
   410   free(realbasedir); realbasedir=0;
       
   411   free(description); description=0;
       
   412   for(int i=0; i<incCount; i++) free(include[i]);
       
   413   free(include); include=0; incCount=0;
       
   414 }
       
   415 
       
   416 void cFileSource::Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include)
       
   417 {
       
   418   Clear();
       
   419   basedir=strdup(Basedir);
       
   420   description=strdup(Description);
       
   421   if(Include) {
       
   422     do {
       
   423       char *s=index(Include,'/');
       
   424       int l=s ? s-Include : strlen(Include);
       
   425       if(l) {
       
   426         char **s=(char **)realloc(include,(incCount+2)*sizeof(char *));
       
   427         if(s) {
       
   428           include=s;
       
   429           include[incCount]=strndup(Include,l);
       
   430           incCount++;
       
   431           include[incCount]=0;
       
   432           }
       
   433         }
       
   434       Include+=l+(s ? 1:0);
       
   435       } while(*Include>0);
       
   436     }
       
   437 #ifdef DEBUG
       
   438   if(include) {
       
   439     printf("sources: filesource %s includes (count=%d):",basedir,incCount);
       
   440     for(int i=0; i<incCount; i++) printf(" '%s'",include[i]);
       
   441     printf("\n");
       
   442     }
       
   443   else
       
   444     printf("sources: filesource %s has no includes set\n",basedir);
       
   445 #endif
       
   446   needsmount=NeedsMount;
       
   447 
       
   448   realbasedir=MALLOC(char,PATH_MAX+1);
       
   449   if(realpath(basedir,realbasedir)) {
       
   450     if(strcmp(basedir,realbasedir)) { esyslog("WARNING: source base %s expands to %s",basedir,realbasedir); }
       
   451     }
       
   452   else {
       
   453     switch(errno) {
       
   454       case EACCES:  esyslog("ERROR: source base %s permission denied",basedir); break;
       
   455       case ENOENT:  esyslog("ERROR: source base %s not found",basedir); break;
       
   456       case ENOTDIR: esyslog("ERROR: source base %s has invalid path",basedir); break;
       
   457       default:      esyslog("ERROR: source base %s realpath: %s",basedir,strerror(errno)); break;
       
   458       }
       
   459     strn0cpy(realbasedir,basedir,PATH_MAX);
       
   460     }
       
   461 }
       
   462 
       
   463 void cFileSource::SetRemember(const char *dir, const char *parent)
       
   464 {
       
   465   ClearRemember();
       
   466   if(dir) browsedir=strdup(dir);
       
   467   if(parent) browseparent=strdup(parent);
       
   468 }
       
   469 
       
   470 void cFileSource::ClearRemember(void)
       
   471 {
       
   472   free(browsedir); browsedir=0;
       
   473   free(browseparent); browseparent=0;
       
   474 }
       
   475 
       
   476 bool cFileSource::GetRemember(char * &dir, char * &parent)
       
   477 {
       
   478   dir=parent=0;
       
   479   if(browsedir) {
       
   480     if(browseparent) parent=strdup(browseparent);
       
   481     dir=strdup(browsedir);
       
   482     return true;
       
   483     }
       
   484   return false;
       
   485 }
       
   486 
       
   487 bool cFileSource::Parse(char *s)
       
   488 {
       
   489   char base[256], des[256], incl[256];
       
   490   int needsmount, n;
       
   491   if((n=sscanf(s,"%255[^;];%255[^;];%d;%255[^;]",base,des,&needsmount,incl))>=3) {
       
   492     char *base2=skipspace(stripspace(base));
       
   493     int l=strlen(base2);
       
   494     while(l>0 && base2[l-1]=='/') {
       
   495       esyslog("WARNING: removing trailing '/' from base %s",base2);
       
   496       base2[l-1]=0;
       
   497       l--;
       
   498       }
       
   499     Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0);
       
   500 
       
   501     // do some checking of the basedir and issue a warning if apropriate
       
   502     if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); }
       
   503     else {
       
   504       struct stat64 ds;
       
   505       if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); }
       
   506       else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); }
       
   507       }
       
   508     return true;
       
   509     }
       
   510   return false;
       
   511 }
       
   512 
       
   513 bool cFileSource::Action(eAction act)
       
   514 {
       
   515   static char *str[] = { "mount","unmount","eject","status" };
       
   516   
       
   517   char *cmd=0;
       
   518   asprintf(&cmd,"%s %s %s",mountscript,str[act],basedir);
       
   519   bool res=(system(cmd)==0);
       
   520   free(cmd);
       
   521   return res;
       
   522 }
       
   523 
       
   524 bool cFileSource::Mount(void)
       
   525 {
       
   526   bool res=false;
       
   527   if(needsmount && (res=Action(acMount))) ClearRemember();
       
   528   return res;
       
   529 }
       
   530 
       
   531 bool cFileSource::Unmount(void)
       
   532 {
       
   533   bool res=false;
       
   534   if(needsmount) {
       
   535     if(!useCount && (res=Action(acUnmount))) ClearRemember();
       
   536     }
       
   537   return res;
       
   538 }
       
   539 
       
   540 bool cFileSource::Eject(void)
       
   541 {
       
   542   bool res=false;
       
   543   if(needsmount) {
       
   544     if(!useCount && (res=Action(acEject))) ClearRemember();
       
   545     }
       
   546   return res;
       
   547 }
       
   548 
       
   549 bool cFileSource::Status(void)
       
   550 {
       
   551   if(needsmount) return Action(acStatus);
       
   552   return true;
       
   553 }
       
   554 
       
   555 // -- cFileSources --------------------------------------------------------------
       
   556 
       
   557 bool cFileSources::Load(const char *filename, bool dummy)
       
   558 {
       
   559   if(cConfig<cFileSource>::Load(filename,true)) {
       
   560     SetSource(First());
       
   561     return true;
       
   562     }
       
   563   return false;
       
   564 }
       
   565 
       
   566 cFileSource *cFileSources::FindSource(const char *filename)
       
   567 {
       
   568   cFileSource *src=First();
       
   569   while(src) {
       
   570     if(startswith(filename,src->RealBaseDir())) return src;
       
   571     src=Next(src);
       
   572     }
       
   573   return 0;
       
   574 }