data.c
author nathan
Sat, 29 Dec 2007 14:47:40 +0100
branchtrunk
changeset 0 474a1293c3c0
child 6 111ef8181229
permissions -rw-r--r--
release 0.10.0
     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 }