/* filedb.c -- handles: low-level manipulation of the filesystem database files reaction to remote requests for files dprintf'ized, 25feb96 english, 5mar96 */ /* This file is part of the eggdrop source code copyright (c) 1997 Robey Pointer and is distributed according to the GNU general public license. For full details, read the top of 'main.c' or the file called COPYING that was distributed with this code. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "eggdrop.h" #include "tclegg.h" #include "proto.h" #include "files.h" #ifndef NO_FILE_SYSTEM #if HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif extern char botnetnick[]; extern char dccdir[]; extern char tempdir[]; extern int dcc_total; extern struct dcc_t dcc[]; extern int copy_to_tmp; /* where to put the filedb, if not in a hidden '.filedb' file in */ /* each directory */ char filedb_path[121]=""; /* lock the file, using fcntl */ void lockfile(f) FILE *f; { struct flock fl; fl.l_type=F_WRLCK; fl.l_start=0; fl.l_whence=SEEK_SET; fl.l_len=0; /* block on lock: */ fcntl(fileno(f),F_SETLKW,&fl); } /* unlock the file */ void unlockfile(f) FILE *f; { struct flock fl; fl.l_type=F_UNLCK; fl.l_start=0; fl.l_whence=SEEK_SET; fl.l_len=0; fcntl(fileno(f),F_SETLKW,&fl); } /* use a where of 0 to start out, then increment 1 space for each next */ filedb *findmatch(f,lookfor,where) FILE *f; char *lookfor; long *where; { static filedb fdb; char match[256]; strncpy(match,lookfor,255); match[255]=0; /* clip any trailing / */ if ((match[0]) && (match[strlen(match)-1]=='/')) match[strlen(match)-1]=0; fseek(f,*where,SEEK_SET); while (!feof(f)) { *where=ftell(f); fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { if (!(fdb.stat&FILE_UNUSED) && (wild_match_file(match,fdb.filename))) return &fdb; } } return NULL; } filedb *findfile(f,name,where) FILE *f; char *name; long *where; { filedb *fdb; long w=0L; /* force a rewind */ fdb=findmatch(f,name,&w); if (where!=NULL) *where=w; return fdb; } /* alternate version so the buffers don't get overwritten */ filedb *findmatch2(f,lookfor,where) FILE *f; char *lookfor; long *where; { static filedb fdb; char match[256]; strcpy(match,lookfor); /* clip any trailing / */ if ((match[0]) && (match[strlen(match)-1]=='/')) match[strlen(match)-1]=0; fseek(f,*where,SEEK_SET); while (!feof(f)) { *where=ftell(f); fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { if (!(fdb.stat&FILE_UNUSED) && (wild_match_file(match,fdb.filename))) return &fdb; } } return NULL; } filedb *findfile2(f,name,where) FILE *f; char *name; long *where; { filedb *fdb; long w=0L; /* force a rewind */ fdb=findmatch2(f,name,&w); if (where!=NULL) *where=w; return fdb; } long findempty(FILE *f) { long where=0L; filedb fdb; rewind(f); while (!feof(f)) { where=ftell(f); fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { if (fdb.stat&FILE_UNUSED) return where; } } fseek(f,0L,SEEK_END); where=ftell(f); return where; } void filedb_timestamp(f) FILE *f; { filedb fdb; int x; /* read 1st filedb entry if it's there */ rewind(f); x=fread(&fdb,sizeof(filedb),1,f); if (x < 1) { /* MAKE 1st filedb entry then! */ fdb.version=FILEVERSION; fdb.stat=FILE_UNUSED; } fdb.timestamp=time(NULL); rewind(f); fwrite(&fdb,sizeof(filedb),1,f); } /* return 1 if i find a '.files' and convert it */ int convert_old_db(path,newfiledb) char *path,*newfiledb; { FILE *f,*g; char s[256],fn[61],nick[20],tm[20]; filedb fdb; int in_file=0; long where; struct stat st; sprintf(s,"%s/.files",path); f=fopen(s,"r"); if (f==NULL) return 0; g=fopen(newfiledb,"w+b"); if (g==NULL) { putlog(LOG_MISC,"(!) Can't create filedb in %s",newfiledb); fclose(f); return 0; } putlog(LOG_FILES,"*",FILES_CONVERT,path); where=ftell(g); /* scan contents of .files and painstakingly create .filedb entries */ while (!feof(f)) { fgets(s,120,f); if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0; if (!feof(f)) { nsplit(fn,s); rmspace(fn); if ((fn[0]) && (fn[0]!=';') && (fn[0]!='#')) { /* not comment */ if (fn[0]=='-') { /* adjust comment for current file */ if (in_file) { rmspace(s); if (strlen(s)+strlen(fdb.desc)<=600) { strcat(fdb.desc,"\n"); strcat(fdb.desc,s); fseek(g,where,SEEK_SET); fwrite(&fdb,sizeof(filedb),1,g); } } } else { in_file=1; where=ftell(g); rmspace(s); nsplit(nick,s); rmspace(nick); rmspace(s); nsplit(tm,s); rmspace(tm); rmspace(s); if (fn[strlen(fn)-1]=='/') fn[strlen(fn)-1]=0; fdb.version=FILEVERSION; fdb.stat=0; fdb.desc[0]=0; strcpy(fdb.filename,fn); strcpy(fdb.uploader,nick); fdb.gots=atoi(s); fdb.sharelink[0]=0; fdb.uploaded=atol(tm); fdb.flags_req=0; sprintf(s,"%s/%s",path,fn); if (stat(s,&st)==0) { /* file is okay */ if (S_ISDIR(st.st_mode)) { fdb.stat|=FILE_DIR; if (nick[0]=='+') fdb.flags_req=str2flags(&nick[1]); } fdb.size=st.st_size; fwrite(&fdb,sizeof(filedb),1,g); } else in_file=0; /* skip */ } } } } fseek(g,0,SEEK_END); fclose(g); fclose(f); return 1; } void filedb_update(path,f) char *path; FILE *f; { struct dirent *dd; DIR *dir; filedb *fdb,fdb1; char name[61]; long where; struct stat st; char s[512]; /* FIRST: make sure every real file is in the database */ dir=opendir(path); if (dir==NULL) { putlog(LOG_MISC,"*",FILES_NOUPDATE); return; } dd=readdir(dir); while (dd!=NULL) { strncpy(name,dd->d_name,60); name[60]=0; if (NAMLEN(dd)<=60) name[NAMLEN(dd)]=0; else { /* truncate name on disk */ char s1[512],s2[256]; strcpy(s1,path); strcat(s1,"/"); strncat(s1,dd->d_name,NAMLEN(dd)); s1[strlen(path)+NAMLEN(dd)+1]=0; sprintf(s2,"%s/%s",path,name); movefile(s1,s2); } if (name[0]!='.') { sprintf(s,"%s/%s",path,name); stat(s,&st); fdb=findfile(f,name,&where); if (fdb==NULL) { /* new file! */ where=findempty(f); fseek(f,where,SEEK_SET); fdb1.version=FILEVERSION; fdb1.stat=0; /* by default, visible regular file */ strcpy(fdb1.filename,name); fdb1.desc[0]=0; strcpy(fdb1.uploader,botnetnick); fdb1.gots=0; fdb1.flags_req=0; fdb1.uploaded=time(NULL); fdb1.size=st.st_size; fdb1.sharelink[0]=0; if (S_ISDIR(st.st_mode)) fdb1.stat|=FILE_DIR; fwrite(&fdb1,sizeof(filedb),1,f); } else { /* update size if needed */ fdb->size=st.st_size; fseek(f,where,SEEK_SET); fwrite(fdb,sizeof(filedb),1,f); } } dd=readdir(dir); } closedir(dir); /* SECOND: make sure every db file is real */ rewind(f); while (!feof(f)) { where=ftell(f); fread(&fdb1,sizeof(filedb),1,f); if ((!feof(f)) && !(fdb1.stat&FILE_UNUSED) && !(fdb1.sharelink[0])) { sprintf(s,"%s/%s",path,fdb1.filename); if (stat(s,&st)!=0) { /* gone file */ fseek(f,where,SEEK_SET); fdb1.stat|=FILE_UNUSED; fwrite(&fdb1,sizeof(filedb),1,f); /* sunos and others will puke bloody chunks if you write the */ /* last record in a file and then attempt to read to EOF: */ fseek(f,where,SEEK_SET); } } } /* write new timestamp */ filedb_timestamp(f); } int count=0; FILE *filedb_open(char *path) { char s[DIRLEN],npath[DIRLEN]; FILE *f; filedb fdb; struct stat st; if (count>=2) { putlog(LOG_MISC,"*","(@) warning: %d open filedb's",count); } sprintf(npath,"%s%s",dccdir,path); /* use alternate filename if requested */ if (filedb_path[0]) { char s2[DIRLEN],*p; strcpy(s2,path); p=s2; while (*p++) if (*p=='/') *p='.'; sprintf(s,"%sfiledb.%s",filedb_path,s2); if (s[strlen(s)-1]=='.') s[strlen(s)-1]=0; } else sprintf(s,"%s/.filedb",npath); f=fopen(s,"r+b"); if (f==NULL) { /* attempt to convert */ if (convert_old_db(npath,s)) { f=fopen(s,"r+b"); if (f==NULL) { putlog(LOG_MISC,FILES_NOCONVERT,npath); return NULL; } lockfile(f); filedb_update(npath,f); /* make it correct */ count++; return f; } /* create new database and fix it up */ f=fopen(s,"w+b"); if (f==NULL) return NULL; lockfile(f); filedb_update(npath,f); count++; return f; } /* lock it from other bots: */ lockfile(f); /* check the timestamp... */ fread(&fdb,sizeof(filedb),1,f); stat(npath,&st); /* update filedb if: */ /* + it's been 6 hours since it was last updated */ /* + the directory has been visibly modified since then */ /* (6 hours may be a bit often) */ if (((time(NULL)-fdb.timestamp)>(6*3600)) || (fdb.timestampuploader,nick); fdb->uploaded=time(NULL); fseek(f,where,SEEK_SET); fwrite(fdb,sizeof(filedb),1,f); } /* fills fdb if can find a match and returns 1, else returns 0 */ int filedb_match(f,match,fdb,first) FILE *f; char *match; filedb *fdb; int first; { if (first) rewind(f); while (!feof(f)) { fread(fdb,sizeof(filedb),1,f); if (!feof(f)) { if (wild_match_file(match,fdb->filename)) return 1; } } return 0; } void filedb_ls(FILE *f,int idx,int atr,char *mask,int showall) { filedb fdb; int ok=0,cnt=0,is=0; char s[81],s1[81],*p; rewind(f); while (!feof(f)) { fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { ok=1; if (fdb.stat&FILE_UNUSED) ok=0; if (fdb.stat&FILE_DIR) { /* check permissions */ if (!flags_ok(fdb.flags_req,atr)) ok=0; } if (ok) is=1; if (!wild_match_file(mask,fdb.filename)) ok=0; if ((fdb.stat&FILE_HIDDEN) && !(showall)) ok=0; if (ok) { /* display it! */ if (cnt==0) { dprintf(idx,FILES_LSHEAD1); dprintf(idx,FILES_LSHEAD2); } if (fdb.stat&FILE_DIR) { char s2[50]; /* too long? */ if (strlen(fdb.filename)>45) { dprintf(idx,"%s/\n",fdb.filename); s2[0]=0; /* causes filename to be displayed on its own line */ } else sprintf(s2,"%s/",fdb.filename); if ((fdb.flags_req) && (atr&(USER_MASTER|USER_JANITOR))) { flags2str(fdb.flags_req,s); dprintf(idx,"%-45s (%s +%s)\n",s2,fdb.stat&FILE_SHARE? " SHARE":"",FILES_REQUIRES,s); } else dprintf(idx,"%-45s \n",s2); } else { char s2[41]; s2[0]=0; if (showall) { if (fdb.stat&FILE_SHARE) strcat(s2," (shr)"); if (fdb.stat&FILE_HIDDEN) strcat(s2," (hid)"); } strcpy(s,ctime(&fdb.uploaded)); s[10]=0; s[7]=0; s[24]=0; strcpy(s,&s[8]); strcpy(&s[2],&s[4]); strcpy(&s[5],&s[22]); if (fdb.size<1024) sprintf(s1,"%5d",fdb.size); else sprintf(s1,"%4dk",(int)(fdb.size/1024)); if (fdb.sharelink[0]) strcpy(s1," "); /* too long? */ if (strlen(fdb.filename)>30) { dprintf(idx,"%s\n",fdb.filename); fdb.filename[0]=0; /* causes filename to be displayed on its own line */ } dprintf(idx,"%-30s %s %-9s (%s) %6d%s\n",fdb.filename,s1, fdb.uploader,s,fdb.gots,s2); if (fdb.sharelink[0]) { dprintf(idx," --> %s\n",fdb.sharelink); } } if (fdb.desc[0]) { p=strchr(fdb.desc,'\n'); while (p!=NULL) { *p=0; if (fdb.desc[0]) dprintf(idx," %s\n",fdb.desc); strcpy(fdb.desc,p+1); p=strchr(fdb.desc,'\n'); } if (fdb.desc[0]) dprintf(idx," %s\n",fdb.desc); } cnt++; } } } if (is==0) dprintf(idx,FILES_NOFILES); else if (cnt==0) dprintf(idx,FILES_NOMATCH); else dprintf(idx,"--- %d file%s.\n",cnt,cnt>1?"s":""); } void remote_filereq(int idx,char *from,char *file) { char *p,what[256],dir[256],s[256],s1[256]; FILE *f; filedb *fdb; int i; strcpy(what,file); p=strrchr(what,'/'); if (p==NULL) dir[0]=0; else { *p=0; strcpy(dir,what); strcpy(what,p+1); } f=filedb_open(dir); if (f==NULL) { tprintf(dcc[idx].sock,"filereject %s:%s/%s %s %s\n",botnetnick,dir,what, from,FILES_DIRDNE); return; } fdb=findfile(f,what,NULL); if (fdb==NULL) { tprintf(dcc[idx].sock,"filereject %s:%s/%s %s %s\n",botnetnick,dir,what, from,FILES_FILEDNE); filedb_close(f); return; } if ((!(fdb->stat&FILE_SHARE)) || (fdb->stat&(FILE_HIDDEN|FILE_DIR))) { tprintf(dcc[idx].sock,"filereject %s:%s/%s %s %s\n",botnetnick,dir,what, from,FILES_NOSHARE); filedb_close(f); return; } filedb_close(f); /* copy to /tmp if needed */ sprintf(s1,"%s%s%s%s",dccdir,dir,dir[0]?"/":"",what); if (copy_to_tmp) { sprintf(s,"%s%s",tempdir,what); copyfile(s1,s); } else strcpy(s,s1); i=raw_dcc_send(s,"*remote",FILES_REMOTE,s); if (i>0) { wipe_tmp_filename(s,-1); tprintf(dcc[idx].sock,"filereject %s:%s/%s %s %s\n",botnetnick,dir,what, from,FILES_SENDERR); return; } /* grab info from dcc struct and bounce real request across net */ i=dcc_total-1; tprintf(dcc[idx].sock,"filesend %s:%s/%s %s %lu %d %lu\n",botnetnick,dir, what,from,iptolong(getmyip()),dcc[i].port,dcc[i].u.xfer->length); putlog(LOG_FILES,"*",FILES_REMOTEREQ,dir,dir[0]?"/":"",what); } /*** for tcl: ***/ void filedb_getdesc(char *dir,char *fn,char *desc) { FILE *f; filedb *fdb; f=filedb_open(dir); if (f==NULL) { desc[0]=0; return; } fdb=findfile(f,fn,NULL); filedb_close(f); if (fdb==NULL) { desc[0]=0; return; } strcpy(desc,fdb->desc); return; } void filedb_getowner(char *dir,char *fn,char *owner) { FILE *f; filedb *fdb; f=filedb_open(dir); if (f==NULL) { owner[0]=0; return; } fdb=findfile(f,fn,NULL); filedb_close(f); if (fdb==NULL) { owner[0]=0; return; } strcpy(owner,fdb->uploader); return; } int filedb_getgots(char *dir,char *fn) { FILE *f; filedb *fdb; f=filedb_open(dir); if (f==NULL) return 0; fdb=findfile(f,fn,NULL); filedb_close(f); if (fdb==NULL) return 0; return fdb->gots; } void filedb_setdesc(char *dir,char *fn,char *desc) { FILE *f; filedb *fdb; long where; f=filedb_open(dir); if (f==NULL) return; fdb=findfile(f,fn,&where); if (fdb==NULL) { filedb_close(f); return; } strncpy(fdb->desc,desc,300); fdb->desc[300]=0; fseek(f,where,SEEK_SET); fwrite(fdb,sizeof(filedb),1,f); filedb_close(f); return; } void filedb_setowner(char *dir,char *fn,char *owner) { FILE *f; filedb *fdb; long where; f=filedb_open(dir); if (f==NULL) return; fdb=findfile(f,fn,&where); if (fdb==NULL) { filedb_close(f); return; } strncpy(fdb->uploader,owner,9); fdb->uploader[9]=0; fseek(f,where,SEEK_SET); fwrite(fdb,sizeof(filedb),1,f); filedb_close(f); return; } void filedb_setlink(char *dir,char *fn,char *link) { FILE *f; filedb fdb,*x; long where; f=filedb_open(dir); if (f==NULL) return; x=findfile(f,fn,&where); if (x!=NULL) { /* change existing one? */ if ((x->stat & FILE_DIR) || !(x->sharelink[0])) return; if (!link[0]) { /* erasing file */ x->stat|=FILE_UNUSED; } else { strncpy(x->sharelink,link,60); x->sharelink[60]=0; } fseek(f,where,SEEK_SET); fwrite(x,sizeof(filedb),1,f); filedb_close(f); return; } fdb.version=FILEVERSION; fdb.stat=0; fdb.desc[0]=0; strcpy(fdb.uploader,botnetnick); strncpy(fdb.filename,fn,30); fdb.filename[30]=0; fdb.flags_req=0; fdb.uploaded=time(NULL); fdb.size=0; fdb.gots=0; strncpy(fdb.sharelink,link,60); fdb.sharelink[60]=0; where=findempty(f); fseek(f,where,SEEK_SET); fwrite(&fdb,sizeof(filedb),1,f); filedb_close(f); } void filedb_getlink(char *dir,char *fn,char *link) { FILE *f; filedb *fdb; f=filedb_open(dir); link[0]=0; if (f==NULL) return; fdb=findfile(f,fn,NULL); if (fdb==NULL) { filedb_close(f); return; } if (fdb->stat&FILE_DIR) { filedb_close(f); return; } strcpy(link,fdb->sharelink); filedb_close(f); return; } void filedb_getfiles(Tcl_Interp *irp,char *dir) { FILE *f; filedb fdb; f=filedb_open(dir); if (f==NULL) return; rewind(f); while (!feof(f)) { fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { if (!(fdb.stat & (FILE_DIR|FILE_UNUSED))) Tcl_AppendElement(irp,fdb.filename); } } filedb_close(f); } void filedb_getdirs(Tcl_Interp *irp,char *dir) { FILE *f; filedb fdb; f=filedb_open(dir); if (f==NULL) return; rewind(f); while (!feof(f)) { fread(&fdb,sizeof(filedb),1,f); if (!feof(f)) { if ((!(fdb.stat&FILE_UNUSED)) && (fdb.stat&FILE_DIR)) Tcl_AppendElement(irp,fdb.filename); } } filedb_close(f); } void filedb_change(char *dir,char *fn,int what) { FILE *f; filedb *fdb; long where; f=filedb_open(dir); if (f==NULL) return; fdb=findfile(f,fn,&where); if (fdb==NULL) { filedb_close(f); return; } if (fdb->stat&FILE_DIR) { filedb_close(f); return; } switch (what) { case FILEDB_HIDE: fdb->stat|=FILE_HIDDEN; break; case FILEDB_UNHIDE: fdb->stat&=~FILE_HIDDEN; break; case FILEDB_SHARE: fdb->stat|=FILE_SHARE; break; case FILEDB_UNSHARE: fdb->stat&=~FILE_SHARE; break; } fseek(f,where,SEEK_SET); fwrite(fdb,sizeof(filedb),1,f); filedb_close(f); return; } #endif /* !NO_FILE_SYSTEM */