// 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; x<numprot; x++)
		sstrcr(text[x]);
	cr();
	sstr(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.  Now do the download.
	cr();
	cr();
	sstrcr(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;
	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; y<numfiles; y++)
						if (!strcmp(mlist[y]->name,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; x<numfiles; x++)
						*kused += mlist[x]->size/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<numfiles; x++)
				{
					sprintf(tmpstr,"%d. %s",x+1,mlist[x]);
					sstrcr(tmpstr);
				}
				cr();
				sstr(UNMARKWHICH);
				gstr(tmpstr,3);
				if (sscanf(tmpstr,"%d",&off) == 1 && off > 0 && off < numfiles+1)
				{
					sprintf(tmpstr,FILEUNMARKED,mlist[off-1]);
					sstrcr(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(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; x<numprot; x++)
		sstrcr(text[x]);
	cr();
	sstr(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();
		sstrcr(QTOEXIT);
		sstr(ENTERFILENAME);
		gstr(filename,MAX_FILENAMELENGTH);
		fflush(stdout);
		if (strcmp(filename,"q") == 0)
		{
			sstrcr(UPLOADABORTED);
			waitcr();
			return(0);
		}
	}
	cr();
	cr();
	sstrcr(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();
	cr();
	cr();
	sstr(UPLOADSUCCESSFUL);
	if (!yesno())
	{
		sstr(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,"/");
		strcat(tmpstr,dentry->d_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_
