nathan@0: /* nathan@0: * MP3/MPlayer plugin to VDR (C++) nathan@0: * nathan@22: * (C) 2001-2009 Stefan Huelswitt nathan@0: * nathan@0: * This code is free software; you can redistribute it and/or nathan@0: * modify it under the terms of the GNU General Public License nathan@0: * as published by the Free Software Foundation; either version 2 nathan@0: * of the License, or (at your option) any later version. nathan@0: * nathan@0: * This code is distributed in the hope that it will be useful, nathan@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of nathan@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nathan@0: * GNU General Public License for more details. nathan@0: * nathan@0: * You should have received a copy of the GNU General Public License nathan@0: * along with this program; if not, write to the Free Software nathan@0: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. nathan@0: * Or, point your browser to http://www.gnu.org/copyleft/gpl.html nathan@0: */ nathan@0: nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@29: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: #include nathan@0: nathan@0: #include nathan@0: nathan@0: #include "common.h" nathan@0: #include "data.h" nathan@0: #include "data-src.h" nathan@0: nathan@0: // ---------------------------------------------------------------- nathan@0: nathan@0: const char *mountscript = "mount.sh"; nathan@0: nathan@0: char *Quote(const char *str) nathan@0: { nathan@0: char *nstr=MALLOC(char,strlen(str)*2); nathan@0: char *p=nstr; nathan@0: while(*str) { nathan@0: switch(*str) { nathan@0: case '$': // dollar nathan@0: case '\\': // backslash nathan@0: case '\"': // double quote nathan@0: case '`': // back tick nathan@0: *p++='\\'; break; nathan@0: } nathan@0: *p++=*str++; nathan@0: } nathan@0: *p=0; nathan@0: return nstr; nathan@0: } nathan@0: nathan@0: char *AddPath(const char *dir, const char *filename) nathan@0: { nathan@29: char *name=aprintf("%s/%s",dir,filename); nathan@0: return name; nathan@0: } nathan@0: nathan@0: bool CheckVDRVersion(int Version, int Major, int Minor, const char *text) nathan@0: { nathan@0: static char vv[] = VDRVERSION; nathan@0: int version, major, minor; nathan@0: if(sscanf(vv,"%d.%d.%d",&version,&major,&minor)==3) { nathan@0: if(versionBaseDir(),subdir); nathan@0: DIR *d=opendir(dir); nathan@0: if(d) { nathan@0: struct dirent64 *e; nathan@0: while((e=readdir64(d))) { nathan@0: if(!strcmp(e->d_name,".") || !strcmp(e->d_name,"..")) continue; nathan@0: free(f); nathan@0: if(!(f=AddPath(dir,e->d_name))) continue; nathan@0: struct stat64 st; nathan@0: if(stat64(f,&st)<0) { nathan@0: esyslog("ERROR: stat(1) %s: %s",f,strerror(errno)); nathan@0: continue; nathan@0: } nathan@0: if(S_ISLNK(st.st_mode)) { nathan@0: char *of=f; f=ReadLink(of); free(of); nathan@0: if(!f) continue; nathan@0: if(stat64(f,&st)<0) { nathan@0: esyslog("ERROR: stat(2) %s: %s",f,strerror(errno)); nathan@0: continue; nathan@0: } nathan@0: } nathan@0: if(S_ISDIR(st.st_mode)) { nathan@0: if(type==stFile && recursiv) { nathan@29: char *s=aprintf(subdir ? "%2$s/%1$s":"%s",e->d_name,subdir); nathan@32: ScanDir(src,s,type,spec,excl,recursiv); nathan@0: free(s); nathan@0: continue; nathan@0: } nathan@0: if(type!=stDir) continue; nathan@0: } nathan@0: if(S_ISREG(st.st_mode)) { nathan@0: if(type!=stFile) continue; nathan@0: if(spec) { nathan@0: bool ok=false; nathan@0: for(const char * const *m=spec; *m; m++) { nathan@0: int n=fnmatch(*m,e->d_name,FNM_CASEFOLD); nathan@0: if(n==0) { ok=true; break; } nathan@0: if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(1) %s: %s",*m,strerror(errno)); nathan@0: } nathan@0: if(!ok) continue; nathan@0: } nathan@0: if(excl) { nathan@0: bool ok=true; nathan@0: for(const char * const *m=excl; *m; m++) { nathan@0: int n=fnmatch(*m,e->d_name,FNM_CASEFOLD); nathan@0: if(n==0) { ok=false; break; } nathan@0: if(n!=FNM_NOMATCH) esyslog("ERROR: fnmatch(2) %s: %s",*m,strerror(errno)); nathan@0: } nathan@0: if(!ok) continue; nathan@0: } nathan@0: } nathan@0: DoItem(src,subdir,e->d_name); nathan@0: } nathan@0: closedir(d); nathan@0: } nathan@0: else { nathan@0: esyslog("ERROR: opendir %s: %s",dir,strerror(errno)); nathan@0: res=false; nathan@0: } nathan@0: free(dir); free(f); nathan@0: return res; nathan@0: } nathan@0: nathan@0: // -- cFileObj -------------------------------------------------------------- nathan@0: nathan@0: cFileObj::cFileObj(cFileSource *Source, const char *Subdir, const char *Name, const eObjType Type) nathan@0: { nathan@0: path=fpath=0; nathan@0: source=Source; nathan@0: subdir=Subdir ? strdup(Subdir):0; nathan@0: name=Name ? strdup(Name):0; nathan@0: type=Type; nathan@0: Set(); nathan@0: } nathan@0: nathan@0: cFileObj::cFileObj(const cFileObj *obj) nathan@0: { nathan@0: path=fpath=0; nathan@0: source=obj->source; nathan@0: subdir=obj->subdir ? strdup(obj->subdir):0; nathan@0: name=obj->name ? strdup(obj->name):0; nathan@0: type=obj->type; nathan@0: Set(); nathan@0: } nathan@0: nathan@0: cFileObj::~cFileObj() nathan@0: { nathan@0: free(name); nathan@0: free(subdir); nathan@0: free(path); nathan@0: free(fpath); nathan@0: } nathan@0: nathan@0: int cFileObj::Compare(const cListObject &ListObject) const nathan@0: { nathan@0: cFileObj *obj=(cFileObj *)&ListObject; nathan@0: if(type==otParent) return obj->type==otParent ? 0:-1; nathan@0: if(obj->type==otParent) return 1; nathan@0: if(type==otBase) return obj->type==otBase ? 0:1; nathan@0: if(obj->type==otBase) return -1; nathan@0: if(type!=obj->type) { nathan@0: if(type==otFile) return 1; nathan@0: return -1; nathan@0: } nathan@0: return strcasecmp(path,obj->path); nathan@0: } nathan@0: nathan@0: void cFileObj::SplitAndSet(const char *Path) nathan@0: { nathan@0: free(subdir); subdir=0; nathan@0: const char *p=Path; nathan@0: if(Path[0]=='/') { nathan@0: int l=strlen(source->BaseDir()); nathan@0: if(!strncasecmp(Path,source->BaseDir(),l)) p+=l+1; nathan@0: else { nathan@0: l=strlen(source->RealBaseDir()); nathan@0: if(!strncasecmp(Path,source->RealBaseDir(),l)) p+=l+1; nathan@0: else { nathan@0: char buff[strlen(Path)+5]; nathan@0: strcpy(buff,"/"); nathan@0: p++; nathan@0: while(1) { nathan@0: char real[PATH_MAX+1]; nathan@0: if(!realpath(buff,real)) { nathan@0: if(errno!=ENOENT && errno!=ENOTDIR) nathan@0: esyslog("ERROR: realpath: %s: %s",buff,strerror(errno)); nathan@0: p=Path+1; nathan@0: break; nathan@0: } nathan@0: if(!strncasecmp(real,source->RealBaseDir(),l)) nathan@0: break; nathan@0: const char *r=index(p,'/'); nathan@0: if(!r) { nathan@0: esyslog("ERROR: can't find source basedir in '%s'. Outside source?",Path); nathan@0: p=Path+1; nathan@0: break; nathan@0: } nathan@0: strn0cpy(buff,Path,r-Path+1); nathan@0: p=r+1; nathan@0: } nathan@0: } nathan@0: } nathan@0: } nathan@0: nathan@0: const char *s=rindex(p,'/'); nathan@0: if(s) { nathan@0: const int l=s-p+1; nathan@0: subdir=MALLOC(char,l); nathan@0: if(subdir) strn0cpy(subdir,p,l); nathan@0: SetName(s+1); nathan@0: } nathan@0: else nathan@0: SetName(p); nathan@0: } nathan@0: nathan@0: void cFileObj::SetName(const char *Name) nathan@0: { nathan@0: free(name); nathan@0: name=Name ? strdup(Name):0; nathan@0: Set(); nathan@0: } nathan@0: nathan@0: void cFileObj::Set(void) nathan@0: { nathan@29: free(path); nathan@29: path=aprintf(subdir ? "%2$s/%1$s":"%s",name,subdir); nathan@0: free(fpath); fpath=0; nathan@0: MakeFullName(&fpath,name); nathan@0: } nathan@0: nathan@0: void cFileObj::MakeFullName(char **fp, const char *Name) nathan@0: { nathan@29: *fp=aprintf(subdir ? "%1$s/%3$s/%2$s":"%s/%s",source->BaseDir(),Name,subdir); nathan@0: } nathan@0: nathan@0: bool cFileObj::GuessType(void) nathan@0: { nathan@0: struct stat64 ds; nathan@0: if(!stat64(fpath,&ds)) { nathan@0: if(S_ISREG(ds.st_mode)) type=otFile; nathan@0: else if(S_ISDIR(ds.st_mode)) type=subdir ? otDir:otBase; nathan@0: else return false; nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cFileObj::Exists(void) nathan@0: { nathan@0: if(type==otFile) { nathan@0: struct stat64 ds; nathan@0: if(!stat64(fpath,&ds) && nathan@0: S_ISREG(ds.st_mode) && nathan@0: !access(fpath,R_OK)) return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cFileObj::TestName(const char *newName) nathan@0: { nathan@0: bool r=false; nathan@0: if(type==otFile) { nathan@0: char *fname; nathan@0: MakeFullName(&fname,newName); nathan@0: if(access(fname,F_OK)==0) r=true; nathan@0: free(fname); nathan@0: } nathan@0: return r; nathan@0: } nathan@0: nathan@0: bool cFileObj::Rename(const char *newName) nathan@0: { nathan@0: bool r=false; nathan@0: if(type==otFile) { nathan@0: char *fname; nathan@0: MakeFullName(&fname,newName); nathan@0: if(access(fname,F_OK) && (!rename(fpath,fname))) { nathan@0: SetName(newName); nathan@0: r=true; nathan@0: } nathan@0: free(fname); nathan@0: } nathan@0: return r; nathan@0: } nathan@0: nathan@0: bool cFileObj::Create(const char *newName) nathan@0: { nathan@0: bool r=false; nathan@0: if(type==otFile) { nathan@0: char *fname; nathan@0: MakeFullName(&fname,newName); nathan@0: FILE *newf; nathan@0: if(access(fname,F_OK) && (newf=fopen(fname,"w"))) { nathan@0: fclose(newf); nathan@0: SetName(newName); nathan@0: r=true; nathan@0: } nathan@0: free(fname); nathan@0: } nathan@0: return r; nathan@0: } nathan@0: nathan@0: bool cFileObj::Delete(void) nathan@0: { nathan@0: if(type==otFile && !unlink(fpath)) return true; nathan@0: return false; nathan@0: } nathan@0: nathan@0: // -- cDirList -------------------------------------------------------------- nathan@0: nathan@0: bool cDirList::Load(cFileSource *src, const char *subdir, const char * const *excl) nathan@0: { nathan@0: static const char *excl_s[] = { ".*",0 }; nathan@0: nathan@0: bool res=false; nathan@0: Clear(); nathan@0: if(subdir) Add(new cFileObj(src,subdir,"..",otParent)); nathan@0: otype=otDir; nathan@0: if(ScanDir(src,subdir,stDir,0,0,false)) { nathan@0: otype=otFile; nathan@0: if(!excl) excl=excl_s; nathan@0: if(ScanDir(src,subdir,stFile,src->Include(),excl,false)) res=true; nathan@0: } nathan@0: Sort(); nathan@0: return res; nathan@0: } nathan@0: nathan@0: void cDirList::DoItem(cFileSource *src, const char *subdir, const char *name) nathan@0: { nathan@0: Add(new cFileObj(src,subdir,name,otype)); nathan@0: } nathan@0: nathan@0: // -- cFileSource -------------------------------------------------------------- nathan@0: nathan@0: cFileSource::cFileSource(void) nathan@0: { nathan@0: browsedir=browseparent=0; nathan@0: basedir=realbasedir=description=0; useCount=0; nathan@0: needsmount=false; nathan@0: include=0; incCount=0; nathan@0: } nathan@0: nathan@0: cFileSource::cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include) nathan@0: { nathan@0: browsedir=browseparent=0; nathan@0: basedir=realbasedir=description=0; useCount=0; nathan@0: include=0; incCount=0; nathan@0: Set(Basedir,Description,NeedsMount,Include); nathan@0: } nathan@0: nathan@0: cFileSource::~cFileSource() nathan@0: { nathan@0: ClearRemember(); nathan@0: Clear(); nathan@0: } nathan@0: nathan@0: void cFileSource::Clear(void) nathan@0: { nathan@0: free(basedir); basedir=0; nathan@0: free(realbasedir); realbasedir=0; nathan@0: free(description); description=0; nathan@0: for(int i=0; i0); nathan@0: } nathan@0: #ifdef DEBUG nathan@0: if(include) { nathan@0: printf("sources: filesource %s includes (count=%d):",basedir,incCount); nathan@0: for(int i=0; i=3) { nathan@0: char *base2=skipspace(stripspace(base)); nathan@0: int l=strlen(base2); nathan@0: while(l>0 && base2[l-1]=='/') { nathan@0: esyslog("WARNING: removing trailing '/' from base %s",base2); nathan@0: base2[l-1]=0; nathan@0: l--; nathan@0: } nathan@0: Set(base2,skipspace(stripspace(des)),needsmount!=0,n>3?skipspace(stripspace(incl)):0); nathan@0: nathan@0: // do some checking of the basedir and issue a warning if apropriate nathan@0: if(access(realbasedir,R_OK)) { esyslog("WARNING: source base %s not found/permission denied",realbasedir); } nathan@0: else { nathan@0: struct stat64 ds; nathan@0: if(lstat64(realbasedir,&ds)) { esyslog("WARNING: can't stat source base %s",realbasedir); } nathan@0: else if(!S_ISDIR(ds.st_mode)) { esyslog("WARNING: source base %s is not a directory",realbasedir); } nathan@0: } nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: bool cFileSource::Action(eAction act) nathan@0: { nathan@6: static const char *str[] = { "mount","unmount","eject","status" }; nathan@0: nathan@29: char *cmd=aprintf("%s %s %s",mountscript,str[act],basedir); nathan@0: bool res=(system(cmd)==0); nathan@0: free(cmd); nathan@0: return res; nathan@0: } nathan@0: nathan@0: bool cFileSource::Mount(void) nathan@0: { nathan@0: bool res=false; nathan@0: if(needsmount && (res=Action(acMount))) ClearRemember(); nathan@0: return res; nathan@0: } nathan@0: nathan@0: bool cFileSource::Unmount(void) nathan@0: { nathan@0: bool res=false; nathan@0: if(needsmount) { nathan@0: if(!useCount && (res=Action(acUnmount))) ClearRemember(); nathan@0: } nathan@0: return res; nathan@0: } nathan@0: nathan@0: bool cFileSource::Eject(void) nathan@0: { nathan@0: bool res=false; nathan@0: if(needsmount) { nathan@0: if(!useCount && (res=Action(acEject))) ClearRemember(); nathan@0: } nathan@0: return res; nathan@0: } nathan@0: nathan@0: bool cFileSource::Status(void) nathan@0: { nathan@0: if(needsmount) return Action(acStatus); nathan@0: return true; nathan@0: } nathan@0: nathan@0: // -- cFileSources -------------------------------------------------------------- nathan@0: nathan@0: bool cFileSources::Load(const char *filename, bool dummy) nathan@0: { nathan@0: if(cConfig::Load(filename,true)) { nathan@0: SetSource(First()); nathan@0: return true; nathan@0: } nathan@0: return false; nathan@0: } nathan@0: nathan@0: cFileSource *cFileSources::FindSource(const char *filename) nathan@0: { nathan@0: cFileSource *src=First(); nathan@0: while(src) { nathan@0: if(startswith(filename,src->RealBaseDir())) return src; nathan@0: src=Next(src); nathan@0: } nathan@0: return 0; nathan@0: }