// Filename: files.C // Contents: the files object methods // Author: Greg Shaw // Created: 8/1/93 /* This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file with other programs, and to distribute those programs without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into another program.) This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _FILES_C_ #define _FILES_C_ #include "bbshdr.h" // Function: constructor // Purpose: initialize the files object to a known state // Input: user - the card to use for the user using the BBS currently // Output: none // Author: Greg Shaw // Created: 8/1/93 files::files() { acl = 0; num_files = 0; name[0] = 0; age = 0; long_desc[0] = 0; sysop[0] = 0; numsel = 0; ksel = 0; type = 'R'; // default to rocat section header_path[0] = 0; // no path }; // Function: edit_file // Purpose: edit the information for a file // Input: item - the information for a particular file // Output: The file may be deleted or edited. In either case, the // file information is updated. // Author: Greg Shaw // Created: 4/27/94 int files::edit_file(FInfo *item) { int edited; // edited anything? int linenum; // line counter for description int off; // char offset into line char *editor; // name of their 'favorite' editor char tmpstr[255]; // temp str char oldname[MAX_FILENAMELENGTH]; // name prior to change char filename[255]; // new name of file char machreq[255]; // machine requirements char c; char ldesc[3][100]; // long description FILE *infile, *outfile; // files clear(); if (type != 'R') { cr(); sstrcr(EDITERROR); waitcr(); return(0); } strcpy(oldname,item->name); // save old name so we can find it later machreq[0] = 0; ldesc[0][0] = 0; sprintf(tmpstr,FILENAME,item->name); sstrcr(tmpstr); cr(); sstr(EDITDELETEQUIT); while (c = tolower(gch(1)), c != 'e' && c != 'd' && c != 'q'); cr(); if (c == 'q') return(0); else if (c == 'd') // delete return(update_information(item, NULL, ldesc, machreq, 1)); else if (c == 'e') // edit { edited = 0; while (1) // return will get out { sstrcr(CURRENTFILE); cr(); info(item,ldesc, machreq); cr(); sstrcr(FILEEDITNAME); sstrcr(FILEEDITDESCRIPTION); sstrcr(FILEEDITUPLOADER); sstrcr(FILEEDITDOWNLOADS); sstrcr(FILEEDITREQUIREMENTS); sstrcr(FILEEDITLONGDESCRIPTION); sstrcr(FILEEDITQUIT); cr(); sstr(FILEEDITWHICH); while (c = tolower(gch(1)), c != 'q' && (c < '0' || c > '6') ); cr(); cr(); switch(c) { case 'q': if (edited) return(update_information(item,oldname,ldesc,machreq,0)); return(0); case '1': // name edited++; sstr(NEWNAME); gstr(item->name,MAX_FILENAMELENGTH); break; case '2': // short description edited++; sstr(NEWDESCRIPTION); gstr(item->sdesc,60); break; case '3': // uploader edited++; sstr(NEWUPLOADER); gstr(item->uploader,40); break; case '4': // number of downloads edited++; sstr(NEWDOWNLOADS); gstr(tmpstr,5); sscanf(tmpstr,"%d",&item->numdls); break; case '5': // machine requirements edited++; sstr(NEWREQUIREMENTS); gstr(machreq,40); break; case '6': // long description edited++; // now get the long description // create temp file strcpy(tmpstr,"bbsXXXXXX"); mktemp(tmpstr); if (tmpstr[0] == 0) { ap_log("Unable to create temp file for long description editing."); return(0); } // got temp file. pass to system. cr(); cr(); sstrcr(NEWDESCRIPTION1); sstrcr(NEWDESCRIPTION2); waitcr(); strcpy(filename,tmpstr); strcpy(tmpstr,editor); if (editor = getenv("EDITOR"), editor == NULL) { sstrcr(NOEDITOR1); sstrcr(NOEDITOR2); sstrcr(NOEDITOR3); cr(); sstrcr(NOEDITOR4); waitcr(); strcpy(tmpstr,"vi"); } else strcpy(tmpstr,editor); strcat(tmpstr," /tmp/"); strcat(tmpstr,filename); sysint(tmpstr,0,0); strcpy(tmpstr,"/tmp/"); strcat(tmpstr,filename); // now open file if (infile = bopen(tmpstr,"r"), infile == NULL) { fprintf(outfile,"[D ]\n"); fprintf(outfile,"[E ]\n"); fprintf(outfile,"[F ]\n"); continue; } // now digest file linenum = 0; off = 0; ldesc[linenum][0] = 0; while (!feof(infile)) { while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)) tmpstr[off++] = c; tmpstr[off] = 0; strcpy(ldesc[linenum],tmpstr); linenum++; off = 0; } bclose(infile); sprintf(tmpstr,"/tmp/%s",filename); if (unlink(tmpstr)) // remove temp file { ap_log("file edit: unable to remove temp file."); // not fatal } } } } else // unknown { ap_log("Unknown quantity found in edit_file."); return(0); } return(0); }; // Function: create // Purpose: create a files section. read all file information from // directory and insert into files header file. // Input: open must be called prior to this one. the name of the // files section must be in the object. // Output: a new files section header file is created in the // $(BBS)/fileshdr directory. // Author: Greg Shaw // Created: 8/4/93 // Notes: *THIS IS VERY DANGEROUS* It should only be done on BBS // files area initialization. It will overwrite the 'old' // files header, losing any information that was there. int files::create(void) { FILE *outfile; // output file DIR *fdir; // directory file descriptor struct dirent *dentry; // directory entry struct stat fistat; // file status record time_t now; // date of file added (today) char bbsdir[255]; // bbs directory char tmpstr[255]; // tmpstr if ( type == 'C') // cdrom? return(0); // don't create time(&now); strcpy(bbsdir,getenv("BBSDIR")); // not checking error strcpy(tmpstr,bbsdir); strcat(tmpstr,"/filehdr/"); // tack on files header strcat(tmpstr,name); if (outfile = bopen(tmpstr,"w"), outfile == NULL) { printf("create: Unable to open files section header %s",name); } strcpy(tmpstr,bbsdir); strcat(tmpstr,"/files/"); strcat(tmpstr,dn_path); if (fdir = opendir(tmpstr), fdir == NULL) { printf("Unable to open directory %s for reading.\n",tmpstr); bclose(outfile); exit(0); } // ok. output file is open. directory is open. doit. while (dentry = readdir(fdir), dentry != NULL) { strcpy(tmpstr,bbsdir); strcat(tmpstr,"/files/"); strcat(tmpstr,dn_path); strcat(tmpstr,"/"); strcat(tmpstr,dentry->d_name); // for stat if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode)) { fprintf(outfile,"[A sysop 0 %s ]\n",dentry->d_name); fprintf(outfile,"[B ]\n"); fprintf(outfile,"[C ]\n"); fprintf(outfile,"[D ]\n"); fprintf(outfile,"[E ]\n"); fprintf(outfile,"[F ]\n"); } } closedir(fdir); bclose(outfile); strcpy(tmpstr,bbsdir); strcat(tmpstr,"/filehdr/"); // tack on files header strcat(tmpstr,name); chmod(tmpstr,0775); chown(tmpstr,bbs_uid(),bbs_gid()); // change owner to bbs return(0); }; // Function: download // Purpose: download file(s) from the BBS // Input: list - an array of FInfo pointers (file records) // num - the number of records (files) // Output: if everything works well, and the karma of the universe aligns with the // BBSs, it will download files. // Author: Greg Shaw // Created: 8/4/93 int files::download(FInfo *list[], int num, int increment, time_t logofftime) { FILE *infile; // protocols file time_t now; // loop (5sec) counter time_t then; // loop (5sec) counter int off; // offset into line int x; int numprot; // number of 'real' protocols int protsel; // index of selected protocol int done; // loop boolean char c; // one char from file char tmpstr[255]; // temp str char comm[MAX_DL_COMMANDS][50]; // 15 commands max char key[MAX_DL_COMMANDS]; // key for selecting command char text[MAX_DL_COMMANDS][50]; // text describing command char line[255]; if (!(num > 0)) return(0); // Ok. Change current working directory to files download area strcpy(tmpstr,getenv("BBSDIR")); strcat(tmpstr,"/files/"); strcat(tmpstr,dn_path); chdir(tmpstr); // change to files download directory (so local naming works) strcpy(tmpstr,getenv("BBSDIR")); strcat(tmpstr,"/config/protocols"); // read protocols file. digest. display options to user. if (infile = bopen(tmpstr,"r"), infile == NULL) { ap_log("Unable to open config/protocols file."); return(0); } // now read protocols file. numprot = 0; while (!feof(infile)) { off = 0; while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile)) line[off++] = c; // now digest line line[off] = 0; // add null (for posterity) (and possibly anterity) if (off < 5 || feof(infile)) // line can't be less than 5 chars long continue; if (line[0] == 'D') // we care about download only { // get command // note: this is pretty nasty. Beer (good) and programming // are not necessarily mutually exclusive. // although beer and excellent programming may be. off = 2; while (line[off] != '|' && line[off] != 0) { comm[numprot][off-2] = line[off++]; } comm[numprot][off-2] = 0; // add null off++; // skip | key[numprot] = line[off++]; // get hot key off++; // skip | x = off; // give an offset while (line[off] != 0) text[numprot][off - x] = line[off++]; text[numprot++][off-x] = 0; } } bclose(infile); // now show the bastich the protocols and let him choose... cr(); sstrcr(SELECTPROTOCOL); cr(); for (x=0; xname); } // ok. pass to system waitcr(); sysint(tmpstr,logofftime,1); for (x=0; xname); ap_log(tmpstr); } if (type == 'R') increment_dls(list,num); time(&then); while (time(&now), now - then < 5) fflush(stdin); // trash any additional garbage left on line return(num); }; // Function: increment_dls // Purpose: increment the number of downloads for the files downloaded // Input: list - the information for the files // Output: none // Author: Greg Shaw // Created: 8/10/93 int files::increment_dls(FInfo *list[], int num) { FILE *infile; FILE *outfile; int x; int off; int numdls; char uploader[10]; char numdone[20]; // max 20 files can be downloaded char line[150]; char tmpstr[255]; char fname[MAX_FILENAMELENGTH+1]; char fname2[MAX_FILENAMELENGTH+1]; char fname3[MAX_FILENAMELENGTH+1]; char c; for (x=0; x< 20; x++) numdone[x] = 0; sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); if (infile = bopen(tmpstr,"r"), infile == NULL) { sprintf(tmpstr,"Unable to open %s for read.",name); ap_log(tmpstr); return(0); } sprintf(tmpstr,"%s/filehdr/%s.new",getenv("BBSDIR"),name); if (outfile = bopen(tmpstr,"w"), outfile == NULL) { sprintf(tmpstr,"Unable to open %s.new for write.",name); ap_log(tmpstr); return(0); } while (!feof(infile)) { off = 0; while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)) line[off++] = c; line[off] = 0; if (line[0] == '[' && line[1] == 'A') // first line? { fname2[0] = 0; fname3[0] = 0; sscanf(&line[2],"%s %d %s", uploader, &numdls, fname, fname2, fname3); if (strlen(fname2) > 0 && fname2[0] != ']') { sprintf(tmpstr,"%s %s",fname,fname2); strcpy(fname,tmpstr); } if (strlen(fname3) > 0 && fname3[0] != ']') { sprintf(tmpstr,"%s %s",fname,fname3); strcpy(fname,tmpstr); } for (x=0; x< num; x++) if (!numdone[x] && strcmp(fname,list[x]->name) == 0 ) { numdone[x] = 1; numdls++; } fprintf(outfile,"[A %s %d %s ]\n",uploader, numdls, fname); } else if (strcmp(line,"") != 0) fprintf(outfile,"%s\n",line); } bclose(infile); bclose(outfile); sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); sprintf(line,"%s/filehdr/%s.old",getenv("BBSDIR"),name); rename(tmpstr,line); // rename file sprintf(tmpstr,"%s/filehdr/%s.new",getenv("BBSDIR"),name); sprintf(line,"%s/filehdr/%s",getenv("BBSDIR"),name); rename(tmpstr,line); // rename file sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); chmod(tmpstr,0775); chown(tmpstr,bbs_uid(),bbs_gid()); // change owner to bbs return(0); }; // Function: info // Purpose: get info on a file and display to user // Input: fptr - a FInfo pointer (NULL to prompt user for filename) // Output: the long information is shown to user // Author: Greg Shaw // Created: 8/10/93 int files::info(FInfo *fptr, char ldesc[3][100], char *machreq) { char tmpstr[255]; char filename[MAX_FILENAMELENGTH+1]; // name of file (if fptr NULL) char line[255]; // one line of description char datestr[12]; // date of file upload char c; int x; int lp; // number of lines of description printed int off; // offset into line int state; // which line are we working on? int numlines=0; // number of lines used for info FILE *infile; FInfo *rec; struct tm *tmrec; // date representation if (fptr == NULL) // don't have a file yet. prompt. { // get file from user sstr(GETFILEINFO); gstr(filename,MAX_FILENAMELENGTH); if (strcmp(filename,"q")==0) return(0); list_obj.top(); rec = list_obj.next(); while (strcmp(rec->name,filename) != 0 && rec != NULL) rec = list_obj.next(); if (rec == NULL) { sprintf(tmpstr,"Unable to find a file named %s.",filename); sstrcr(tmpstr); waitcr(); return(0); } } else rec = fptr; if (type == 'R') // rocat section sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); else if (type == 'C') // CDROM section strcpy(tmpstr,header_path); // absolute path else // error { sprintf(tmpstr,"files.info(): Invalid files section type %c",type); ap_log(tmpstr); return(0); } if (ldesc != NULL && strlen(ldesc[0]) != 0)// if we have description already, don't bother looking for it { tmrec = localtime(&rec->date); strftime(datestr,11,"%x",tmrec); sprintf(tmpstr,FILELISTTITLE1, rec->name,rec->size,datestr,rec->uploader); sstrcr(tmpstr); sprintf(tmpstr,FILELISTTITLE2,rec->numdls,rec->sdesc); sstrcr(tmpstr); sprintf(tmpstr,FILELISTTITLE3,rec->avail,machreq); sstrcr(tmpstr); for (x=0; x<3; x++) { sstr(" "); sstrcr(ldesc[x]); } return(6); } if (infile = bopen(tmpstr,"r"), infile == NULL) { sprintf(tmpstr,"files.info: Unable to open files section %s.",name); ap_log(tmpstr); sstrcr(NOFILESFOUND); waitcr(); return(0); } if (fseek(infile,rec->filepos,0) != 0) { sprintf(tmpstr,"Unable to set position in %s to %ld.",name,rec->filepos); ap_log(tmpstr); return(0); } // Ok. right place. now let's read the beastie tmrec = localtime(&rec->date); strftime(datestr,11,"%x",tmrec); sprintf(tmpstr,FILELISTTITLE1,rec->name,rec->size,datestr,rec->uploader); sstrcr(tmpstr); sprintf(tmpstr,FILELISTTITLE2,rec->numdls,rec->sdesc); sstrcr(tmpstr); if (type == 'R') { numlines += 2; state = 0; lp = 0; while (state < 4 && !feof(infile)) { off = 0; while (c = fgetc(infile), c != ']' && !feof(infile)) line[off++] = c; line[off] = 0; if (feof(infile)) continue; if (c == ']') // if got right bracket skip rest of line while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)); state++; switch(state) { case 1: // machine requirements line sprintf(tmpstr," Avail? %c Machine Requirements: %s",rec->avail,&line[2]); sstrcr(tmpstr); if (machreq != NULL) strcpy(machreq,&line[2]); numlines++; lp++; break; case 2: // first long description line if (strcmp(&line[2]," ")!=0) { sstr(" "); sstrcr(&line[2]); numlines++; lp++; } if (ldesc != NULL) // save long desc for return strcpy(ldesc[0],&line[2]); break; case 3: // second long description line if (strcmp(&line[2]," ")!=0) { sstr(" "); sstrcr(&line[2]); numlines++; lp++; } if (ldesc != NULL) // save long desc for return strcpy(ldesc[1],&line[2]); break; case 4: // third long description line if (strcmp(&line[2]," ")!=0) { sstr(" "); sstrcr(&line[2]); numlines++; lp++; } if (ldesc != NULL) // save long desc for return strcpy(ldesc[2],&line[2]); } } if (lp == 0) { sstrcr(NODESCRIPTION); numlines++; } } else if (type == 'C') { // read the file numlines += 2; // for previous lines // skip whitespace while (c = fgetc(infile), isspace(c) && !feof(infile)); off = 0; line[off++] = c; while (c != '\n' && !feof(infile)) { if (off != 1) off = 0; while (c = fgetc(infile), c != '\n' && off< 75 && !feof(infile)) { line[off++] = c; } line[off] = 0; numlines++; sstr(" "); sstrcr(line); off = 0; } } else { sprintf(tmpstr,"Invalid file section type %c found",type); ap_log(tmpstr); return(0); } bclose(infile); return(numlines); }; // Function: list // Purpose: list the files found in the section // Input: can_download - true if give user option to download // kused - the amount of K used by the user today // Output: a files listing // Author: Greg Shaw // Created: 8/1/93 int files::list(int can_download, CardRec *user, int *kused, time_t since_time, float uratio, int timelimit, time_t logon) { FILE *infile; FInfo *flist[20]; // five files on screen at once FInfo *mlist[20]; // the file info for files he has marked FInfo *rec; // temporary rec struct tm *tmrec; time_t now; // time conversion time_t then; // used for idle timeout time_t fromdate; // date to show the user 'from' time_t logofftime; // time when user's available time ends char tmpstr[255]; char fname[255]; char datestr[11]; char *bbsdir; char c; int day,mon,year; // for 'from' date selection int display_dir; // direction of display int t; int off; int x; // counter int y; // counter int done; // main loop counter int numlines; // number of descriptions on screen int numfiles; // number of files marked int ksel; // amount of 'k' selected for download int oneline; // one line or multiple lines? int numdownls; // number of files user downloaded int inactivity; // inactivity timeout numdownls = 0; if (type == 'R') // rocat section { bbsdir = getenv("BBSDIR"); sprintf(tmpstr,"%s/filehdr/%s",bbsdir,name); // } else if (type == 'C') // CDROM section strcpy(tmpstr,header_path); else { sprintf(tmpstr,"Invalid section type %c for %s",type,name); ap_log(tmpstr); return(0); } strcpy(fname,tmpstr); if (infile = bopen(tmpstr,"r"), infile == NULL) { sprintf(tmpstr,"files.list: Unable to open %s files section.",name); ap_log(tmpstr); sstrcr(NOFILESFOUND); waitcr(); return(0); } // got the file. let's ask him what he wants clear(); sstrcr(LISTFILESAS); cr(); sstrcr(ONEFILELINEDESCRIPTION); sstrcr(FULLFILEDESCRIPTION); cr(); sstr("Choice? "); while (c = gch(1), c != '1' && c != '2'); if (c == '1') oneline = 1; else oneline = 0; cr(); cr(); if (!since_time) { sstrcr(LISTFILESORDER); cr(); sstrcr(FORWARDCHRONOLOGICALLY); sstrcr(BACKWARDCHRONOLOGICALLY); sstrcr(ALPHABETICALLY); sstrcr(FORWARDFROMDATE1); cr(); sstr(YOURCHOICE); c = 0; while (c != '1' && c != '2' && c != '3' && c != '4') c = gch(1); cr(); cr(); switch(c) { case '1': // forward chrono list_obj.sort(1); display_dir = 0; break; case '2': // reverse chrono list_obj.sort(1); display_dir = 1; break; case '3': // alphabetically list_obj.sort(2); display_dir = 0; break; case '4': // forward from user entered date list_obj.sort(1); display_dir = 0; // get date from user done = 0; while (!done) { cr(); sstr(FORWARDFROMDATE2); gstr(tmpstr,9); if (sscanf(tmpstr,"%d/%d/%d",&mon,&day,&year) == 3) { done++; time(&now); tmrec = localtime(&now); tmrec->tm_mday = day; tmrec->tm_mon = mon-1; tmrec->tm_year = year; tmrec->tm_sec = 1; tmrec->tm_min = 0; tmrec->tm_hour = 0; fromdate = mktime(tmrec); since_time = fromdate; list_obj.top(); rec = list_obj.next(); while (rec->date < fromdate && rec != NULL) rec = list_obj.next(); if (rec == NULL) { waitcr(); return(0); } rec = list_obj.previous(); // move back one (for forward) } } } } else // 'new' files only { list_obj.sort(1); display_dir = 0; list_obj.top(); rec = list_obj.next(); done = 0; while (!done) { if (rec == NULL) { done++; continue; } if (rec->date < since_time) rec = list_obj.next(); else done++; } if (rec == NULL) { sstrcr(NONEWFILES); waitcr(); return(0); } rec = list_obj.previous(); // move back one (for forward) } // Ok. got everything we need. Now show him the files. done = 0; if (!since_time) if (display_dir == 1) list_obj.bottom(); else list_obj.top(); numfiles = 0; ksel = 0; // nothing selected (yet) while (!done) { x = 0; clear(); numlines=1; sprintf(tmpstr,SECTIONTITLE,long_desc,list_obj.numrecs()); sstrcr(tmpstr); if (oneline) sstrcr(FILESLISTHEADER); while (x<20&&numlines<20) { if (display_dir == 1) rec = list_obj.previous(); else rec = list_obj.next(); flist[x] = rec; if (rec == NULL) { x = 20; continue; } if (oneline) { tmrec = localtime(&rec->date); strftime(datestr,11,"%x",tmrec); sprintf(tmpstr,"%2d. %-14.14s %7d %s %2d %c %-38.38s",x+1, rec->name,rec->size,datestr,rec->numdls,rec->avail,rec->sdesc); if (numfiles > 0) { for (y=0; yname,rec->name)) sprintf(tmpstr,"%2d.*%-14.14s %7d %s %2d %c %-38.38s",x+1, rec->name,rec->size,datestr,rec->numdls,rec->avail,rec->sdesc); } sstrcr(tmpstr); } else { sprintf(tmpstr,"%2d. ",x+1); sstr(tmpstr); numlines += info(rec,NULL,NULL)+1; } x++; } // now give him a prompt cr(); if (strcmp(user->colr,"black")==0 || strcmp(username(),sysop)==0) // sysop sstr(EDITOPTION); if (can_download) sstr(DOWNLOADOPTION); sstr(INFOOPTION); time(&then); inactivity = inactivity_timeout(); while (c = tolower(gch(2)), c != 'd' && c != 'm' && c != 'i' && c != 'u' && c != 'q' && c != 'f' && c != 'b' && c != 'e' && c != '\n' && c != '-') { time(&now); if ((now - then)/60 > inactivity) { return(numdownls); } } cr(); switch(c) { case 'e': if (strcmp(user->colr,"black")==0 || strcmp(username(),sysop)==0) // sysop { sprintf(tmpstr,EDITWHICH,x); sstr(tmpstr); gstr(tmpstr,3); if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off < 21) { edit_file(flist[off-1]); } } break; case 'd': if (can_download) { if (numfiles <= 0) { // do one file sprintf(tmpstr,DOWNLOADWHICHNUM,x); sstr(tmpstr); gstr(tmpstr,3); if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off <= x) { if (flist[off-1]->avail != 'Y') { sprintf(tmpstr,FILENOTAVAILABLE,flist[off-1]->name); sstrcr(tmpstr); waitcr(); } else { time(&now); logofftime = (timelimit * 60) - (now - logon) + now; logofftime += ((logofftime-now) * timelimit_fudge())/100; // add fudge factor numdownls += download(&flist[off-1],1,1,logofftime);// one file *kused += flist[off-1]->size/1024; return(numdownls); } } } else { time(&now); logofftime = (timelimit * 60) - (now - logon) + now; numdownls += download(mlist,numfiles,1, logofftime);// many files for (x=0; xsize/1024; numfiles = 0; ksel = 0; return(numdownls); } } else { sstrcr(NOPRIVILIDGES1); waitcr(); } break; case 'q': // exit case '-': if (numfiles > 0) { clear(); sstrcr(NOPRIVILIDGES2); sstr(EXITWITHOUTDOWNLOAD); if (yesno()) return(0); } else return(0); case 'm': // mark a file if (numfiles < 19&&can_download) { if (uratio > fabs(ratio())) { sstrcr(BADRATIO); sprintf(tmpstr,UPLOADRATIO,ratio()); sstrcr(tmpstr); sstrcr(UPLOADTOFIX1); waitcr(); return(0); } if (uratio <= 0 && ratio() < 0) { sstrcr(UPLOADTOFIX2); sstrcr(UPLOADTOFIX3); sstrcr(UPLOADTOFIX4); waitcr(); return(0); } sprintf(tmpstr,MARKWHICH,x); sstr(tmpstr); gstr(tmpstr,3); if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off < 21) { if (flist[off-1] != NULL) { if (*kused + ((flist[off-1]->size/1024)+ksel) > thisuser->kbytes && thisuser->kbytes > 0) { cr(); cr(); sstrcr(NOTENOUGHSPACE); sprintf(tmpstr,YOUHAVESPACE,thisuser->kbytes,waittime()); sstrcr(tmpstr); waitcr(); } else if (ksel + (flist[off-1]->size/1024) > maxk()) { cr(); cr(); sprintf(tmpstr,BATCHSIZEPERDOWNLOAD,maxk()); sstrcr(tmpstr); sstrcr(BATCHTOOBIG1); waitcr(); } else { if (flist[off-1]->avail != 'Y') { sprintf(tmpstr,FILEUNAVAILABLE,flist[off-1]->name); sstrcr(tmpstr); waitcr(); } else { ksel += flist[off-1]->size/1024; cr(); cr(); sprintf(tmpstr,MARKEDFORDOWNLOAD,flist[off-1]->name); sstrcr(tmpstr); waitcr(); mlist[numfiles++] = flist[off-1]; } } } } } else { if (numfiles >= 19) sstrcr(BATCHTOOBIG2); else sstrcr(NOPRIVILIDGES1); waitcr(); } break; case 'i': // info on a file sprintf(tmpstr,INFOWHICH,x); sstr(tmpstr); gstr(tmpstr,3); if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off < 21) { if (flist[off-1] != NULL) { info(flist[off-1],NULL,NULL); waitcr(); } } break; case 'f': // move to next screen case '\n': case '\r': continue; break; case 'b': // move back one screen x = 0; while (x<40 && rec != NULL ) { if (display_dir == 1) rec = list_obj.next(); else rec = list_obj.previous(); x++; } continue; break; case 'u': // unmark a file if (numfiles > 0) { cr(); sstrcr(FILESMARKED); for (x=0; x 0 && off < numfiles+1) { sprintf(tmpstr,FILEUNMARKED,mlist[off-1]); sstrcr(tmpstr); // umark the file. compact list t = 0; for (x=0; x-1 && rec != NULL) { if (display_dir != 1) rec = list_obj.previous(); else rec = list_obj.next(); x--; } } return(numdownls); }; // Function: one_download // Purpose: get a filename from the user and download that file // Input: none // Output: if the file name is found, the file will be downloaded // Author: Greg Shaw // Created: 8/10/93 int files::one_download(int *kused, float uratio, char *filename, int counts) { char str[50]; char tmpstr[20]; FInfo *rec,tmprec; // get filename clear(); if (filename != NULL) { // create a dummy record for this download strcpy(tmprec.name,filename); tmprec.avail = 'Y'; tmprec.numdls = 0; tmprec.size = 0; rec = &tmprec; } else { sstrcr(CASEMATTERS); cr(); sstr(DOWNLOADWHICH); gstr(tmpstr,MAX_FILENAMELENGTH); if (strcmp(tmpstr,"q") == 0) return(0); cr(); // now find the file in the list list_obj.top(); rec = list_obj.next(); while (strcmp(rec->name,tmpstr) != 0 && rec != NULL) rec = list_obj.next(); if (rec == NULL) { sprintf(str,UNABLETOFIND,tmpstr); sstrcr(str); waitcr(); return(0); } if (rec->avail == 'N') { sprintf(str,FILEUNAVAILABLE,rec->name); sstrcr(str); waitcr(); return(0); } } // download the beastie! if (counts) // does this count to upload/download ratios? { if (*kused + ((rec->size/1024)+ksel) > thisuser->kbytes && thisuser->kbytes > 0) { cr(); cr(); sstrcr(NOTENOUGHSPACE); sprintf(tmpstr,YOUHAVESPACE,thisuser->kbytes,waittime()); sstrcr(tmpstr); waitcr(); return(0); } if (uratio > fabs(ratio())) { sstrcr(BADRATIO); sprintf(tmpstr,UPLOADRATIO,ratio()); sstrcr(tmpstr); sstrcr(UPLOADTOFIX1); waitcr(); return(0); } if (uratio <= 0 && ratio() < 0) { sstrcr(UPLOADTOFIX2); sstrcr(UPLOADTOFIX3); sstrcr(UPLOADTOFIX4); waitcr(); return(0); } } download(&rec,1,counts,0); return(1); }; // Function: open // Purpose: open the files section. // Input: path - the name of the bbs section to read // Output: none. setup function only. // Author: Greg Shaw // Created: 8/1/93 int files::open(char *sname, CardRec *user) { FILE *infile; struct stat fistat; // file status record FInfo newrec; // record for insert into list char word[50]; char tmpstr[255]; char tmpstr2[255]; char tmpstr3[255]; int x; int off; int line; int found; char *u; // used for erasing right bracket char *bbsdir;// used for bbsdir environment var char c; thisuser = user; bbsdir = getenv("BBSDIR"); // not checking because he shouldn't // get this far w/o BBSDIR env var if (strcmp(sname,name) != 0) // don't open if already open { list_obj.clear_list(); // nuke old values strcpy(tmpstr,getenv("BBSDIR")); // not checking because he shouldn't // get here if bbsdir not set strcat(tmpstr,"/filehdr/bbs_files_hdr"); // tack on files header if (infile = bopen(tmpstr,"r"), infile == NULL) { sprintf(tmpstr,"Unable to open main files section header (bbs_files_hdr)",name); ap_log(tmpstr); return(-1); } // ok. got file. let's find the line we're looking for found = 0; while (!found && !feof(infile)) { // look for left bracket while (c = fgetc(infile), c != '[' && !feof(infile)); // now get the rest of the line if (fscanf(infile,"%s %c %s %s %d %s %s %d%50s",name, &type,header_path,sysop,&acl,dn_path, up_path, &age, long_desc) != 9) { sprintf(tmpstr,"Unable to find %s in bbs main files header.",sname); ap_log(tmpstr); return(-1); } while (fscanf(infile,"%s",word) == 1 && strchr(word,']') == NULL) { strcat(long_desc," "); strcat(long_desc,word); } if (u = strchr(long_desc,']'), u != NULL) u[0] = 0; // turn into null if (strcmp(name,sname) == 0) found++; // gotcha } bclose(infile); if (type == 'R') // rocat section { strcpy(tmpstr,getenv("BBSDIR"));// not checking because he shouldn't make it this far strcat(tmpstr,"/filehdr/"); // get name of file area strcat(tmpstr,name); if (infile = bopen(tmpstr,"r"),infile == NULL) { // empty files section return(0); } // ok. now read everything into dllist object line = 0; while (!feof(infile)) { while (c=fgetc(infile), c != '[' && !feof(infile)); if (feof(infile)) continue; switch(line) { case 0: // line 1 if (c = fgetc(infile), c == 'A') { tmpstr2[0] = 0; tmpstr3[0] = 0; fscanf(infile,"%s %d %s",newrec.uploader, &newrec.numdls,newrec.name,tmpstr2,tmpstr3); if (strlen(tmpstr2) > 0 && tmpstr2[0] != ']') { sprintf(tmpstr,"%s %s",newrec.name,tmpstr2); strcpy(newrec.name,tmpstr); } if (strlen(tmpstr3) > 0 && tmpstr3[0] != ']') { sprintf(tmpstr,"%s %s",newrec.name,tmpstr3); strcpy(newrec.name,tmpstr); } sprintf(tmpstr,"%s/files/%s/%s",bbsdir,dn_path,newrec.name); if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode)) { newrec.size = fistat.st_size; newrec.date = fistat.st_ctime; newrec.avail = 'Y'; } else { newrec.size = 0; newrec.date = 0; newrec.avail = 'N'; } line++; } break; case 1: // line 2 if (c = fgetc(infile), c == 'B') { off = 0; while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)) tmpstr[off++] = c; tmpstr[off] = 0; strcpy(newrec.sdesc,tmpstr); if (u = strchr(newrec.sdesc,']'), u != NULL) u[0] = 0; // turn into null if (strcmp(newrec.sdesc," ") == 0) strcpy(newrec.sdesc,"none"); line++; } break; case 2: // line 3 if (c = fgetc(infile), c == 'C') { newrec.filepos = ftell(infile); list_obj.add(&newrec); fscanf(infile,"%s",tmpstr); line++; } break; case 3: // line 4 if (c = fgetc(infile), c == 'D') { fscanf(infile,"%s",tmpstr); line++; } break; case 4: // line 5 if (c = fgetc(infile), c == 'E') { fscanf(infile,"%s",tmpstr); line++; } break; case 5: // line 6 if (c = fgetc(infile), c == 'F') { fscanf(infile,"%s",tmpstr); line = 0; } break; } } bclose(infile); } else if (type == 'C') // cdrom section (files.bbs) { // open section strcpy(tmpstr,header_path); // should be absolute if (infile = bopen(tmpstr,"r"), infile == NULL) { clear(); sstrcr(CDROMOFFLINE1); sstrcr(CDROMOFFLINE2); waitcr(); return(1); } // skip the lines that have a space at the start of the line if (c = fgetc(infile), c == ' ') { while (c == ' ' && !feof(infile)) { while (c = fgetc(infile), c != '\n' && !feof(infile)); c = fgetc(infile); // should be a space if comment } } // ok. now read everything into dllist object line = 0; word[0] = tolower(c); // allow for previous skip off = 1; while (!feof(infile)) { // get filename and convert to lower case while (c = fgetc(infile), !isspace(c) && !feof(infile)) word[off++] = tolower(c); word[off] = 0; // skip extra white space while (c=fgetc(infile), isspace(c) && !feof(infile)); if (feof(infile)) continue; strcpy(newrec.name,word); // skip date information while (c = fgetc(infile), !isspace(c) && !feof(infile)); // get position of information // get first 40 chars of description (for short) while (c = fgetc(infile), isspace(c) && !feof(infile)); newrec.filepos = ftell(infile)-1; off = 0; newrec.sdesc[off++] = c; while (c = fgetc(infile), c != '\n') if (off < SDESC_LEN) newrec.sdesc[off++] = c; newrec.sdesc[off] = 0; newrec.numdls = 0; newrec.uploader[0] = 0; // no uploader strcpy(tmpstr,header_path); // should be absolute // chop off files header name off = 0; for (x=0; x< strlen(tmpstr); x++) if (tmpstr[x] == '/') off = x; if (off != 0) tmpstr[off+1] = 0; strcat(tmpstr,newrec.name); if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode)) { newrec.size = fistat.st_size; newrec.date = fistat.st_ctime; newrec.avail = 'Y'; } else { newrec.size = 0; newrec.date = 0; newrec.avail = 'N'; } list_obj.add(&newrec); off = 0; } bclose(infile); } else { sprintf(tmpstr,"Unknown file header type %c found in bbs_files_hdr",type); ap_log(tmpstr); return(-1); } } return(0); }; // Function: update_information // Purpose: update the file information in the header file // Input: list - the information for the file // Output: none // Author: Greg Shaw // Created: 8/10/93 int files::update_information(FInfo *item, char *origname, char desc[3][100], char *machreq, int del) { FILE *infile; FILE *outfile; int off; char line[150]; char tmpstr[255]; char fname[MAX_FILENAMELENGTH+1]; char c; sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); if (infile = bopen(tmpstr,"r"), infile == NULL) { sprintf(tmpstr,"Unable to open %s for read.",name); ap_log(tmpstr); return(0); } sprintf(tmpstr,"%s/filehdr/%s.new",getenv("BBSDIR"),name); if (outfile = bopen(tmpstr,"w"), outfile == NULL) { sprintf(tmpstr,"Unable to open %s.new for write.",name); ap_log(tmpstr); return(0); } while (!feof(infile)) { off = 0; while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)) line[off++] = c; line[off] = 0; if (line[0] == '[' && line[1] == 'A') // first line? { if (!sscanf(&line[2],"%s %*ld %*ld %*d %*s", fname)) { sprintf(tmpstr,"Error found in files listing %s for file %s",name,fname); ap_log(tmpstr); } if (strcmp(fname,origname) == 0 ) { if (del) { sprintf(tmpstr,"%s/files/%s/%s",getenv("BBSDIR"), dn_path, item->name); if (unlink(tmpstr)) { sprintf(tmpstr,"Unable to delete file %s",item->name); ap_log(tmpstr); } // skip next 5 lines off=0; while (off < 5) { if (c = fgetc(infile), c == '\r' || c == '\n') off++; } } fprintf(outfile,"[A %s %d %s ]\n",item->uploader, item->numdls, item->name); fprintf(outfile,"[B %s ]\n",item->sdesc); fprintf(outfile,"[C %s ]\n",machreq); fprintf(outfile,"[D %s ]\n",desc[0]); fprintf(outfile,"[E %s ]\n",desc[1]); fprintf(outfile,"[F %s ]\n",desc[2]); // skip next 5 lines off=0; while (off < 5) { if (c = fgetc(infile), c == '\r' || c == '\n') off++; } while (!feof(infile)) if (c = fgetc(infile), c != EOF) fputc(c,outfile); } else fprintf(outfile,"%s\n",line); } else if (strcmp(line,"") != 0) fprintf(outfile,"%s\n",line); } bclose(infile); bclose(outfile); sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); sprintf(line,"%s/filehdr/%s.old",getenv("BBSDIR"),name); rename(tmpstr,line); // rename file sprintf(tmpstr,"%s/filehdr/%s.new",getenv("BBSDIR"),name); sprintf(line,"%s/filehdr/%s",getenv("BBSDIR"),name); rename(tmpstr,line); // rename file sprintf(tmpstr,"%s/filehdr/%s",getenv("BBSDIR"),name); chmod(tmpstr,0775); chown(tmpstr,bbs_uid(),bbs_gid()); // change owner to bbs return(0); }; // Function: search // Purpose: search for a file in the files section // Input: can_download - can the user download the file if he wants // Output: if the search is successfull, the file will be displayed to the user // bbs directory upload directory (or wherever the uploaddir is) // Author: Greg Shaw // Created: 8/9/93 int files::search(int can_download, int timelimit, time_t logon) { char fname[MAX_FILENAMELENGTH+1]; // filename to search for FInfo *rec; time_t now; time_t logofftime; clear(); sstrcr(ENTERSUBSTRING); cr(); sstr(SEARCHSTRING); gstr(fname,MAX_FILENAMELENGTH); if (fname[0] == 0) return(0); list_obj.top(); while (rec = list_obj.next(), rec != NULL) { if (strstr(rec->name,fname) != NULL) { info(rec,NULL,NULL); waitcr(); if (can_download) { cr(); sstr(DOWNLOADTHISFILE); time(&now); logofftime = (timelimit * 60) - (now - logon) + now; if (yesno()) download(&rec,1,1,logofftime); } sstr(CONTINUESEARCH); if (!yesno()) break; } } cr(); sstrcr(NOMOREFILESFOUND); waitcr(); return(0); }; // Function: upload // Purpose: upload file(s) to the BBS // Input: name - name of user (for temp directory name) // Output: if the file upload is successful, it will be moved to the // bbs directory upload directory (or wherever the uploaddir is) // Author: Greg Shaw // Created: 8/9/93 int files::upload(char *uname, char *editor, int *credminutes) { DIR *fdir; // directory file descriptor struct dirent *dentry; // directory entry struct stat fistat; // file status record time_t now; // upload date time_t start; // start of upload time time_t end; // end of upload time FILE *outfile; // protocols file FILE *infile; // protocols file int off; // offset into line int x; int numuploads; // number of files uploaded int numprot; // number of 'real' protocols int protsel; // index of selected protocol int done; // loop boolean int linenum; // line number char c; // one char from file char tmpstr[255]; // temp str char tmpstr2[255]; // temp str char bbsdir[255]; // temp str char comm[MAX_DL_COMMANDS][50]; // 15 commands max char key[MAX_DL_COMMANDS]; // key for selecting command char needs_filename[MAX_DL_COMMANDS];// key for selecting command char filename[MAX_FILENAMELENGTH]; // filename char text[MAX_DL_COMMANDS][50]; // text describing command char line[255]; clear(); if (type != 'R') // no uploads except for rocat areas { cr(); sstrcr(NOUPLOAD); cr(); waitcr(); } time(&start); numuploads = 0; strcpy(bbsdir,getenv("BBSDIR")); // not checking error strcpy(tmpstr,bbsdir); strcat(tmpstr,"/config/protocols"); // read protocols file. digest. display options to user. if (infile = bopen(tmpstr,"r"), infile == NULL) { ap_log("Unable to open config/protocols file."); return(0); } // now read protocols file. numprot = 0; while (!feof(infile)) { off = 0; while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile)) line[off++] = c; // now digest line line[off] = 0; // add null (for posterity) (and possibly anterity) if (off < 5 || feof(infile)) // line can't be less than 5 chars long continue; if (line[0] == 'U') // we care about upload only { // get command // note: this is pretty nasty. Beer (good) and programming // are not necessarily mutually exclusive. // although beer and excellent programming may be. off = 2; while (line[off] != '|' && line[off] != 0) { comm[numprot][off-2] = line[off++]; } comm[numprot][off-2] = 0; // add null off++; // skip | needs_filename[numprot] = line[off++]; // get 'needs filename' information off++; // skip | key[numprot] = line[off++]; // get hot key off++; // skip | x = off; // give an offset while (line[off] != 0) text[numprot][off - x] = line[off++]; text[numprot++][off-x] = 0; } } bclose(infile); // now show the bastich the protcols and let him choose... cr(); sstrcr(SELECTUPLOADPROTOCOL); cr(); for (x=0; xd_name); // for stat if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode)) { sprintf(tmpstr,UPLOADCORRECTLY,dentry->d_name); sstr(tmpstr); if (!yesno()) continue; // skip bad file upload fprintf(outfile,"[A %s 0 %s ]\n",uname, dentry->d_name); sprintf(tmpstr,ENTERINFORMATION,dentry->d_name); sstrcr(tmpstr); cr(); sstrcr(SHORTDESCRIPTION); sstr(": "); gstr(tmpstr,40); fprintf(outfile,"[B %s ]\n",tmpstr); // get machine requirements cr(); sstrcr(SOFTHARDWAREDESCRIPTION); sprintf(tmpstr,FORFILE,dentry->d_name); sstr(tmpstr); gstr(tmpstr,40); fprintf(outfile,"[C %s ]\n",tmpstr); // now get the long description // create temp file strcpy(tmpstr,"bbsXXXXXX"); mktemp(tmpstr); if (tmpstr[0] == 0) { ap_log("Unable to create temp file for upload long description entry."); fprintf(outfile,"[D ]\n"); fprintf(outfile,"[E ]\n"); fprintf(outfile,"[F ]\n"); continue; } // got temp file. pass to system. cr(); cr(); sstrcr(LONGDESCRIPTION1); sstrcr(LONGDESCRIPTION2); sstrcr(LONGDESCRIPTION3); sstrcr(LONGDESCRIPTION4); waitcr(); strcpy(filename,tmpstr); strcpy(tmpstr,editor); strcat(tmpstr," /tmp/"); strcat(tmpstr,filename); sysint(tmpstr,0,0); strcpy(tmpstr,"/tmp/"); strcat(tmpstr,filename); // now open file if (infile = bopen(tmpstr,"r"), infile == NULL) { fprintf(outfile,"[D ]\n"); fprintf(outfile,"[E ]\n"); fprintf(outfile,"[F ]\n"); continue; } // now digest file linenum = 0; off = 0; line[0] = 0; while (!feof(infile)) { while (c = fgetc(infile), c != '\r' && c != '\n' && !feof(infile)) line[off++] = c; line[off] = 0; linenum++; switch(linenum) { case 1: fprintf(outfile,"[D %s ]\n",line); off = 0; break; case 2: fprintf(outfile,"[E %s ]\n",line); off = 0; break; case 3: fprintf(outfile,"[F %s ]\n",line); off = 0; } } if (linenum < 3) // got all 3 lines? { if (linenum == 0) // no lines { fprintf(outfile,"[D %s ]\n",line); fprintf(outfile,"[E ]\n"); fprintf(outfile,"[F ]\n"); } else if (linenum == 1) // one line { fprintf(outfile,"[E %s ]\n",line); fprintf(outfile,"[F ]\n"); } else if (linenum == 2) // two lines fprintf(outfile,"[F %s ]\n",line); } bclose(infile); // now move file to uploads area sprintf(tmpstr,"%s/tmp/%s/%s",bbsdir,uname,dentry->d_name); sprintf(line,"%s/%s/%s",bbsdir,up_path,dentry->d_name); if (rename(tmpstr,line)) { sprintf(tmpstr2,"Unable to move %s to %s",tmpstr,line); ap_log(tmpstr2); } sprintf(tmpstr,"/tmp/%s",filename); unlink(tmpstr); // remove temp file numuploads++; sprintf(tmpstr,"%s uploaded %s to %s",uname,dentry->d_name,name); ap_log(tmpstr); } } bclose(outfile); closedir(fdir); sprintf(tmpstr,"%s",bbsdir); chdir(tmpstr); // change to bbs root directory sprintf(tmpstr,"%s/filehdr/%s",bbsdir,name); chmod(tmpstr,0775); // chmod 775 chown(tmpstr,bbs_uid(),bbs_gid()); // change owner to bbs sprintf(tmpstr,"%s/tmp/%s",bbsdir,uname); chmod(tmpstr,0775); // chmod 775 chown(tmpstr,bbs_uid(),bbs_gid()); // change owner to bbs sprintf(tmpstr,"%s/tmp/%s/* 2> /dev/null",bbsdir,uname); unlink(tmpstr); // remove any leftover files sprintf(tmpstr,"%s/tmp/%s 2> /dev/null",bbsdir,uname); rmdir(tmpstr); // remove upload directory cr(); sstrcr(ENDOFUPLOAD); cr(); sstrcr(UPLOADMOVE1); sstrcr(UPLOADMOVE2); waitcr(); time(&end); *credminutes = (end - start)/60; return(numuploads); }; #endif // _FILES_C_