// 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"

extern Chat chatobj;	// check for chat requests

// 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
	// name prior to change
	char   oldname[MAX_FILENAMELENGTH];
	char   filename[255];       // new name of file
	char   machreq[255];        // machine requirements
	char   c;
	char   ldesc[3][100];       // long description
	FILE   *infile;             // files

	clear_scr();
	if (type != 'R')
	{
		cr();
		sstrcrl("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,gstrl("FILENAME"),item->name);
	sstrcr_c(tmpstr);
	cr();
	sstrl("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
		{
			sstrcrl("CURRENTFILE");
			cr();
			info(item,ldesc, machreq);
			printf("after info\n");
			fflush(stdout);
			cr();
			sstrl("FILEEDITMENU");
			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++;
					sstrl("NEWNAME");
					gstr(item->name,MAX_FILENAMELENGTH);
					break;
				case '2':       // short description
					edited++;
					sstrl("NEWDESCRIPTION");
					gstr(item->sdesc,60);
					break;
				case '3':       // uploader
					edited++;
					sstrl("NEWUPLOADER");
					gstr(item->uploader,40);
					break;
				case '4':       // number of downloads
					edited++;
					sstrl("NEWDOWNLOADS");
					gstr(tmpstr,5);
					sscanf(tmpstr,"%d",&item->numdls);
					break;
				case '5':       // machine requirements
					edited++;
					sstrl("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();
					sstrcrl("NEWDESCRIPTION1");

					waitcr();
					strcpy(filename,tmpstr);
					if (editor = getenv("EDITOR"), editor == NULL)
					{
						sstrcrl("NOEDITOR");
						waitcr();
						strcpy(tmpstr,"vi");
					}
					else
						strcpy(tmpstr,editor);
					strcat(tmpstr," /tmp/");
					strcat(tmpstr,filename);
					sysint(tmpstr,0,0);
					strcpy(tmpstr,"/tmp/");
					// now open file
					strcat(tmpstr,filename);
					if (infile = bopen(tmpstr,"r"), infile == NULL)
					{
						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);
					// remove temp file
					if (unlink(tmpstr))
					{
						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);
	// not checking error
	strcpy(bbsdir,getenv("BBSDIR"));
	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,"/");
		// for stat
		strcat(tmpstr,dentry->d_name);
		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);
	// change owner to bbs
	chown(tmpstr,bbs_uid(),bbs_gid());
	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 = 0;         // index of selected protocol
	int    done;                // loop boolean
	char   c;                   // one char from file
	char   tmpstr[255];         // temp str
	// 15 commands max
	char   comm[MAX_DL_COMMANDS][50];
	char   key[MAX_DL_COMMANDS];// key for selecting command
	// text describing command
	char   text[MAX_DL_COMMANDS][50];
	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)
	sprintf(tmpstr,"%s/config/protocols",getenv("BBSDIR"));
	// 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)
		// line can't be less than 5 chars long
		if (off < 5 || feof(infile))
			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++];
			}
			// add null
			comm[numprot][off-2] = 0;
			off++;              // skip |
			// get hot key
			key[numprot] = line[off++];
			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();
	sstrcrl("SELECTPROTOCOL");
	cr();
	for (x=0; x<numprot; x++)
		sstrcr_c(text[x]);
	cr();
	sstrl("YOURCHOICE");
	done = 0;
	while (!done)
	{
		c = gch(1);
		if (c == 'q')
			return(0);
		for (x=0; x<numprot; x++)
			if (key[x] == c)
		{
			done++;
			protsel = x;
		}
	}
	// Ok.  Got protocol.  Show him a quick list of files to be downloaded
	sstrcrl("FILESTODOWNLOAD");
	for (x=0; x<num; x++)
	{
		sprintf(tmpstr,gstrl("FILEDOWNLIST"),list[x]->name,list[x]->size);
		sstrcr(tmpstr);
	}
	cr();
	cr();
	sstrcrl("READYTOSTARTDOWNLOAD");
	cr();
	strcpy(tmpstr,comm[protsel]);
	for (x=0; x<num; x++)
	{
		strcat(tmpstr," ");
		strcat(tmpstr,list[x]->name);
	}
	// ok.  pass to system
	waitcr();
	sysint(tmpstr,logofftime,1);
	for (x=0; x<num; x++)
	{
		sprintf(tmpstr,"%s downloaded %s",username(),list[x]->name);
		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;
	if (name == NULL)
		return(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;
		// first line?
		if (line[0] == '[' && line[1] == 'A')
		{
			fname2[0] = 0;
			fname3[0] = 0;
			sscanf(&line[2],"%s %d %s %s %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);
	// change owner to bbs
	chown(tmpstr,bbs_uid(),bbs_gid());
	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];
	// name of file (if fptr NULL)
	char   filename[MAX_FILENAMELENGTH+1];
	char   line[255];           // one line of description
	char   datestr[12];         // date of file upload
	char   c;
	char   *p;
	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
		sstrl("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_c(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
		// absolute path
		strcpy(tmpstr,header_path);
	else                        // error
	{
		sprintf(tmpstr,"files.info(): Invalid files section type %c",type);
		ap_log(tmpstr);
		return(0);
	}
	// if we have description already, don't bother looking for it
	if (ldesc != NULL && strlen(ldesc[0]) != 0)
	{
		if (rec->date != 0)
		{
			tmrec = localtime(&rec->date);
			strftime(datestr,11,"%x",tmrec);
			sprintf(tmpstr,gstrl("FILELISTTITLE1"),rec->name,rec->size,datestr,rec->uploader);
		}
		else
			sprintf(tmpstr,gstrl("FILELISTTITLE4"),rec->name,rec->size,rec->uploader);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("FILELISTTITLE2"),rec->numdls,rec->sdesc);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("FILELISTTITLE3"),rec->avail,machreq);
		sstrcr_c(tmpstr);
		sstr_c("   ");
		sstrcrl("LONGDESCRIPTION");
		numlines = 4;           // four lines so far
		for (x=0; x<3; x++)
		{
			p = ldesc[x];
			while (isspace(*p) && *p != 0)
				p++;
			if (*p != 0)        // anything meaningful?
			{
				numlines++;
				// then print
				sstrcr_c(ldesc[x]);
			}
		}
		return(numlines);
	}
	if (infile = bopen(tmpstr,"r"), infile == NULL)
	{
		sprintf(tmpstr,"files.info: Unable to open files section %s.",name);
		ap_log(tmpstr);
		sstrcrl("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,gstrl("FILELISTTITLE1"),rec->name,rec->size,datestr,rec->uploader);
	sstrcr_c(tmpstr);
	numlines++;
	sprintf(tmpstr,gstrl("FILELISTTITLE2"),rec->numdls,rec->sdesc);
	sstrcr_c(tmpstr);
	numlines++;
	if (type == 'R')
	{
		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_c(tmpstr);
					if (machreq != NULL)
						strcpy(machreq,&line[2]);
					numlines++;
					lp++;
					sstr_c("   ");
					sstrcrl("LONGDESCRIPTION");
					numlines++;
					break;
				case 2:         // first long description line
					if (strcmp(&line[2]," ")!=0)
					{
						p = &line[2];
						while (isspace(*p) && *p != 0)
							p++;
						// anything meaningful?
						if (*p != 0)
						{
							sstr_c(" ");
							sstrcr_c(&line[2]);
							numlines++;
							lp++;
						}
					}
					// save long desc for return
					if (ldesc != NULL)
						strcpy(ldesc[0],&line[2]);
					break;
				case 3:         // second long description line
					if (strcmp(&line[2]," ")!=0)
					{
						p = &line[2];
						while (isspace(*p) && *p != 0)
							p++;
						// anything meaningful?
						if (*p != 0)
						{
							sstr_c(" ");
							sstrcr_c(&line[2]);
							numlines++;
							lp++;
						}
					}
					// save long desc for return
					if (ldesc != NULL)
						strcpy(ldesc[1],&line[2]);
					break;
				case 4:         // third long description line
					if (strcmp(&line[2]," ")!=0)
					{
						p = &line[2];
						while (isspace(*p) && *p != 0)
							p++;
						// anything meaningful?
						if (*p != 0)
						{
							sstr_c(" ");
							sstrcr_c(&line[2]);
							numlines++;
							lp++;
						}
					}
					// save long desc for return
					if (ldesc != NULL)
						strcpy(ldesc[2],&line[2]);
			}
		}
		if (lp == 0)
		{
			sstrcrl("NODESCRIPTION");
			numlines++;
		}
	}
	else if (type == 'C')
	{
		// read the file
		// 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_c("    ");
			sstrcr_c(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, unsigned int *kused, time_t
since_time, float uratio, int timelimit, time_t logon, int timeused)
{
	FILE   *infile;
	FInfo  *flist[20];          // five files on screen at once
	FInfo  *mlist[20];          // the file info for files he has marked
	FInfo  *rec = NULL;         // 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	*bcast;
	char   c;
	int    day,mon,year;        // for 'from' date selection
	int    display_dir = 0;     // 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
	char   bsstr[] =            // backspace
	{
		0x8,0x20,0x8,0x0
	};


	if (list_obj.numrecs() == 0)
	{
		cr();
		cr();
		sstrcrl("NOFILESFOUND");
		waitcr();
		return(0);
	}
	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);
		sstrcrl("NOFILESFOUND");
		waitcr();
		return(0);
	}
	// got the file.  let's ask him what he wants
	clear_scr();
	sstrcrl("LISTFILESAS");
	cr();
	sstrl("YOURCHOICE");
	while (c = gch(1), c != '1' && c != '2');
	if (c == '1')
		oneline = 1;
	else
		oneline = 0;
	cr();
	cr();
	if (!since_time)
	{
		sstrcrl("LISTFILESORDER");
		cr();
		sstrl("YOURCHOICE");
		c = 0;
		while (c != '1' && c != '2' && c != '3' && c != '4')
			c = gch(1);
		cr();
		cr();
		printf("before sort\n");
		fflush(stdout);
		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();
					sstrl("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);
						}
						// move back one (for forward)
						rec = list_obj.previous();
					}
				}
		}
	}
	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)
		{
			sstrcrl("NONEWFILES");
			waitcr();
			return(0);
		}
		// move back one (for forward)
		rec = list_obj.previous();
	}
	// 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)
	printf("Loop top\n");
	fflush(stdout);
	while (!done)
	{
		x = 0;
		clear_scr();
		numlines=1;
		sprintf(tmpstr,gstrl("SECTIONTITLE"),long_desc,list_obj.numrecs());
		sstrcr_c(tmpstr);
		if (oneline)
			sstrcrl("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)
			{                   // anything on screen?
				if (x == 0)
					continue;   // no, list
				x = 20;         // yes, stop there
				continue;
			}
			if (oneline)
			{
				tmrec = localtime(&rec->date);
				strftime(datestr,11,"%x",tmrec);
				sprintf(tmpstr,"\\o\\f6%2d. \\f5%-14.14s \\f3%7ld \\f5%s \\n%2d \\f7%c \\f4%-38.38s",x+1,rec->name,rec->size,datestr,rec->numdls,rec->avail,rec->sdesc);
				if (numfiles > 0)
				{
					for (y=0; y<numfiles; y++)
						if (!strcmp(mlist[y]->name,rec->name))
							sprintf(tmpstr,"%2d.*%-14.14s %7ld %s %2d %c %-38.38s",x+1,
					rec->name,rec->size,datestr,rec->numdls,rec->avail,rec->sdesc);
				}
				sstrcr_c(tmpstr);
			} else
			{
				sprintf(tmpstr,"%2d. ",x+1);
				sstr_c(tmpstr);
				numlines += info(rec,NULL,NULL)+1;
			}
			x++;
		}
		// now give him a prompt
		cr();
		// sysop
		if (strcmp(user->colr,gstrl("BLACK"))==0 || strcmp(username(),sysop)==0)
			sstrl("EDITOPTION");
		if (can_download)
			sstrl("DOWNLOADOPTION");
		sstrl("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 != '-')
		{
			if (c != 0)         // invalid char?
				sstr_c(bsstr);  // then erase it
			time(&now);
			if ((now - then)/60 > inactivity)
			{
				return(numdownls);
			}
			if (bcast = chatobj.check_broadcast(), bcast != NULL)
			{
					// private request?
				if (!chatobj.check_private(bcast,0))
				{                   // nope, regular message
					clear_scr();
					sstrcrl("MESSAGERECEIVED");
					cr();
					sstrcr(bcast);
					cr();
					cr();
					waitcr();
					// exit since he'll be confused otherwise
					return(numdownls);
				}
			}
		}
		cr();
		switch(c)
		{
			case 'e':
				// sysop
				if (strcmp(user->colr,gstrl("BLACK"))==0 || strcmp(username(),sysop)==0)
				{
					sprintf(tmpstr,gstrl("EDITWHICH"),x);
					sstr_c(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,gstrl("DOWNLOADWHICHNUM"),x);
						sstr_c(tmpstr);
						gstr(tmpstr,3);
						if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off <= x)
						{
							if (flist[off-1]->avail != 'Y')
							{
								sprintf(tmpstr,gstrl("FILENOTAVAILABLE"),flist[off-1]->name);
								sstrcr_c(tmpstr);
								waitcr();
							}
							else
							{
								time(&now);
								logofftime =
								(timelimit * 60) - (now - logon) + now;
								logofftime
								+=
								((logofftime-now)
								*
								timelimit_fudge())/100 - timeused*60;
								// add fudge factor

								numdownls +=
								// one file
								download(&flist[off-1],1,1,logofftime);
								*kused += flist[off-1]->size/1024;
								return(numdownls);
							}
						}
					}
					else
					{
						time(&now);
						logofftime =
						(timelimit * 60) - (now - logon) + now - timeused*60;
						// many files
						numdownls += download(mlist,numfiles,1, logofftime);
						for (x=0; x<numfiles; x++)
							*kused += mlist[x]->size/1024;
						numfiles = 0;
						ksel = 0;
						return(numdownls);
					}
				}
				else
				{
					sstrcrl("NOPRIVILIDGES1");
					waitcr();
				}
				break;
			case 'q':           // exit
			case '-':
				if (numfiles > 0)
				{
					clear_scr();
					sstrcrl("NOPRIVILIDGES2");
					sstrl("EXITWITHOUTDOWNLOAD");
					if (yesno())
						return(0);
				}
				else
					return(0);
			case 'm':           // mark a file
				if (numfiles < 19&&can_download)
				{
					if (uratio > fabs(ratio()))
					{
						sstrcrl("BADRATIO");
						sprintf(tmpstr,gstrl("UPLOADRATIO"),ratio());
						sstrcr_c(tmpstr);
						sstrcrl("UPLOADTOFIX1");
						waitcr();
						return(0);
					}
					if (uratio <= 0 && ratio() < 0)
					{
						sstrcrl("UPLOADTOFIX2");
						waitcr();
						return(0);
					}
					sprintf(tmpstr,gstrl("MARKWHICH"),x);
					sstr_c(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();
								sstrcrl("NOTENOUGHSPACE");
								sprintf(tmpstr,gstrl("YOUHAVESPACE"),thisuser->kbytes,waittime());
								sstrcr_c(tmpstr);
								waitcr();
							}
							else if (ksel + (flist[off-1]->size/1024) > maxk())
							{
								cr();
								cr();
								sprintf(tmpstr,gstrl("BATCHSIZEPERDOWNLOAD"),maxk());
								sstrcr_c(tmpstr);
								sstrcrl("BATCHTOOBIG1");
								waitcr();
							}
							else
							{
								if (flist[off-1]->avail != 'Y')
								{
									sprintf(tmpstr,gstrl("FILEUNAVAILABLE"),flist[off-1]->name);
									sstrcr_c(tmpstr);
									waitcr();
								}
								else
								{
									ksel += flist[off-1]->size/1024;
									cr();
									cr();
									sprintf(tmpstr,gstrl("MARKEDFORDOWNLOAD"),flist[off-1]->name);
									sstrcr_c(tmpstr);
									waitcr();
									mlist[numfiles++] = flist[off-1];
								}
							}
						}
					}
				}
				else
				{
					if (numfiles >= 19)
						sstrcrl("BATCHTOOBIG2");
					else
						sstrcrl("NOPRIVILIDGES1");
					waitcr();
				}
				break;
			case 'i':           // info on a file
				sprintf(tmpstr,gstrl("INFOWHICH"),x);
				sstr_c(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();
					sstrcrl("FILESMARKED");
					for (x=0; x<numfiles; x++)
					{
						sprintf(tmpstr,"%d.  %s",x+1,mlist[x]->name);
						sstrcr_c(tmpstr);
					}
					cr();
					sstrl("UNMARKWHICH");
					gstr(tmpstr,3);
					if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off < numfiles+1)
					{
						sprintf(tmpstr,gstrl("FILEUNMARKED"),mlist[off-1]);
						sstrcr_c(tmpstr);
						// umark the file.  compact list
						t = 0;
						for (x=0; x<numfiles; x++)
							if (x != off-1)
								flist[x] = mlist[t++];
						else
							t++;
						// now copy back w/o file
						for (x=0; x<numfiles-1; x++)
							mlist[x] = flist[x];
						numfiles--;
						waitcr();
					}
				}
		}
		// go back so that 'next' batch is right
		while (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(unsigned int *kused, float uratio, char *filename, int counts)
{
	char   str[50];
	char   tmpstr[20];
	FInfo  *rec,tmprec;

	// get filename
	clear_scr();
	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
	{
		sstrcrl("CASEMATTERS");
		cr();
		sstrl("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,gstrl("UNABLETOFIND"),tmpstr);
			sstrcr_c(str);
			waitcr();
			return(0);
		}
		if (rec->avail == 'N')
		{
			sprintf(str,gstrl("FILEUNAVAILABLE"),rec->name);
			sstrcr_c(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();
			sstrcrl("NOTENOUGHSPACE");
			sprintf(tmpstr,gstrl("YOUHAVESPACE"),thisuser->kbytes,waittime());
			sstrcr_c(tmpstr);
			waitcr();
			return(0);
		}
		if (uratio > fabs(ratio()))
		{
			sstrcrl("BADRATIO");
			sprintf(tmpstr,gstrl("UPLOADRATIO"),ratio());
			sstrcr_c(tmpstr);
			sstrcrl("UPLOADTOFIX1");
			waitcr();
			return(0);
		}
		if (uratio <= 0 && ratio() < 0)
		{
			sstrcrl("UPLOADTOFIX2");
			sstrcrl("UPLOADTOFIX3");
			sstrcrl("UPLOADTOFIX4");
			waitcr();
			return(0);
		}
	}
	download(&rec,1,counts,0);
	return(1);
};

// Function:   one_upload
// Purpose:    upload file(s) to the BBS with no entries in files section
// Input:  name - name of user (for temp directory name)
//		uppath - path (directory) to use as a target
// 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::one_upload(char *uname, char *uppath)
{
	FILE   *infile;             // protocols file
	int    off;                 // offset into line
	int    x;
	int    numprot;             // number of 'real' protocols
	int    protsel = 0;         // index of selected protocol
	int    done;                // loop boolean
	char   c;                   // one char from file
	char   tmpstr[255];         // temp str
	char   bbsdir[255];         // temp str
	// 15 commands max
	char   comm[MAX_DL_COMMANDS][50];
	char   key[MAX_DL_COMMANDS];// key for selecting command
	// key for selecting command
	char   needs_filename[MAX_DL_COMMANDS];
	// filename
	char   filename[MAX_FILENAMELENGTH];
	// text describing command
	char   text[MAX_DL_COMMANDS][50];
	char   line[255];

	clear_scr();
	// not checking error
	strcpy(bbsdir,getenv("BBSDIR"));
	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)
		// line can't be less than 5 chars long
		if (off < 5 || feof(infile))
			continue;
		if (line[0] == 'U')     // we care about upload only
		{
			// get command
			off = 2;
			while (line[off] != '|' && line[off] != 0)
			{
				comm[numprot][off-2] = line[off++];
			}
			// add null
			comm[numprot][off-2] = 0;
			off++;              // skip |
			// get 'needs filename' information
			needs_filename[numprot] = line[off++];
			off++;              // skip |
			// get hot key
			key[numprot] = line[off++];
			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();
	sstrcrl("SELECTUPLOADPROTOCOL");
	cr();
	for (x=0; x<numprot; x++)
		sstrcr_c(text[x]);
	cr();
	sstrl("YOURCHOICE");
	done = 0;
	while (!done)
	{
		c = gch(1);
		if (c == 'q')
			return(0);
		for (x=0; x<numprot; x++)
			if (key[x] == c)
		{
			done++;
			protsel = x;
		}
	}
	if (uppath == NULL)	// NULL for /bbs/tmp/$LOGNAME dir
	{
		// Got protocol.  Now create temp directory.
		sprintf(tmpstr,"%s/tmp/%s",bbsdir,uname);
		if (mkdir(tmpstr,(S_IRUSR | S_IWUSR | S_IXUSR)) != 0 && errno != EEXIST)
		{
			sprintf(tmpstr,"Unable to create %s/tmp/%s",bbsdir,uname);
			ap_log(tmpstr);
			return(-1);
		}
	}
	else	// got a dir to put it
		strcpy(tmpstr,uppath);
	// Ok.  Change current working directory to new directory
	chdir(tmpstr);              // change to new directory
	if (needs_filename[protsel] == 'y')
	{
		cr();
		cr();
		sstrcrl("QTOEXIT");
		sstrl("ENTERFILENAME");
		gstr(filename,MAX_FILENAMELENGTH);
		fflush(stdout);
		if (strcmp(filename,"q") == 0)
		{
			sstrcrl("UPLOADABORTED");
			waitcr();
			return(0);
		}
	}
	cr();
	cr();
	sstrcrl("READYTOUPLOAD");
	cr();
	strcpy(tmpstr,comm[protsel]);
	if (needs_filename[protsel] == 'y')
	{
		strcat(tmpstr," ");
		strcat(tmpstr,filename);
	}
	// ok.  pass to system
	waitcr();
	sysint(tmpstr,0,0);
	clear_scr();
	cr();
	cr();
	sstrcrl("ENDOFUPLOAD");
	cr();
	waitcr();
	// log the upload
	if (uppath != NULL)
		sprintf(tmpstr,"%s uploaded file(s) to %s",uname,uppath);
	else
		sprintf(tmpstr,"%s uploaded file(s) to /bbs/tmp",uname);
	ap_log(tmpstr);
	return(0);
};


// 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
		// not checking because he shouldn't
		strcpy(tmpstr,getenv("BBSDIR"));
		// get here if bbsdir not set
		// tack on files header
		strcat(tmpstr,"/filehdr/bbs_files_hdr");
		if (infile = bopen(tmpstr,"r"), infile == NULL)
		{
			sprintf(tmpstr,"Unable to open main files section header (bbs_files_hdr) %s",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));
			if (feof(infile))
				continue;
			// 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
		{
			// not checking because he shouldn't make it this far
			strcpy(tmpstr,getenv("BBSDIR"));
			// get name of file area
			strcat(tmpstr,"/filehdr/");
			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);
							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)
								// turn into null
								u[0] = 0;
							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
			// should be absolute
			strcpy(tmpstr,header_path);
			if (infile = bopen(tmpstr,"r"), infile  == NULL)
			{
				clear_scr();
				sstrcrl("CDROMOFFLINE");
				waitcr();
				return(1);
			}
			// skip the lines that have a space at the start of the line
			if (c = fgetc(infile), isspace(c))
			{
				while (isspace(c) && !feof(infile))
				{
					while (c = fgetc(infile), c != '\n' && !feof(infile));
					// should be a space if comment
						c = fgetc(infile);
				}
			}
			// ok.  now read everything into dllist object
			line = 0;
			// allow for previous skip
			word[0] = tolower(c);
			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-1 && c != '\r')
						newrec.sdesc[off++] = c;
				newrec.sdesc[off] = 0;
				newrec.numdls = 0;
				// no uploader
				newrec.uploader[0] = 0;
				// should be absolute
				strcpy(tmpstr,header_path);
				// 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;
		// first line?
		if (line[0] == '[' && line[1] == 'A')
		{
			if (!sscanf(&line[2],"%s %*d %*d %*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);
	// change owner to bbs
	chown(tmpstr,bbs_uid(),bbs_gid());
	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, int timeused)
{
	// filename to search for
	char   fname[MAX_FILENAMELENGTH+1];
	FInfo  *rec;
	time_t now;
	time_t logofftime;

	clear_scr();
	sstrcrl("ENTERSUBSTRING");
	cr();
	sstrl("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();
				sstrl("DOWNLOADTHISFILE");
				time(&now);
				logofftime = (timelimit * 60) - (now - logon) + now - timeused*60;
				if (yesno())
					download(&rec,1,1,logofftime);
			}
			sstrl("CONTINUESEARCH");
			if (!yesno())
				break;
		}
	}
	cr();
	sstrcrl("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 = 0;         // 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
	// 15 commands max
	char   comm[MAX_DL_COMMANDS][50];
	char   key[MAX_DL_COMMANDS];// key for selecting command
	// key for selecting command
	char   needs_filename[MAX_DL_COMMANDS];
	// filename
	char   filename[MAX_FILENAMELENGTH];
	// text describing command
	char   text[MAX_DL_COMMANDS][50];
	char   line[255];

	clear_scr();
	if (type != 'R')            // no uploads except for rocat areas
	{
		cr();
		sstrcrl("NOUPLOAD");
		cr();
		waitcr();
	}
	time(&start);
	numuploads = 0;
	// not checking error
	strcpy(bbsdir,getenv("BBSDIR"));
	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)
		// line can't be less than 5 chars long
		if (off < 5 || feof(infile))
			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++];
			}
			// add null
			comm[numprot][off-2] = 0;
			off++;              // skip |
			// get 'needs filename' information
			needs_filename[numprot] = line[off++];
			off++;              // skip |
			// get hot key
			key[numprot] = line[off++];
			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();
	sstrcrl("SELECTUPLOADPROTOCOL");
	cr();
	for (x=0; x<numprot; x++)
		sstrcr_c(text[x]);
	cr();
	sstrl("YOURCHOICE");
	done = 0;
	while (!done)
	{
		c = gch(1);
		if (c == 'q')
			return(0);
		for (x=0; x<numprot; x++)
			if (key[x] == c)
		{
			done++;
			protsel = x;
		}
	}
	// Got protocol.  Now create temp directory.
	strcpy(tmpstr,bbsdir);
	strcat(tmpstr,"/tmp/");     // bbsdir/tmp
	strcat(tmpstr,uname);       // bbsdir/tmp/username
	if (mkdir(tmpstr,(S_IRUSR | S_IWUSR | S_IXUSR)) != 0 && errno != EEXIST)
	{
		sprintf(tmpstr,"Unable to create %s/tmp/%s",bbsdir,uname);
		ap_log(tmpstr);
		return(-1);
	}
	// Ok.  Change current working directory to new directory
	chdir(tmpstr);              // change to new directory
	if (needs_filename[protsel] == 'y')
	{
		cr();
		cr();
		sstrcrl("QTOEXIT");
		sstrl("ENTERFILENAME");
		gstr(filename,MAX_FILENAMELENGTH);
		fflush(stdout);
		if (strcmp(filename,"q") == 0)
		{
			sstrcrl("UPLOADABORTED");
			waitcr();
			return(0);
		}
	}
	cr();
	cr();
	sstrcrl("READYTOUPLOAD");
	cr();
	strcpy(tmpstr,comm[protsel]);
	if (needs_filename[protsel] == 'y')
	{
		strcat(tmpstr," ");
		strcat(tmpstr,filename);
	}
	// ok.  pass to system
	waitcr();
	sysint(tmpstr,0,0);
	clear_scr();
	cr();
	cr();
	sstrl("UPLOADSUCCESSFUL");
	if (!yesno())
	{
		sstrl("SAVETRANSFER");
		if (!yesno())
		{
			sprintf(tmpstr,"rm -rf %s/tmp/%s",bbsdir,uname);
			sysint(tmpstr,0,0);
		}
		return(0);
	}
	// now look at directory and get filename(s) that were uploaded
	time(&now);
	strcpy(tmpstr,bbsdir);
	strcat(tmpstr,"/filehdr/"); // tack on files header
	strcat(tmpstr,name);
	if (outfile = bopen(tmpstr,"a"), outfile == NULL)
	{
		sprintf(tmpstr,"files.upload: Unable to open files section header %s",name);
		ap_log(tmpstr);
		return(0);
	}
	strcpy(tmpstr,bbsdir);
	strcat(tmpstr,"/tmp/");
	strcat(tmpstr,uname);
	if (fdir = opendir(tmpstr), fdir == NULL)
	{
		sprintf(line,"Unable to open directory %s for reading.\n",tmpstr);
		bclose(outfile);
		ap_log(line);
		return(0);
	}
	// ok.  now loop for every file found, getting file information
	while (dentry = readdir(fdir), dentry != NULL)
	{
		strcpy(tmpstr,bbsdir);
		strcat(tmpstr,"/tmp/");
		strcat(tmpstr,uname);
		strcat(tmpstr,"/");
		// for stat
		strcat(tmpstr,dentry->d_name);
		if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode))
		{
			sprintf(tmpstr,gstrl("UPLOADCORRECTLY"),dentry->d_name);
			sstr_c(tmpstr);
			if (!yesno())
				continue;       // skip bad file upload
			fprintf(outfile,"[A %s 0 %s ]\n",uname, dentry->d_name);
			sprintf(tmpstr,gstrl("ENTERINFORMATION"),dentry->d_name);
			sstrcr_c(tmpstr);
			cr();
			sstrcrl("SHORTDESCRIPTION");
			sstr_c(": ");
			gstr(tmpstr,40);
			fprintf(outfile,"[B %s ]\n",tmpstr);
			// get machine requirements
			cr();
			sstrcrl("SOFTHARDWAREDESCRIPTION");
			sprintf(tmpstr,gstrl("FORFILE"),dentry->d_name);
			sstr_c(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();
			sstrcrl("LONGDESCRIPTION0");
			waitcr();
			strcpy(filename,tmpstr);
			strcpy(tmpstr,editor);
			strcat(tmpstr," /tmp/");
			strcat(tmpstr,filename);
			sysint(tmpstr,0,0);
			strcpy(tmpstr,"/tmp/");
			// now open file
			strcat(tmpstr,filename);
			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?
			{
				// no lines
				if (linenum == 0)
				{
					fprintf(outfile,"[D %s ]\n",line);
					fprintf(outfile,"[E ]\n");
					fprintf(outfile,"[F ]\n");
				}
				// one line
				else if (linenum == 1)
				{
					fprintf(outfile,"[E %s ]\n",line);
					fprintf(outfile,"[F ]\n");
				}
				// two lines
				else if (linenum == 2)
					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
	// change owner to bbs
	chown(tmpstr,bbs_uid(),bbs_gid());
	sprintf(tmpstr,"%s/tmp/%s",bbsdir,uname);
	chmod(tmpstr,0775);         // chmod 775
	// change owner to bbs
	chown(tmpstr,bbs_uid(),bbs_gid());
	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();
	sstrcrl("ENDOFUPLOAD");
	cr();
	sstrcrl("UPLOADMOVE");
	waitcr();
	time(&end);
	*credminutes = (end - start)/60;
	return(numuploads);
};


#endif                          // _FILES_C_






