#line 1 "XFER_OVL.C"

/* Copyright 1992 Digital Dynamics, All rights reserved. */

#include "sbbs.h"

/****************************************************************************/
/* List detailed information about the files in 'filespec'. Prompts for     */
/* action depending on 'mode.'                                              */
/* Returns number of files matching filespec that were found                */
/****************************************************************************/
int listfileinfo(uint dirnum, char *filespec, char mode)
{
    uchar str[258],path[258],dirpath[256],done=0,nostop=0,ch,fname[13];
    uchar huge *ixbbuf,*usrxfrbuf=NULL,*p;
    int i,j,found=0,file;
    ulong l,m,usrcdt,usrxfrlen;
    time_t start,end;
    file_t f;

sprintf(str,"%sXFER.IXT",indx_dir);
if(mode==FI_USERXFER && flength(str)>0L) {
    if((file=nopen(str,O_RDONLY))==-1) {
        errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
        return(0); }
    usrxfrlen=filelength(file);
    if((usrxfrbuf=farmalloc(usrxfrlen))==NULL) {
        close(file);
        errormsg(WHERE,ERR_ALLOC,str,usrxfrlen);
        return(0); }
    if(read(file,usrxfrbuf,usrxfrlen)!=usrxfrlen) {
        close(file);
        farfree(usrxfrbuf);
        errormsg(WHERE,ERR_READ,str,usrxfrlen);
        return(0); }
    close(file); }
sprintf(str,"%sDIRS\\%s.IXB",indx_dir,dir[dirnum]->code);
if((file=nopen(str,O_RDONLY))==-1)
    return(0);
l=filelength(file);
if(!l) {
    close(file);
    return(0); }
if((ixbbuf=(char *)farmalloc(l))==NULL) {
    close(file);
    errormsg(WHERE,ERR_ALLOC,str,l);
    return(0); }
if(lread(file,ixbbuf,l)!=l) {
    close(file);
    errormsg(WHERE,ERR_READ,str,l);
    farfree((char *)ixbbuf);
    if(usrxfrbuf)
        farfree(usrxfrbuf);
    return(0); }
close(file);
sprintf(str,"%sDIRS\\%s.DAT",data_dir,dir[dirnum]->code);
if((file=nopen(str,O_RDONLY))==-1) {
    errormsg(WHERE,ERR_READ,str,O_RDONLY);
    farfree((char *)ixbbuf);
    if(usrxfrbuf)
        farfree(usrxfrbuf);
    return(0); }
m=filelength(file);
close(file);
m=0;
while(online && !done && m<l) {
    if(mode==FI_REMOVE && SYSOP)
        action=NODE_SYSP;
    else action=NODE_LFIL;
    if(msgabort()) {
        found=-1;
        break; }
    for(i=0;i<12 && m<l;i++)
        if(i==8)
            str[i]='.';
        else
            str[i]=ixbbuf[m++];     /* Turns FILENAMEEXT into FILENAME.EXT */
    str[i]=0;
    unpadfname(str,fname);
    if(filespec[0] && !filematch(str,filespec)) {
        m+=11;
        continue; }
    f.datoffset=ixbbuf[m]|((long)ixbbuf[m+1]<<8)|((long)ixbbuf[m+2]<<16);
    f.dateuled=ixbbuf[m+3]|((long)ixbbuf[m+4]<<8)
        |((long)ixbbuf[m+5]<<16)|((long)ixbbuf[m+6]<<24);
    f.datedled=ixbbuf[m+7]|((long)ixbbuf[m+8]<<8)
        |((long)ixbbuf[m+9]<<16)|((long)ixbbuf[m+10]<<24);
    m+=11;
    if(mode==FI_OLD && f.datedled>ns_time)
        continue;
    if((mode==FI_OLDUL || mode==FI_OLD) && f.dateuled>ns_time)
        continue;
    if(mode==FI_OFFLINE) {      /* look in default path first */
        sprintf(path,"%s%s",dir[dirnum]->path,fname);
        if(fexist(path))
            continue; }
    f.dir=dirnum;
    strcpy(f.name,str);
    getfiledat(&f);
    if(mode==FI_OFFLINE && f.altpath>0 && f.altpath<=altpaths) {
        sprintf(path,"%s%s",altpath[f.altpath-1],fname);
        if(fexist(path))
            continue; }
    if(f.altpath>0 && f.altpath<=altpaths)
        strcpy(dirpath,altpath[f.altpath-1]);
    else
        strcpy(dirpath,dir[f.dir]->path);
    if(mode==FI_CLOSE && !f.opencount)
        continue;
    if(mode==FI_USERXFER) {
        for(p=usrxfrbuf;p<usrxfrbuf+usrxfrlen;p+=24) {
            sprintf(str,"%17.17s",p);   /* %4.4u %12.12s */
            if(!strcmp(str+5,f.name) && useron.number==atoi(str))
                break; }
        if(p>=usrxfrbuf+usrxfrlen) /* file wasn't found */
            continue; }
    if((mode==FI_REMOVE) && (!SYSOP && strcmpi(f.uler
        ,useron.alias) && !(useron.exempt&FLAG('R')))) continue;
    if(mode==FI_INFO && found && !nostop) {
        SYNC;
        mnemonics(text[FileInfoPrompt]);
        switch(getkeys("QC\r",0)) {
            case 'Q':
                done=1;
                found=-1;
                continue;
            case 'C':
                nostop=1;
                break; } }
    found++;
    fileinfo(f);
    if(mode==FI_CLOSE) {
        if(!noyes(text[CloseFileRecordQ])) {
            f.opencount=0;
            putfiledat(f); } }
    else if(mode==FI_REMOVE || mode==FI_OLD || mode==FI_OLDUL
        || mode==FI_OFFLINE) {
        SYNC;
        CRLF;
        if(f.opencount) {
            mnemonics(text[QuitOrNext]);
            strcpy(str,"Q\r"); }
        else if(SYSOP) {
            mnemonics(text[SysopRemoveFilePrompt]);
            strcpy(str,"EFMCQR\r"); }
        else if(useron.exempt&FLAG('R')) {
            mnemonics(text[RExemptRemoveFilePrompt]);
            strcpy(str,"EMQR\r"); }
        else {
            mnemonics(text[UserRemoveFilePrompt]);
            strcpy(str,"EQR\r"); }
        switch(getkeys(str,0)) {
            case 'E':   /* edit file information */
                if(SYSOP) {
                    bputs(text[EditFilename]);
                    strcpy(str,fname);
                    getstr(str,12,K_EDIT|K_UPPER);
                    if(strcmp(str,fname)) { /* rename */
                        padfname(str,path);
                        if(findfile(f.dir,path))
                            bprintf(text[FileAlreadyThere],path);
                        else {
                            sprintf(path,"%s%s",dirpath,fname);
                            sprintf(tmp,"%s%s",dirpath,str);
                            if(rename(path,tmp))
                                bprintf(text[CouldntRenameFile],path,tmp);
                            else {
                                bprintf(text[FileRenamed],path,tmp);
                                if(f.misc&FM_EXTDESC) {
                                    sprintf(path,"%sDIRS\\%s.EXT\\%s"
                                        ,data_dir,dir[f.dir]->code,fname);
                                    sprintf(tmp,"%sDIRS\\%s.EXT\\%s"
                                        ,data_dir,dir[f.dir]->code,str);
                                    rename(path,tmp); }
                                strcpy(fname,str);
                                removefiledat(f);
                                strcpy(f.name,padfname(str,tmp));
                                addfiledat(&f); } } } }
                bputs(text[EditDescription]);
                getstr(f.desc,LEN_FDESC,K_LINE|K_EDIT);
                sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir,dir[f.dir]->code
                    ,fname);
                if(f.misc&FM_EXTDESC) {
                    if(!noyes(text[EditExtDescriptionQ]))
                        editfile(str);
                    else if(!noyes(text[DeleteExtDescriptionQ])) {
                        remove(str);
                        f.misc&=~FM_EXTDESC; } }
                else if(dir[f.dir]->misc&DIR_EXTDESC || SYSOP
                    || useron.exempt&FLAG('R')) {
                    if(!noyes(text[CreateExtDescriptionQ])
						&& writemsg(str,nulstr,nulstr,WM_EXTDESC,0,nulstr))
                            f.misc|=FM_EXTDESC; }
                if(!SYSOP) {
                    putfiledat(f);
                    break; }
                bputs(text[EditUploader]);
                getstr(f.uler,LEN_ALIAS,K_UPRLWR|K_EDIT);
                ultoa(f.cdt,str,10);
                bputs(text[EditCreditValue]);
                getstr(str,7,K_NUMBER|K_EDIT);
                f.cdt=atol(str);
                itoa(f.timesdled,str,10);
                bputs(text[EditTimesDownloaded]);
                getstr(str,5,K_NUMBER|K_EDIT);
                f.timesdled=atoi(str);
                if(f.opencount) {
                    itoa(f.opencount,str,10);
                    bputs(text[EditOpenCount]);
                    getstr(str,3,K_NUMBER|K_EDIT);
                    f.opencount=atoi(str); }
                if(altpaths || f.altpath) {
                    itoa(f.altpath,str,10);
                    bputs(text[EditAltPath]);
                    getstr(str,3,K_NUMBER|K_EDIT);
                    f.altpath=atoi(str);
                    if(f.altpath>altpaths)
                        f.altpath=0; }
                putfiledat(f);
                break;
            case 'F':   /* delete file only */
                sprintf(str,"%s%s",dirpath,fname);
                if(!fexist(str))
                    bputs(text[FileNotThere]);
                else {
                    if(!noyes(text[AreYouSureQ])) {
                        if(remove(str))
                            bprintf(text[CouldntRemoveFile],str);
                        else {
                            sprintf(tmp,"Deleted %s",str);
                            logline(nulstr,tmp); } } }
                break;
            case 'R':   /* remove file from database */
                if(noyes(text[AreYouSureQ]))
                    break;
                removefiledat(f);
                sprintf(str,"Removed %s from %s %s",f.name
                    ,lib[dir[f.dir]->lib]->sname,dir[f.dir]->sname);
                logline("U-",str);
                sprintf(str,"%s%s",dirpath,fname);
                if(fexist(str)) {
                    if(SYSOP) {
                        if(!noyes(text[DeleteFileQ])) {
                            if(remove(str))
                                bprintf(text[CouldntRemoveFile],str);
                            else {
                                sprintf(tmp,"Deleted %s",str);
                                logline(nulstr,tmp); } } }
                    else if(remove(str))    /* always remove if not sysop */
                        bprintf(text[CouldntRemoveFile],str); }
                if(SYSOP || useron.exempt&FLAG('R')) {
                    i=lib[dir[f.dir]->lib]->offline_dir;
                    if(i!=dirnum && i!=(int)INVALID_DIR
                        && !findfile(i,f.name)) {
                        sprintf(str,text[AddToOfflineDirQ]
                            ,fname,lib[dir[i]->lib]->sname,dir[i]->sname);
                        if(yesno(str)) {
                            if(f.misc&FM_EXTDESC) {
                                sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir
                                    ,dir[f.dir]->code,fname);
                                sprintf(path,"%sDIRS\\%s.EXT\\%s",data_dir
                                    ,dir[i]->code,fname);
                                mv(str,path,0); }
                            f.dir=i;
                            addfiledat(&f); } } }
                if(f.misc&FM_EXTDESC) {
                    sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir
                        ,dir[dirnum]->code,fname);
                    remove(str); }
                if(SYSOP || strcmpi(f.uler,useron.alias)) {
                    if(noyes(text[RemoveCreditsQ]))
/* Fall through */      break; }
            case 'C':   /* remove credits only */
                if((i=matchuser(f.uler))==0) {
                    bputs(text[UserNotFound]);
                    break; }
                if(SYSOP) {
                    usrcdt=(ulong)(f.cdt*(cdt_up_pct/100.0)); /* upload pts */
                    if(f.timesdled)     /* all downloads */
                        usrcdt+=(ulong)((long)f.timesdled
                            *f.cdt*(cdt_dn_pct/100.0));
                    ultoa(usrcdt,str,10);
                    bputs(text[CreditsToRemove]);
                    getstr(str,10,K_NUMBER|K_LINE|K_EDIT);
                    f.cdt=atol(str); }
                usrcdt=adjustuserrec(i,U_CDT,10,-f.cdt);
                if(i==useron.number)
                    useron.cdt=usrcdt;
                sprintf(str,text[FileRemovedUserMsg]
                    ,f.name,ultoac(f.cdt,tmp));
                putsmsg(i,str);
                usrcdt=adjustuserrec(i,U_ULB,10,-f.size);
                if(i==useron.number)
                    useron.ulb=usrcdt;
                usrcdt=adjustuserrec(i,U_ULS,5,-1);
                if(i==useron.number)
                    useron.uls=usrcdt;
                break;
            case 'M':   /* move the file to another dir */
                for(i=0;i<usrlibs;i++)
                    bprintf(text[MoveToLibLstFmt],i+1,lib[usrlib[i]]->lname);
                SYNC;
                bprintf(text[MoveToLibPrompt],dir[dirnum]->lib+1);
				if((i=getnum(usrlibs))==-1)
                    continue;
                if(!i)
                    i=dir[dirnum]->lib;
                else
                    i--;
                CRLF;
                for(j=0;j<usrdirs[i];j++)
                    bprintf(text[MoveToDirLstFmt]
                        ,j+1,dir[usrdir[i][j]]->lname);
                SYNC;
                bprintf(text[MoveToDirPrompt],usrdirs[i]);
                if((j=getnum(usrdirs[i]))==-1)
                    continue;
                if(!j)
                    j=usrdirs[i]-1;
                else j--;
                CRLF;
                if(findfile(usrdir[i][j],f.name)) {
                    bprintf(text[FileAlreadyThere],f.name);
                    break; }
                removefiledat(f);
                f.dir=usrdir[i][j];
                addfiledat(&f);
                bprintf(text[MovedFile],f.name
                    ,lib[dir[f.dir]->lib]->sname,dir[f.dir]->sname);
                sprintf(str,"Moved %s to %s %s",f.name
                    ,lib[dir[f.dir]->lib]->sname,dir[f.dir]->sname);
                logline(nulstr,str);
                if(!f.altpath) {    /* move actual file */
                    sprintf(str,"%s%s",dir[dirnum]->path,fname);
                    if(fexist(str)) {
                        sprintf(path,"%s%s",dir[f.dir]->path,fname);
                        mv(str,path,0); } }
                if(f.misc&FM_EXTDESC) {
                    sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir
                        ,dir[dirnum]->code,fname);
                    sprintf(path,"%sDIRS\\%s.EXT\\%s",data_dir
                        ,dir[f.dir]->code,fname);
                    mv(str,path,0); }
                break;
            case 'Q':   /* quit */
                found=-1;
                done=1;
                break; } }
    else if(mode==FI_DOWNLOAD || mode==FI_USERXFER) {
        sprintf(path,"%s%s",dirpath,fname);
        if(f.size<1L) { /* getfiledat will set this to -1 if non-existant */
            SYNC;       /* and 0 byte files shouldn't be d/led */
            mnemonics(text[QuitOrNext]);
            if(getkeys("\rQ",0)=='Q') {
                found=-1;
                break; }
            continue; }
        if(!(dir[f.dir]->misc&DIR_FREE) && !(useron.exempt&FLAG('D'))
            && f.cdt>useron.cdt) {
            SYNC;
            bprintf(text[YouOnlyHaveNCredits],ultoac(useron.cdt,tmp));
            mnemonics(text[QuitOrNext]);
            if(getkeys("\rQ",0)=='Q') {
                found=-1;
                break; }
            continue; }
		if(dir[f.dir]->dllevel>useron.tl
			|| !FLAGCMP(useron.tf,dir[f.dir]->dlflags)) {
			SYNC;
			bputs(text[CantDownloadFromDir]);
			mnemonics(text[QuitOrNext]);
            if(getkeys("\rQ",0)=='Q') {
                found=-1;
                break; }
            continue; }
        if(f.timetodl>timeleft && !SYSOP && !(useron.exempt&FLAG('T'))) {
            SYNC;
            bputs(text[NotEnoughTimeToDl]);
            mnemonics(text[QuitOrNext]);
            if(getkeys("\rQ",0)=='Q') {
                found=-1;
                break; }
            continue; }
        menu("DLPROT");
        openfile(f);
        SYNC;
        mnemonics(text[ProtocolBatchQuitOrNext]);
        strcpy(str,"BQ\r");
        for(i=0;i<total_prots;i++)
            if(prot[i]->dlcmd[0]) {
                sprintf(tmp,"%c",prot[i]->mnemonic);
                strcat(str,tmp); }
        ch=getkeys(str,0);
        if(ch=='Q') {
            found=-1;
            done=1; }
        else if(ch=='B') {
            if(!addtobatdl(f)) {
                closefile(f);
                break; } }
        else if(ch!=CR) {
            for(i=0;i<total_prots;i++)
                if(prot[i]->mnemonic==ch)
                    break;
            if(i<total_prots) {
                delfiles(temp_dir,"*.*");
                if(dir[f.dir]->misc&DIR_CPTOTMP) {
                    lncntr=0;
                    bprintf(text[RetrievingFile],fname);
                    sprintf(str,"%s%s",dirpath,fname);
                    sprintf(path,"%s%s",temp_dir,fname);
                    mv(str,path,1); /* copy the file to temp dir */
                    CRLF; }
                getnodedat(node_num,&thisnode,1);
                action=NODE_DLNG;
                unixtodos(now+f.timetodl,&date,&curtime);
                thisnode.aux=(curtime.ti_hour*60)+curtime.ti_min;
                putnodedat(node_num,thisnode); /* calculate ETA */
                start=time(NULL);
                j=protocol(cmdstr(prot[i]->dlcmd,path,nulstr));
                end=time(NULL);
                if(prot[i]->misc&PROT_DSZLOG) {
                    if(checkprotlog(f))
                        downloadfile(f);
                    else
                        notdownloaded(f.size,start,end); }
                else {
                    if(!j)
                        downloadfile(f);
                       else
                        notdownloaded(f.size,start,end); }
                delfiles(temp_dir,"*.*");
                autohangup(); } }
        closefile(f); }
    if(filespec[0] && !strchr(filespec,'*') && !strchr(filespec,'?')) break; }
farfree((char *)ixbbuf);
if(usrxfrbuf)
    farfree(usrxfrbuf);
return(found);
}


/****************************************************************************/
/* Checks  directory data file for 'filename' (must be padded). If found,   */
/* it returns the 1, else returns 0.                                        */
/* Called from upload and bulkupload                                        */
/****************************************************************************/
char findfile(uint dirnum, char *filename)
{
    char str[256],c,fname[12],huge *ixbbuf;
    int file;
    ulong length,l;

strcpy(fname,filename);
for(c=8;c<12;c++)   /* Turn FILENAME.EXT into FILENAMEEXT */
    fname[c]=fname[c+1];
sprintf(str,"%sDIRS\\%s.IXB",indx_dir,dir[dirnum]->code);
if((file=nopen(str,O_RDONLY))==-1) return(0);
length=filelength(file);
if(!length) {
    close(file);
    return(0); }
if((ixbbuf=(char *)farmalloc(length))==NULL) {
    close(file);
    errormsg(WHERE,ERR_ALLOC,str,length);
    return(0); }
if(lread(file,ixbbuf,length)!=length) {
    close(file);
    errormsg(WHERE,ERR_READ,str,length);
    farfree((char *)ixbbuf);
    return(0); }
close(file);
for(l=0;l<length;l+=F_IXBSIZE) {
    for(c=0;c<11;c++)
        if(fname[c]!=ixbbuf[l+c]) break;
    if(c==11) break; }
farfree((char *)ixbbuf);
if(l!=length)
    return(1);
return(0);
}


/****************************************************************************/
/* This is the batch menu section											*/
/****************************************************************************/
void batchmenu()
{
	char str[129],tmp2[250],done=0,ch;
	uint i,j,xfrprot;
	ulong totalcdt,totalsize;
	time_t start,end;
	int file,n;
	file_t f;

if(!batdn_total && !batup_total) {
	bputs(text[NoFilesInBatchQueue]);
	return; }
CRLF;
if(useron.misc&RIP && !(useron.misc&EXPERT))
	menu("BATCHXFR");
lncntr=0;
while(online && !done && (batdn_total || batup_total)) {
	if(!(useron.misc&(EXPERT|RIP))) {
		sys_status&=~SS_ABORT;
		if(lncntr) {
			SYNC;
			CRLF;
			if(lncntr)			/* CRLF or SYNC can cause pause */
                pause(); }
		menu("BATCHXFR"); }
	SYNC;
	bputs(text[BatchMenuPrompt]);
	ch=getkeys("BCDLQRU?\r",0);
	if(ch>SP)
		logch(ch,0);
	switch(ch) {
		case '?':
			if(useron.misc&(EXPERT|RIP))
				menu("BATCHXFR");
			break;
		case CR:
		case 'Q':
			lncntr=0;
			done=1;
			break;
		case 'B':   /* Bi-directional transfers */
			if(useron.rest&FLAG('D')) {
				bputs(text[R_Download]);
				return; }
			if(useron.rest&FLAG('U')) {
				bputs(text[R_Upload]);
				return; }
			if(!batdn_total) {
				bputs(text[DownloadQueueIsEmpty]);
				break; }
			if(!batup_total) {
				bputs(text[UploadQueueIsEmpty]);
				break; }
			for(i=0,totalcdt=0;i<batdn_total;i++)
					totalcdt+=batdn_cdt[i];
			if(!(useron.exempt&FLAG('D')) && totalcdt>useron.cdt) {
				bprintf(text[YouOnlyHaveNCredits],ultoac(useron.cdt,tmp));
				break; }
			for(i=0,totalsize=0;i<batdn_total;i++)
				totalsize+=batdn_size[i];
			if(cur_cps && totalsize/(ulong)cur_cps>timeleft) {
				bputs(text[NotEnoughTimeToDl]);
				break; }
			menu("BIPROT");
			if(!create_batchdn_lst())
				break;
			if(!create_batchup_lst())
				break;
			if(!create_bimodem_pth())
				break;
			SYNC;
			mnemonics(text[ProtocolOrQuit]);
			strcpy(tmp2,"Q");
			for(i=0;i<total_prots;i++)
				if(prot[i]->bicmd[0]) {
					sprintf(tmp,"%c",prot[i]->mnemonic);
					strcat(tmp2,tmp); }
			ch=getkeys(tmp2,0);
			if(ch=='Q')
				break;
			for(i=0;i<total_prots;i++)
				if(prot[i]->mnemonic==ch)
					break;
			if(i<total_prots) {
				xfrprot=i;
                action=NODE_BXFR;
                SYNC;
				/* delfiles(temp_dir,"*.*"); fix for CD-ROM */
				for(i=0;i<batdn_total;i++)
					if(dir[batdn_dir[i]]->misc&DIR_CPTOTMP) {
						lncntr=0;
						unpadfname(batdn_name[i],tmp);
                        sprintf(tmp2,"%s%s",temp_dir,tmp);
						if(!fexist(tmp2)) {
							bprintf(text[RetrievingFile],tmp);
							sprintf(str,"%s%s",dir[batdn_dir[i]]->path,tmp);
							mv(str,tmp2,1); /* copy the file to temp dir */
							CRLF; } }
				sprintf(str,"%sBATCHDN.LST",node_dir);
                sprintf(tmp2,"%sBATCHUP.LST",node_dir);
				start=time(NULL);
				protocol(cmdstr(prot[xfrprot]->bicmd,str,tmp2));
				end=time(NULL);
				batch_upload();
				batch_download(xfrprot);
				if(batdn_total)
					notdownloaded(totalsize,start,end);
				/* delfiles(temp_dir,"*.*"); fix for CD-ROM */
				autohangup(); }
            break;
		case 'C':
			if(batup_total) {
				if(!noyes(text[ClearUploadQueueQ])) {
					for(i=0;i<batup_total;i++)
						if(batup_misc[i]&FM_EXTDESC) {	/* remove ext desc */
							sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir
								,dir[batup_dir[i]]->code
								,unpadfname(batup_name[i],tmp));
							remove(str); }
					batup_total=0;
					bputs(text[UploadQueueCleared]); } }
			if(batdn_total) {
				if(!noyes(text[ClearDownloadQueueQ])) {
					for(i=0;i<batdn_total;i++) {
                    	f.dir=batdn_dir[i];
						f.datoffset=batdn_offset[i];
						f.size=batdn_size[i];
						strcpy(f.name,batdn_name[i]);
						closefile(f); }
					batdn_total=0;
					bputs(text[DownloadQueueCleared]); } }
			break;
		case 'D':
			if(useron.rest&FLAG('D')) {
				bputs(text[R_Download]);
				return; }
			if(!batdn_total) {
				bputs(text[DownloadQueueIsEmpty]);
				break; }
			for(i=0,totalcdt=0;i<batdn_total;i++)
				totalcdt+=batdn_cdt[i];
			if(!(useron.exempt&FLAG('D')) && totalcdt>useron.cdt) {
				bprintf(text[YouOnlyHaveNCredits],ultoac(useron.cdt,tmp));
				break; }
			for(i=0,totalsize=0;i<batdn_total;i++)
				totalsize+=batdn_size[i];
			if(cur_cps && totalsize/(ulong)cur_cps>timeleft) {
				bputs(text[NotEnoughTimeToDl]);
				break; }
			menu("BATDPROT");
			if(!create_batchdn_lst())
				break;
			if(!create_bimodem_pth())
				break;
			SYNC;
			mnemonics(text[ProtocolOrQuit]);
			strcpy(tmp2,"Q");
			for(i=0;i<total_prots;i++)
				if(prot[i]->batdlcmd[0]) {
					sprintf(tmp,"%c",prot[i]->mnemonic);
					strcat(tmp2,tmp); }
			ch=getkeys(tmp2,0);
			if(ch=='Q')
				break;
			for(i=0;i<total_prots;i++)
				if(prot[i]->mnemonic==ch)
					break;
			if(i<total_prots) {
				xfrprot=i;
				/* delfiles(temp_dir,"*.*"); fix for CD-ROM */
				for(i=0;i<batdn_total;i++)
					if(dir[batdn_dir[i]]->misc&DIR_CPTOTMP) {
						lncntr=0;
						unpadfname(batdn_name[i],tmp);
						sprintf(tmp2,"%s%s",temp_dir,tmp);
						if(!fexist(tmp2)) {
							bprintf(text[RetrievingFile],tmp);
							sprintf(str,"%s%s",dir[batdn_dir[i]]->path,tmp);
							mv(str,tmp2,1); /* copy the file to temp dir */
							CRLF; } }
				sprintf(str,"%sBATCHDN.LST",node_dir);
                getnodedat(node_num,&thisnode,1);
				action=NODE_DLNG;
				if(cur_cps)
					unixtodos(now+(totalsize/(ulong)cur_cps)
						,&date,&curtime);
                thisnode.aux=(curtime.ti_hour*60)+curtime.ti_min;
                putnodedat(node_num,thisnode); /* calculate ETA */
				start=time(NULL);
				protocol(cmdstr(prot[xfrprot]->batdlcmd,str,nulstr));
				end=time(NULL);
				batch_download(xfrprot);
				if(batdn_total)
					notdownloaded(totalsize,start,end);
				/* delfiles(temp_dir,"*.*"); fix for CD-ROM */
				autohangup(); }
			break;
		case 'L':
			if(batup_total) {
				bputs(text[UploadQueueLstHdr]);
				for(i=0;i<batup_total;i++)
					bprintf(text[UploadQueueLstFmt],i+1,batup_name[i]
						,batup_desc[i]); }
			if(batdn_total) {
				bputs(text[DownloadQueueLstHdr]);
				for(i=0,totalcdt=0,totalsize=0;i<batdn_total;i++) {
					bprintf(text[DownloadQueueLstFmt],i+1
						,batdn_name[i],ultoac(batdn_cdt[i],tmp)
						,ultoac(batdn_size[i],str)
						,cur_cps
						? sectostr(batdn_size[i]/(ulong)cur_cps,tmp2)
						: "??:??:??");
					totalsize+=batdn_size[i];
					totalcdt+=batdn_cdt[i]; }
				bprintf(text[DownloadQueueTotals]
					,ultoac(totalcdt,tmp),ultoac(totalsize,str),cur_cps
					? sectostr(totalsize/(ulong)cur_cps,tmp2)
					: "??:??:??"); }
			break;	/* Questionable line ^^^, see note above function */
		case 'R':
			if(batup_total) {
				bprintf(text[RemoveWhichFromUlQueue],batup_total);
				n=getnum(batup_total);
				if(n>=1) {
					n--;
					if(batup_misc[n]&FM_EXTDESC) {	/* remove ext desc */
						sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir
							,dir[batup_dir[n]]->code
							,unpadfname(batup_name[n],tmp));
						remove(str); }
					batup_total--;
					while(n<batup_total) {
						batup_dir[n]=batup_dir[n+1];
						batup_misc[n]=batup_misc[n+1];
						strcpy(batup_name[n],batup_name[n+1]);
						strcpy(batup_desc[n],batup_desc[n+1]);
						n++; }
					if(!batup_total)
						bputs(text[UploadQueueCleared]); } }
			 if(batdn_total) {
				bprintf(text[RemoveWhichFromDlQueue],batdn_total);
				n=getnum(batdn_total);
				if(n>=1) {
					n--;
					f.dir=batdn_dir[n];
					strcpy(f.name,batdn_name[n]);
					f.datoffset=batdn_offset[n];
					f.size=batdn_size[n];
					closefile(f);
					batdn_total--;
					while(n<batdn_total) {
						strcpy(batdn_name[n],batdn_name[n+1]);
						batdn_dir[n]=batdn_dir[n+1];
						batdn_cdt[n]=batdn_cdt[n+1];
						batdn_size[n]=batdn_size[n+1];
						batdn_offset[n]=batdn_offset[n+1];
						n++; }
					if(!batdn_total)
						bputs(text[DownloadQueueCleared]); } }
			break;
	   case 'U':
			if(!batup_total) {
				bputs(text[UploadQueueIsEmpty]);
				break; }
			menu("BATUPROT");
			if(!create_batchup_lst())
				break;
			if(!create_bimodem_pth())
				break;
			SYNC;
			mnemonics(text[ProtocolOrQuit]);
			strcpy(str,"Q");
			for(i=0;i<total_prots;i++)
				if(prot[i]->batulcmd[0]) {
					sprintf(tmp,"%c",prot[i]->mnemonic);
					strcat(str,tmp); }
			ch=getkeys(str,0);
			if(ch=='Q')
				break;
			for(i=0;i<total_prots;i++)
				if(prot[i]->mnemonic==ch)
					break;
			if(i<total_prots) {
				sprintf(str,"%sBATCHUP.LST",node_dir);
				delfiles(temp_dir,"*.*");
				xfrprot=i;
            	action=NODE_ULNG;
				SYNC;
				start=time(NULL);
				if(temp_dir[0]=='\\' || strncmp(temp_dir,"..",2)
					|| temp_dir[1]==':')  {                 /* drive */
					if(temp_dir[1]==':')                    /* or prev dir */
						setdisk(toupper(temp_dir[0])-'A');
					strcpy(tmp,temp_dir);
					tmp[strlen(tmp)-1]=0;	/* take off '\' */
					if(chdir(tmp))
						errormsg(WHERE,ERR_CHDIR,tmp,0); }
				protocol(cmdstr(prot[xfrprot]->batulcmd,str,nulstr));
				end=time(NULL);
				starttime+=end-start;
				batch_upload();
				delfiles(temp_dir,"*.*");
				autohangup(); }
			break; } }
delfiles(temp_dir,"*.*");
}

/*****************************************************************************/
/* Temp directory section. Files must be extracted here and both temp_uler   */
/* and temp_uler fields should be filled before entrance.                    */
/*****************************************************************************/
void temp_xfer()
{
	char str[256],tmp2[256],done=0,ch;
	uint i,dirnum=total_dirs,j,files;
	ulong bytes;
	time_t start,end;
	struct ffblk ff;
	struct dfree d;
	file_t f;

if(useron.rest&FLAG('D')) {
	bputs(text[R_Download]);
	return; }
CRLF;
/*************************************/
/* Create TEMP directory information */
/*************************************/
if((dir[dirnum]=(dir_t *)farmalloc(sizeof(dir_t)))==0) {
	errormsg(WHERE,ERR_ALLOC,"temp_dir",sizeof(dir_t));
	return; }
dir[dirnum]->lib=0;
strcpy(dir[dirnum]->lname,"Temporary");
strcpy(dir[dirnum]->sname,"Temp");
strcpy(dir[dirnum]->code,"TEMP");
dir[dirnum]->level=dir[dirnum]->ullevel=dir[dirnum]->age=dir[dirnum]->flags
	=dir[dirnum]->ulflags=0L;
strcpy(dir[dirnum]->path,temp_dir);
dir[dirnum]->maxfiles=0;
dir[dirnum]->exts[0]=0;
strcpy(dir[dirnum]->defext,dir[0]->defext); /* use default ext from dir #1 */
dir[dirnum]->misc=0;
total_dirs++;

/****************************/
/* Fill filedat information */
/****************************/
sprintf(f.name,"TEMP_%3.3d.%s",node_num,temp_ext);
strcpy(f.desc,"Temp File");
f.dir=dirnum;
f.misc=f.timesdled=f.dateuled=f.datedled=0L;

if(useron.misc&RIP && !(useron.misc&EXPERT))
	menu("TEMPXFER");
lncntr=0;
while(online && !done) {
	if(!(useron.misc&(EXPERT|RIP))) {
		sys_status&=~SS_ABORT;
		if(lncntr) {
			SYNC;
			CRLF;
			if(lncntr)			/* CRLF or SYNC can cause pause */
                pause(); }
		menu("TEMPXFER"); }
	SYNC;
	bputs(text[TempDirPrompt]);
	strcpy(f.uler,temp_uler);
	ch=getkeys("ADEFNILQRVX?\r",0);
	if(ch>SP)
		logch(ch,0);
	switch(ch) {
		case 'A':	/* add to temp file */
			if(temp_dir[1]==':')
				i=temp_dir[0]-'A'+1;
			else i=0;
			getdfree(i,&d);
			if(d.df_sclus==0xffff)
				errormsg(WHERE,ERR_CHK,temp_dir,0);
			if((ulong)d.df_bsec*(ulong)d.df_sclus
				*(ulong)d.df_avail<(ulong)min_dspace*1024L) {
				bputs(text[LowDiskSpace]);
				sprintf(str,"Diskspace is low: %s",temp_dir);
				errorlog(str);
				if(!SYSOP)
					break; }
			bprintf(text[DiskNBytesFree],ultoac((ulong)d.df_bsec
				*(ulong)d.df_sclus*(ulong)d.df_avail,tmp));
			if(!getfilespec(tmp))
				break;
			unpadfname(tmp,str);
			if(!checkfname(str))
				break;
			sprintf(tmp2,"Added %s to %s",str,f.name);
			logline(nulstr,tmp2);
			sprintf(tmp2,"%s%s",temp_dir,str);
			sprintf(str,"%s%s",temp_dir,f.name);
			external(cmdstr(temp_cmd,str,tmp2),EX_CC|EX_OUTL|EX_OUTR);
			break;
		case 'D':	/* download from temp dir */
			sprintf(str,"%s%s",temp_dir,f.name);
			if(!fexist(str)) {
				bprintf(text[TempFileNotCreatedYet],f.name);
				break; }
			f.size=f.cdt=flength(str);
			f.opencount=0;
			if(temp_cdt)	/* if file was not free */
				f.cdt=cdt_byte_value ? f.size/cdt_byte_value : 0;
			else
				f.cdt=0;
			if(!(useron.exempt&FLAG('D')) && f.cdt>useron.cdt) {
				bprintf(text[YouOnlyHaveNCredits],ultoac(useron.cdt,tmp));
				break; }	/* f.cdt must equal size here */
			if(cur_cps && f.size/(ulong)cur_cps>timeleft) {
				bputs(text[NotEnoughTimeToDl]);
				break; }
			if(dir[usrdir[curlib][curdir[curlib]]]->dllevel>useron.tl
				|| !FLAGCMP(useron.tf
				,dir[usrdir[curlib][curdir[curlib]]]->dlflags)) {
				bputs(text[CantDownloadFromDir]);
				break; }
			addfiledat(&f);
			menu("DLPROT");
			SYNC;
			mnemonics(text[ProtocolOrQuit]);
			strcpy(tmp2,"Q");
			for(i=0;i<total_prots;i++)
				if(prot[i]->dlcmd[0]) {
					sprintf(tmp,"%c",prot[i]->mnemonic);
					strcat(tmp2,tmp); }
			ch=getkeys(tmp2,0);
			for(i=0;i<total_prots;i++)
				if(prot[i]->mnemonic==ch)
					break;
			if(i<total_prots) {
                getnodedat(node_num,&thisnode,1);
				action=NODE_DLNG;
				if(cur_cps)
					unixtodos(now+(f.size/(ulong)cur_cps)
						,&date,&curtime);
                thisnode.aux=(curtime.ti_hour*60)+curtime.ti_min;
                putnodedat(node_num,thisnode); /* calculate ETA */
				start=time(NULL);
				j=protocol(cmdstr(prot[i]->dlcmd,str,nulstr));
				end=time(NULL);
				if(prot[i]->misc&PROT_DSZLOG) {
					if(checkprotlog(f))
						downloadfile(f);
					else
						notdownloaded(f.size,start,end); }
				else {
					if(!j)
						downloadfile(f);
					else
						notdownloaded(f.size,start,end); }
				autohangup(); }
			removefiledat(f);
			break;
		case 'E':
			extract(usrdir[curlib][curdir[curlib]]);
			break;
		case 'F':   /* Create a file list */
			delfiles(temp_dir,"*.*");
			create_filelist("FILELIST.TXT",0);
			logline(nulstr,"Created list of all files");
			CRLF;
			break;
		case 'I':	/* information on what's here */
			bprintf(text[TempFileInfo],f.uler,temp_file);
			break;
		case 'L':	/* list files in dir */
			if(!getfilespec(str))
				break;
			bytes=files=0L;
			CRLF;
			sprintf(tmp2,"%s%s",temp_dir,str);
			i=findfirst(tmp2,&ff,0);
			while(!i && !msgabort()) {
				bprintf("%s %10s\r\n",padfname(ff.ff_name,str)
					,ultoac(ff.ff_fsize,tmp));
				files++;
				bytes+=ff.ff_fsize;
				i=findnext(&ff); }
			if(!files)
				bputs(text[EmptyDir]);
			else if(files>1)
				bprintf(text[TempDirTotal],ultoac(bytes,tmp),files);
			break;
		case 'N':   /* Create a list of new files */
			delfiles(temp_dir,"*.*");
			create_filelist("NEWFILES.TXT",FL_ULTIME);
			logline(nulstr,"Created list of new files");
			CRLF;
			break;
		case 'R':	/* Remove files from dir */
			if(!getfilespec(str))
				break;
			bprintf(text[NFilesRemoved],delfiles(temp_dir,str));
			break;
		case 'V':	/* view files in dir */
			bputs(text[FileSpec]);
			if(!getstr(str,12,K_UPPER) || !checkfname(str))
				break;
			if(!strchr(str,'.')) {	/* default extension */
				sprintf(tmp,".%s",dir[dirnum]->defext);
				strcat(str,tmp); }
			viewfiles(dirnum,str);
			break;
		case CR:
		case 'Q':	/* quit */
			done=1;
			break;
		case 'X':   /* extract from archive in temp dir */
            extract(dirnum);
            break;
		case '?':	/* menu */
			if(useron.misc&(EXPERT|RIP))
				menu("TEMPXFER");
			break; }
	if(sys_status&SS_ABORT)
		break; }
farfree(dir[dirnum]);
total_dirs--;
}

/****************************************************************************/
/* This function is called when a file is unsuccessfully downloaded.		*/
/* It logs the tranfer time and checks for possible leech protocol use. 	*/
/****************************************************************************/
void notdownloaded(ulong size, time_t start, time_t end)
{
	char str[256],tmp2[256];

sprintf(str,"Estimated Time: %s  Transfer Time: %s"
	,sectostr(cur_cps ? size/cur_cps : 0,tmp)
	,sectostr((uint)end-start,tmp2));
logline(nulstr,str);
if(leech_pct && cur_cps 				/* leech detection */
	&& end-start>=leech_sec
	&& end-start>=(double)(size/cur_cps)*(double)leech_pct/100.0) {
	errorlog("Possible use of leech protocol");
	useron.leech=adjustuserrec(useron.number,U_LEECH,2,1); }
}

/****************************************************************************/
/* Prints one file's information on a single line to a file 'file'          */
/****************************************************************************/
void listfiletofile(char *fname, char huge *buf, uint dirnum, int file)
{
    char str[256],exist=1;
    uchar alt;
    ulong cdt;

strcpy(str,fname);
if(buf[F_MISC]!=ETX && (buf[F_MISC]-SP)&FM_EXTDESC)
    strcat(str,"+");
else
    strcat(str," ");
write(file,str,13);
getrec((char *)buf,F_ALTPATH,2,str);
alt=(uchar)ahtoul(str);
sprintf(str,"%s%s",alt>0 && alt<=altpaths ? altpath[alt-1]
    : dir[dirnum]->path,unpadfname(fname,tmp));
if(dir[dirnum]->misc&DIR_FCHK && !fexist(str))
    exist=0;
getrec((char *)buf,F_CDT,7,str);
cdt=atol(str);
if(!cdt)
    strcpy(str,"   FREE");
else
    sprintf(str,"%7lu",cdt);
if(exist)
    strcat(str," ");
else
    strcat(str,"-");
write(file,str,8);
getrec((char *)buf,F_DESC,LEN_FDESC,str);
write(file,str,strlen(str));
write(file,crlf,2);
}

/****************************************************************************/
/* Uploads files                                                            */
/****************************************************************************/
void upload(uint dirnum)
{
	static char lastdesc[59];
    char str[256],descbeg[6]={""},descend[9]={""},path[256]
        ,fname[13],keys[256],ch,*p;
    time_t start,end;
    int i,j,file,destuser[MAX_USERXFER],destusers=0;
    file_t f;
    struct dfree d;
    user_t user;
    node_t node;

if(sys_status&SS_EVENT && online==ON_REMOTE && !SYSOP)
    bprintf(text[UploadBeforeEvent],timeleft/60);
if(altul)
    strcpy(path,altpath[altul-1]);
else
    strcpy(path,dir[dirnum]->path);
if(path[1]==':')
    i=path[0]-'A'+1;
else i=0;
getdfree(i,&d);
if(d.df_sclus==0xffff)
    errormsg(WHERE,ERR_CHK,path,0);
if((ulong)d.df_bsec*(ulong)d.df_sclus
    *(ulong)d.df_avail<(ulong)min_dspace*1024L) {
    bputs(text[LowDiskSpace]);
    sprintf(str,"Diskspace is low: %s",path);
    errorlog(str);
    if(!SYSOP)
        return; }
else {
    CRLF; }
bprintf(text[DiskNBytesFree],ultoac((ulong)d.df_bsec
    *(ulong)d.df_sclus*(ulong)d.df_avail,tmp));
f.dir=dirnum;
f.misc=0;
f.altpath=altul;
bputs(text[Filename]);
if(!getstr(fname,12,K_UPPER) || strchr(fname,'?') || strchr(fname,'*')
	|| !checkfname(fname) || trashcan(fname,"FILE"))
    return;
for(i=0;i<strlen(fname);i++)
    if(fname[i]<32 || fname[i]>126) return;
if(dirnum==sysop_dir)
    sprintf(str,text[UploadToSysopDirQ],fname);
else if(dirnum==user_dir)
    sprintf(str,text[UploadToUserDirQ],fname);
else
    sprintf(str,text[UploadToCurDirQ],fname,lib[dir[dirnum]->lib]->sname
        ,dir[dirnum]->sname);
if(!yesno(str)) return;
action=NODE_ULNG;
padfname(fname,f.name);
sprintf(str,"%s%s",path,fname);
if(fexist(str)) {   /* File is on disk */
    if(!SYSOP) {
        bprintf(text[FileAlreadyThere],fname);
        return; }
    if(!yesno(text[FileOnDiskAddQ]))
        return; }
else if(online==ON_LOCAL) {
    bputs(text[FileNotOnDisk]);
    return; }
strcpy(str,dir[dirnum]->exts);
strcpy(tmp,f.name);
truncsp(tmp);
j=strlen(str);
ch=0;
for(i=0;i<j;i+=ch+1) { /* Check extension of upload with allowable exts */
    p=strchr(str+i,',');
    if(p!=NULL)
        *p=NULL;
    ch=strlen(str+i);
    if(!strcmp(tmp+9,str+i))
        break; }
if(j && i>=j) {
    bputs(text[TheseFileExtsOnly]);
    bputs(dir[dirnum]->exts);
    CRLF;
    if(!SYSOP) return; }
for(i=0;i<usrlibs;i++)
    for(j=0;j<usrdirs[i];j++)
        if(findfile(usrdir[i][j],f.name)) {
            bputs(text[FileAlreadyOnline]);
            if(!SYSOP) return;      /* File is in database for another dir */
            if(usrdir[i][j]==dirnum) return; } /* don't allow duplicates */
if(dirnum==user_dir) {  /* User to User transfer */
    bputs(text[EnterAfterLastDestUser]);
    while((!SYSOP && destusers<max_userxfer) || destusers<MAX_USERXFER) {
        bputs(text[SendFileToUser]);
        if(!getstr(str,LEN_ALIAS,K_UPPER))
            break;
        if((user.number=finduser(str))!=0) {
            if(!SYSOP && user.number==useron.number) {
                bputs(text[CantSendYourselfFiles]);
                continue; }
            for(i=0;i<destusers;i++)
                if(user.number==destuser[i])
                    break;
            if(i<destusers) {
                bputs(text[DuplicateUser]);
                continue; }
            getuserdat(&user);
            if((user.rest&(FLAG('T')|FLAG('D')))
                || user.tl<lib[dir[user_dir]->lib]->level
                || !FLAGCMP(user.tf,dir[user_dir]->flags)) {
                bprintf(text[UserWontBeAbleToDl],user.alias); }
            else {
                bprintf(text[UserAddedToDestList],user.alias);
                destuser[destusers++]=user.number; } }
        else {
            CRLF; } }
    if(!destusers)
        return; }
if(dir[dirnum]->misc&DIR_RATE) {
    SYNC;
    bputs(text[RateThisFile]);
    switch(getkeys("GRXQ",0)) {
        case 'Q':
            return;
        case 'G':
            strcpy(descbeg,text[GRated]);
            break;
        case 'R':
            strcpy(descbeg,text[RRated]);
            break;
        case 'X':
            strcpy(descbeg,text[XRated]);
            break; } }
if(dir[dirnum]->misc&DIR_MULT) {
    SYNC;
    if(!noyes(text[MultipleDiskQ])) {
        bputs(text[HowManyDisksTotal]);
        if((i=getnum(99))<2)
            return;
        bputs(text[NumberOfFile]);
        if((j=getnum(i))<1)
            return;
		if(j==1)
			lastdesc[0]=0;
        if(i>9)
            sprintf(descend,text[FileOneOfTen],j,i);
        else
			sprintf(descend,text[FileOneOfTwo],j,i); }
	else
		lastdesc[0]=0; }
else
	lastdesc[0]=0;
bputs(text[EnterDescNow]);
i=LEN_FDESC-(strlen(descbeg)+strlen(descend));
if(!getstr(lastdesc,i,K_LINE|K_EDIT))
    return;
if(descend[0])      /* end of desc specified, so pad desc with spaces */
	sprintf(f.desc,"%s%-*s%s",descbeg,i,lastdesc,descend);
else                /* no end specified, so string ends at desc end */
	sprintf(f.desc,"%s%s",descbeg,lastdesc);
if(dir[dirnum]->misc&DIR_EXTDESC && !noyes(text[CreateExtDescriptionQ])) {
    sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir,dir[dirnum]->code,fname);
    if(fexist(str)) {
        bprintf(text[FileAlreadyThere],str);
        return; }
	if(!writemsg(str,nulstr,nulstr,WM_EXTDESC,0,nulstr))
        return;
    f.misc|=FM_EXTDESC; }
if(dir[dirnum]->misc&DIR_ANON && !(dir[dirnum]->misc&DIR_AONLY)
	&& (SYSOP || useron.exempt&FLAG('A'))) {
	if(!noyes(text[AnonymousQ]))
		f.misc|=FM_ANON; }
sprintf(str,"%s%s",path,fname);
if(fexist(str)) {   /* File is on disk */
    if(!uploadfile(f))
        return; }
else {
    menu("ULPROT");
    SYNC;
    strcpy(keys,"Q");
    if(dirnum==user_dir || !max_batup)  /* no batch user to user xfers */
        mnemonics(text[ProtocolOrQuit]);
    else {
        mnemonics(text[ProtocolBatchOrQuit]);
        strcat(keys,"B"); }
    for(i=0;i<total_prots;i++)
        if(prot[i]->ulcmd[0]) {
            sprintf(tmp,"%c",prot[i]->mnemonic);
            strcat(keys,tmp); }
    ch=getkeys(keys,0);
	if(ch=='Q') {
		if(f.misc&FM_EXTDESC) {  /* delete extended description */
			sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir,dir[dirnum]->code,fname);
			remove(str); }
		return; }
    if(ch=='B') {
        if(batup_total>=max_batup)
            bputs(text[BatchUlQueueIsFull]);
        else {
            for(i=0;i<batup_total;i++)
                if(!strcmp(batup_name[i],f.name)) {
                    bputs(text[FileAlreadyInQueue]);
                    return; }
            strcpy(batup_name[batup_total],f.name);
            strcpy(batup_desc[batup_total],f.desc);
            batup_dir[batup_total]=dirnum;
            batup_misc[batup_total]=f.misc;
            batup_alt[batup_total]=altul;
            batup_total++;
            bprintf(text[FileAddedToUlQueue]
                ,f.name,batup_total,max_batup); } }
    else {
        for(i=0;i<total_prots;i++)
            if(prot[i]->mnemonic==ch)
                break;
        if(i<total_prots) {
            start=time(NULL);
            protocol(cmdstr(prot[i]->ulcmd,str,nulstr));
            end=time(NULL);
			starttime+=end-start;
            ch=uploadfile(f);
            autohangup();
            if(!ch) { /* upload failed, don't process user to user xfer */
				if(f.misc&FM_EXTDESC) {  /* delete extended description */
					sprintf(str,"%sDIRS\\%s.EXT\\%s",data_dir,dir[dirnum]->code
                        ,fname);
                    remove(str); }
                return; } } } }
if(dirnum==user_dir) {  /* Add files to XFER.IXT in INDX dir */
    sprintf(str,"%sXFER.IXT",indx_dir);
    if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) {
        errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_APPEND);
        return; }
    for(j=0;j<destusers;j++) {
        for(i=1;i<=sys_nodes;i++) { /* Tell user, if online */
            getnodedat(i,&node,0);
            if(node.useron==destuser[j] && !(node.misc&NODE_POFF)
                && (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
                sprintf(str,text[UserToUserXferNodeMsg],node_num,useron.alias);
                putsmsg(destuser[j],str);
                break; } }
        sprintf(str,"%4.4u %12.12s %4.4u\r\n"
            ,destuser[j],f.name,useron.number);
        write(file,str,strlen(str)); }
    close(file); }
}

/****************************************************************************/
/* Checks directory for 'dir' and prompts user to enter description for     */
/* the files that aren't in the database.                                   */
/* Returns 1 if the user aborted, 0 if not.                                 */
/****************************************************************************/
char bulkupload(uint dirnum)
{
    char done,str[256];
    struct ffblk ff;
    file_t f;

memset(&f,NULL,sizeof(file_t));
f.dir=dirnum;
f.altpath=altul;
bprintf(text[BulkUpload],lib[dir[dirnum]->lib]->sname,dir[dirnum]->sname);
sprintf(str,"%s*.*",altul>0 && altul<=altpaths ? altpath[altul-1]
    : dir[dirnum]->path);
done=findfirst(str,&ff,0);
action=NODE_ULNG;
SYNC;
while(!done && !msgabort()) {
    if(gettotalfiles(dirnum)>dir[dirnum]->maxfiles) {
        bputs(text[DirFull]);
        return(0); }
    padfname(ff.ff_name,str);
    if(findfile(f.dir,str)==0) {
        strcpy(f.name,str);
		f.cdt=cdt_byte_value ? ff.ff_fsize/cdt_byte_value : 0;
        bprintf(text[BulkUploadDescPrompt],f.name,f.cdt);
        if(!getstr(f.desc,LEN_FDESC,K_LINE)) return(1);
        uploadfile(f); }    /* used to abort here if the file failed upload */
    done=findnext(&ff); }
return(0);
}

/****************************************************************************/
/* Handles start and stop routines for transfer protocols                   */
/****************************************************************************/
int protocol(char *cmdline)
{
    int i;

autohang=yesno(text[HangUpAfterXferQ]);
sprintf(tmp,"%sPROTOCOL.LOG",node_dir);
remove(tmp);                            /* Deletes the protocol log */
bputs(text[StartXferNow]);
riosync();
lprintf("%s\r\n",cmdline);
i=external(cmdline,EX_OUTL);	/* EX_CC removed because of error level prob */
if(online==ON_REMOTE)
    rioctl(IOFB);
CRLF;
if(autohang) sys_status|=SS_PAUSEOFF;	/* Pause off after download */
return(i);
}

/****************************************************************************/
/* Invokes the timed auto-hangup after transfer routine                     */
/****************************************************************************/
void autohangup()
{
    char a,c,k;

SYNC;
sys_status&=~SS_PAUSEOFF;		/* turn pause back on */
rioctl(IOFI);
if(!autohang) return;
lncntr=0;
bputs(text[Disconnecting]);
attr(GREEN);
outchar('[');
for(c=9,a=0;c>-1 && online && !a;c--) {
    checkline();
    attr(LIGHTGRAY|HIGH);
    bputs(itoa(c,tmp,10));
    attr(GREEN);
    outchar(']');
	while((k=inkey(0))!=0 && online) {
        if(toupper(k)=='H') {
            c=0;
            break; }
        if(toupper(k)=='A') {
            a=1;
            break; } }
	mswait(DELAY_AUTOHG);
    outchar(BS);
    outchar(BS); }
if(c==-1) {
    bputs(text[Disconnected]);
    hangup(); }
else
    CRLF;
}

/****************************************************************************/
/* Checks dsz compatible log file for errors in transfer                    */
/* Returns 1 if the file in the struct file_t was successfuly transfered    */
/****************************************************************************/
char checkprotlog(file_t f)
{
    char str[256];
    int file;
    FILE *stream;

sprintf(str,"%sPROTOCOL.LOG",node_dir);
if((file=nopen(str,O_RDONLY))==-1) {
    bprintf(text[FileNotSent],f.name);
    if(f.dir<total_dirs)
        sprintf(str,"Attempted to download %s from %s %s",f.name
            ,lib[dir[f.dir]->lib]->sname,dir[f.dir]->sname);
    else
        strcpy(str,"Attempted to download QWK packet");
    logline("D!",str);
    return(0); }
if((stream=fdopen(file,"rb"))==0) {
    close(file);
    errormsg(WHERE,ERR_FDOPEN,str,0);
    return(1); }
unpadfname(f.name,tmp);
if(tmp[strlen(tmp)-1]=='.')     /* DSZ log uses FILE instead of FILE. */
    tmp[strlen(tmp)-1]=0;       /* when there isn't an extension.     */
while(!ferror(stream)) {
    if(!fgets(str,256,stream))
        break;
    if(str[strlen(str)-2]==CR)
        str[strlen(str)-2]=0;       /* chop off CRLF */
    strupr(str);
    if(strstr(str,tmp)) {   /* Only check for name, Bimodem doesn't put path */
        logline(nulstr,str);
		if(str[0]=='E' || str[0]=='L' || (str[6]==SP && str[7]=='0'))
            break;          /* E for Error or L for Lost Carrier */
        fclose(stream);     /* or only sent 0 bytes! */
        return(1); } }
fclose(stream);
bprintf(text[FileNotSent],f.name);
if(f.dir<total_dirs)
    sprintf(str,"Attempted to download %s (%s) from %s %s",f.name
        ,ultoac(f.size,tmp),lib[dir[f.dir]->lib]->sname,dir[f.dir]->sname);
else
    strcpy(str,"Attempted to download QWK packet");
logline("D!",str);
return(0);
}

/*****************************************************************************/
/* View viewable file types from dir 'dirnum'                                */
/* 'fspec' should NOT be padded                                              */
/*****************************************************************************/
void viewfiles(uint dirnum, char *fspec)
{
    char viewcmd[256];
    int i;

sprintf(viewcmd,"%s%s",dir[dirnum]->path,fspec);
if(!fexist(viewcmd)) {
    bputs(text[FileNotFound]);
    return; }
padfname(fspec,tmp);
for(i=0;i<total_fviews;i++)
    if(!strcmp(tmp+9,fview[i]->ext)) {
        strcpy(viewcmd,fview[i]->cmd);
        break; }
if(i==total_fviews) {
    bputs(text[NonviewableFile]);
    return; }
sprintf(tmp,"%s%s",dir[dirnum]->path,fspec);
if((i=external(cmdstr(viewcmd,nulstr,tmp),EX_OUTL|EX_OUTR|EX_INR|EX_CC))!=0)
	errormsg(WHERE,ERR_EXEC,viewcmd,i);    /* must of EX_CC to ^C */
}

/****************************************************************************/
/* Compares filenames for ascending name sort								*/
/****************************************************************************/
int fnamecmp_a(char **str1, char **str2)
{
return(strncmp(*str1,*str2,11));
}

/****************************************************************************/
/* Compares filenames for descending name sort								*/
/****************************************************************************/
int fnamecmp_d(char **str1, char **str2)
{
return(strncmp(*str2,*str1,11));
}

/****************************************************************************/
/* Compares file upload dates for ascending date sort						*/
/****************************************************************************/
int fdatecmp_a(uchar **buf1, uchar **buf2)
{
	time_t date1,date2;

date1=((*buf1)[14]|((long)(*buf1)[15]<<8)|((long)(*buf1)[16]<<16)
	|((long)(*buf1)[17]<<24));
date2=((*buf2)[14]|((long)(*buf2)[15]<<8)|((long)(*buf2)[16]<<16)
	|((long)(*buf2)[17]<<24));
if(date1>date2)	return(1);
if(date1<date2)	return(-1);
return(0);
}

/****************************************************************************/
/* Compares file upload dates for descending date sort						*/
/****************************************************************************/
int fdatecmp_d(uchar **buf1, uchar **buf2)
{
	time_t date1,date2;

date1=((*buf1)[14]|((long)(*buf1)[15]<<8)|((long)(*buf1)[16]<<16)
	|((long)(*buf1)[17]<<24));
date2=((*buf2)[14]|((long)(*buf2)[15]<<8)|((long)(*buf2)[16]<<16)
	|((long)(*buf2)[17]<<24));
if(date1>date2)	return(-1);
if(date1<date2)	return(1);
return(0);
}

/****************************************************************************/
/* Re-sorts file directory 'dirnum' according to dir[dirnum]->sort type 	*/
/****************************************************************************/
void resort(uint dirnum)
{
	char str[256],ixbfname[256],datfname[256];
	uchar huge *ixbbuf, huge *datbuf;
	uchar huge *ixbptr[MAX_FILES];
	int ixbfile,datfile,i;
	ulong ixblen,datlen,offset,l;

bprintf(text[ResortLineFmt],lib[dir[dirnum]->lib]->sname,dir[dirnum]->sname);
sprintf(ixbfname,"%sDIRS\\%s.IXB",indx_dir,dir[dirnum]->code);
sprintf(datfname,"%sDIRS\\%s.DAT",data_dir,dir[dirnum]->code);
if(flength(ixbfname)<1L || flength(datfname)<1L) {
	remove(ixbfname);
	remove(datfname);
	bputs(text[ResortEmptyDir]);
	return; }
bputs(text[Sorting]);
if((ixbfile=nopen(ixbfname,O_RDONLY))==-1) {
	errormsg(WHERE,ERR_OPEN,ixbfname,O_RDONLY);
	return; }
if((datfile=nopen(datfname,O_RDONLY))==-1) {
	close(ixbfile);
	errormsg(WHERE,ERR_OPEN,datfname,O_RDONLY);
	return; }
ixblen=filelength(ixbfile);
datlen=filelength(datfile);
if((ixbbuf=farmalloc(ixblen))==NULL) {
	close(ixbfile);
	close(datfile);
	errormsg(WHERE,ERR_ALLOC,ixbfname,ixblen);
	return; }
if((datbuf=farmalloc(datlen))==NULL) {
	close(ixbfile);
	close(datfile);
	farfree((char *)ixbbuf);
	errormsg(WHERE,ERR_ALLOC,datfname,datlen);
	return; }
if(lread(ixbfile,ixbbuf,ixblen)!=ixblen) {
	close(ixbfile);
	close(datfile);
	farfree((char *)ixbbuf);
	farfree((char *)datbuf);
	errormsg(WHERE,ERR_READ,ixbfname,ixblen);
	return; }
if(lread(datfile,datbuf,datlen)!=datlen) {
	close(ixbfile);
	close(datfile);
	farfree((char *)ixbbuf);
	farfree((char *)datbuf);
	errormsg(WHERE,ERR_READ,datfname,datlen);
	return; }
close(ixbfile);
close(datfile);
if((ixbfile=nopen(ixbfname,O_WRONLY|O_TRUNC))==-1) {
	farfree((char *)ixbbuf);
	farfree((char *)datbuf);
	errormsg(WHERE,ERR_OPEN,ixbfname,O_WRONLY|O_TRUNC);
	return; }
if((datfile=nopen(datfname,O_WRONLY|O_TRUNC))==-1) {
	close(ixbfile);
	farfree((char *)ixbbuf);
	farfree((char *)datbuf);
	errormsg(WHERE,ERR_OPEN,datfname,O_WRONLY|O_TRUNC);
	return; }
for(l=0,i=0;l<ixblen && i<MAX_FILES;l+=F_IXBSIZE,i++)
	ixbptr[i]=ixbbuf+l;
switch(dir[dirnum]->sort) {
	case SORT_NAME_A:
		qsort((void *)ixbptr,ixblen/F_IXBSIZE,sizeof(ixbptr[0])
			,(int(*)(const void*, const void*))fnamecmp_a);
		break;
	case SORT_NAME_D:
		qsort((void *)ixbptr,ixblen/F_IXBSIZE,sizeof(ixbptr[0])
			,(int(*)(const void*, const void*))fnamecmp_d);
		break;
	case SORT_DATE_A:
		qsort((void *)ixbptr,ixblen/F_IXBSIZE,sizeof(ixbptr[0])
			,(int(*)(const void*, const void*))fdatecmp_a);
		break;
	case SORT_DATE_D:
		qsort((void *)ixbptr,ixblen/F_IXBSIZE,sizeof(ixbptr[0])
			,(int(*)(const void*, const void*))fdatecmp_d);
		break; }
for(i=0;i<ixblen/F_IXBSIZE;i++) {
	offset=ixbptr[i][11]|((long)ixbptr[i][12]<<8)|((long)ixbptr[i][13]<<16);
	lwrite(datfile,&datbuf[offset],F_LEN);
	offset=(ulong)i*(ulong)F_LEN;
	str[0]=offset&0xff;		/* Get offset within DAT file for IXB file */
	str[1]=(offset>>8)&0xff;
	str[2]=(offset>>16)&0xff;
	lwrite(ixbfile,ixbptr[i],11);		/* filename */
	lwrite(ixbfile,str,3);				/* offset */
	lwrite(ixbfile,ixbptr[i]+14,8);	} 	/* upload and download times */
close(ixbfile);
close(datfile);
farfree((char *)ixbbuf);
farfree((char *)datbuf);
if(ixblen/F_IXBSIZE==datlen/F_LEN)
	bputs(text[Sorted]);
else
	bprintf(text[Compressed]
		,(uint)((datlen/F_LEN)-(ixblen/F_IXBSIZE))
		,ultoac(((datlen/F_LEN)-(ixblen/F_IXBSIZE))*F_LEN,tmp));
}

/*****************************************************************************/
/* Handles extraction from a normal transfer file to the temp directory      */
/*****************************************************************************/
void extract(uint dirnum)
{
	char fname[13],str[256],excmd[256],path[256],done
		,files[256],cmd[256],intmp=0;
    int i;
    struct ffblk ff;
    file_t f;
    struct dfree d;

if(!strcmp(dir[dirnum]->code,"TEMP"))
	intmp=1;
if(temp_dir[1]==':')
	i=temp_dir[0]-'A'+1;
else i=0;
getdfree(i,&d);
if(d.df_sclus==0xffff)
	errormsg(WHERE,ERR_CHK,temp_dir,0);
if((ulong)d.df_bsec*(ulong)d.df_sclus
	*(ulong)d.df_avail<(ulong)min_dspace*1024L) {
    bputs(text[LowDiskSpace]);
	sprintf(str,"Diskspace is low: %s",temp_dir);
    errorlog(str);
    if(!SYSOP)
        return; }
else if(!intmp) {	/* not in temp dir */
    CRLF; }
bprintf(text[DiskNBytesFree],ultoac((ulong)d.df_bsec
	*(ulong)d.df_sclus*(ulong)d.df_avail,tmp));
if(!intmp) {	/* not extracting FROM temp directory */
    sprintf(str,"%s*.*",temp_dir);
    if(fexist(str)) {
        bputs(text[RemovingTempFiles]);
        done=findfirst(str,&ff,0);
        while(!done) {
            sprintf(str,"%s%s",temp_dir,ff.ff_name);
            remove(str);
            done=findnext(&ff); }
        CRLF; } }
bputs(text[ExtractFrom]);
if(!getstr(fname,12,K_UPPER) || !checkfname(fname) || strchr(fname,'*')
    || strchr(fname,'?'))
    return;
if(!strchr(fname,'.')) {    /* default extension */
    sprintf(tmp,".%s",dir[dirnum]->defext);
    strcat(fname,tmp); }
padfname(fname,f.name);
for(i=0;i<total_fextrs;i++)
    if(!strcmp(f.name+9,fextr[i]->ext)) {
        strcpy(excmd,fextr[i]->cmd);
        break; }
if(i==total_fextrs) {
    bputs(text[UnextractableFile]);
    return; }
if(!intmp && !findfile(dirnum,f.name)) {	/* not temp dir */
    bputs(text[FileNotFound]);  /* not in database */
    return; }
sprintf(path,"%s%s",dir[dirnum]->path,fname);
if(!intmp) {	/* not temp dir, so get temp_file info */
    f.datoffset=f.dateuled=f.datedled=0L;
    f.dir=dirnum;
    getfileixb(&f);
    if(!f.datoffset && !f.dateuled && !f.datedled)  /* error reading ixb */
        return;
    getfiledat(&f);
    fileinfo(f);
	if(f.altpath>0 && f.altpath<=altpaths)
		sprintf(path,"%s%s",altpath[f.altpath-1],fname);
	temp_cdt=f.cdt;
    strcpy(temp_uler,f.uler);
    strcpy(temp_file,f.name); }     /* padded filename */
if(!fexist(path)) {
    bputs(text[FileNotThere]);  /* not on disk */
    return; }
done=0;
while(online && !done) {
    mnemonics(text[ExtractFilesPrompt]);
    switch(getkeys("EVQ",0)) {
        case 'E':
			if(!getfilespec(tmp))
                break;
            unpadfname(tmp,str);
			if(!checkfname(str))
				break;
			if((i=external(cmdstr(excmd,path,str)
				,EX_INR|EX_OUTL|EX_OUTR|EX_CC))!=0) {
				errormsg(WHERE,ERR_EXEC,cmd,i);
                return; }
            sprintf(cmd,"Extracted %s from %s",str,path);
			logline(nulstr,cmd);
            CRLF;
            break;
        case 'V':
            viewfiles(dirnum,fname);
            break;
        default:
            done=1;
            break; } }
}

/****************************************************************************/
/* Creates the file BATCHDN.LST in the node directory. Returns 1 if 		*/
/* everything goes okay. 0 if not.											*/
/****************************************************************************/
int create_batchdn_lst()
{
	char str[256];
	int i,file;

sprintf(str,"%sBATCHDN.LST",node_dir);
if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
	errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
	return(0); }
for(i=0;i<batdn_total;i++) {
	if(batdn_dir[i]>=total_dirs || dir[batdn_dir[i]]->misc&DIR_CPTOTMP)
		strcpy(str,temp_dir);
	else
		strcpy(str,batdn_alt[i]>0 && batdn_alt[i]<=altpaths
			? altpath[batdn_alt[i]-1] : dir[batdn_dir[i]]->path);
	write(file,str,strlen(str));
	unpadfname(batdn_name[i],str);
	strcat(str,crlf);
	write(file,str,strlen(str)); }
close(file);
return(1);
}

/****************************************************************************/
/* Creates the file BATCHUP.LST in the node directory. Returns 1 if 		*/
/* everything goes okay. 0 if not.											*/
/* This list is not used by any protocols to date.							*/
/****************************************************************************/
int create_batchup_lst()
{
	char str[256];
	int i,file;

sprintf(str,"%sBATCHUP.LST",node_dir);
if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
	errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
	return(0); }
for(i=0;i<batup_total;i++) {
	if(batup_dir[i]>=total_dirs)
		strcpy(str,temp_dir);
	else
		strcpy(str,batup_alt[i]>0 && batup_alt[i]<=altpaths
			? altpath[batup_alt[i]-1] : dir[batup_dir[i]]->path);
	write(file,str,strlen(str));
	unpadfname(batup_name[i],str);
	strcat(str,crlf);
	write(file,str,strlen(str)); }
close(file);
return(1);
}

/****************************************************************************/
/* Creates the file BIMODEM.PTH in the node directory. Returns 1 if 		*/
/* everything goes okay. 0 if not.											*/
/****************************************************************************/
int create_bimodem_pth()
{
	char str[256],tmp2[512];
	int i,file;

sprintf(str,"%sBIMODEM.PTH",node_dir);  /* Create bimodem file */
if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
	errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
	return(0); }
for(i=0;i<batup_total;i++) {
	sprintf(str,"%s%s",batup_dir[i]>=total_dirs ? temp_dir
		: batup_alt[i]>0 && batup_alt[i]<=altpaths
		? altpath[batup_alt[i]-1] : dir[batup_dir[i]]->path
		,unpadfname(batup_name[i],tmp));
	sprintf(tmp2,"D       %-80.80s%-160.160s"
		,unpadfname(batup_name[i],tmp),str);
	write(file,tmp2,248); }
for(i=0;i<batdn_total;i++) {
	sprintf(str,"%s%s"
		,(batdn_dir[i]>=total_dirs || dir[batdn_dir[i]]->misc&DIR_CPTOTMP)
		? temp_dir : batdn_alt[i]>0 && batdn_alt[i]<=altpaths
			? altpath[batdn_alt[i]-1] : dir[batdn_dir[i]]->path
			,unpadfname(batdn_name[i],tmp));
	sprintf(tmp2,"U       %-240.240s",str);
	write(file,tmp2,248); }
close(file);
return(1);
}

/****************************************************************************/
/* Processes files that were supposed to be received in the batch queue 	*/
/****************************************************************************/
void batch_upload()
{
	char str1[256],str2[256];
	int i,j;
	file_t f;

for(i=0;i<batup_total;) {
	lncntr=0;								/* defeat pause */
	unpadfname(batup_name[i],tmp);
	sprintf(str1,"%s%s",temp_dir,tmp);
	sprintf(str2,"%s%s",dir[batup_dir[i]]->path,tmp);
	if(fexist(str1) && fexist(str2)) { /* file's in two places */
		bprintf(text[FileAlreadyThere],batup_name[i]);
		remove(str1);	 /* this is the one received */
		i++;
		continue; }
	if(fexist(str1))
		mv(str1,str2,0);
	strcpy(f.name,batup_name[i]);
	strcpy(f.desc,batup_desc[i]);
	f.dir=batup_dir[i];
	f.misc=batup_misc[i];
	f.altpath=batup_alt[i];
	if(uploadfile(f)) {
		for(j=i;j<batup_total;j++) {
			batup_dir[j]=batup_dir[j+1];
			batup_misc[j]=batup_misc[j+1];
			strcpy(batup_name[j],batup_name[j+1]);
			strcpy(batup_desc[j],batup_desc[j+1]); }
		batup_total--; }
	else i++; }
}

/****************************************************************************/
/* Processes files that were supposed to be sent from the batch queue		*/
/****************************************************************************/
void batch_download(int xfrprot)
{
	int i,j;
	file_t f;

for(i=0;i<batdn_total;) {
	lncntr=0;								/* defeat pause */
	f.dir=batdn_dir[i];
	strcpy(f.name,batdn_name[i]);
	f.datoffset=batdn_offset[i];
	f.size=batdn_size[i];
	f.altpath=batdn_alt[i];
	if((prot[xfrprot]->misc&PROT_DSZLOG && checkprotlog(f))
		|| !(prot[xfrprot]->misc&PROT_DSZLOG)) {
		downloadfile(f);
		closefile(f);
		for(j=i;j<batdn_total;j++) {
			strcpy(batdn_name[j],batdn_name[j+1]);
			batdn_dir[j]=batdn_dir[j+1];
			batdn_cdt[j]=batdn_cdt[j+1];
			batdn_size[j]=batdn_size[j+1];
			batdn_offset[j]=batdn_offset[j+1]; }
		batdn_total--; }
	else i++; }
}

/****************************************************************************/
/* Creates a text file named NEWFILES.DAT in the temp directory that		*/
/* all new files since p-date. Returns number of files in list. 			*/
/****************************************************************************/
uint create_filelist(char *name, char mode)
{
	char str[256];
	int i,j,k,l,file;

bprintf(text[CreatingFileList],name);
sprintf(str,"%s%s",temp_dir,name);
if((file=nopen(str,O_CREAT|O_WRONLY))==-1) {
	errormsg(WHERE,ERR_OPEN,str,O_CREAT|O_WRONLY);
	return(0); }
k=0;
if(mode&FL_ULTIME) {
	sprintf(str,"New files since: %s\r\n",timestr(&ns_time));
	write(file,str,strlen(str)); }
for(i=0;i<usrlibs;i++) {
	for(j=0;j<usrdirs[i];j++) {
		if(mode&FL_ULTIME && lib[usrlib[i]]->offline_dir==usrdir[i][j])
			continue;
		l=listfiles(usrdir[i][j],nulstr,file,mode);
		if(l==-1)
			break;
		k+=l; }
	if(j<usrdirs[i])
		break; }
if(k>1) {
	sprintf(str,"\r\n%d Files Listed.\r\n",k);
	write(file,str,strlen(str)); }
close(file);
if(k)
	bprintf(text[CreatedFileList],name);
else {
	bputs(text[NoFiles]);
	sprintf(str,"%s%s",temp_dir,name);
	remove(str); }
strcpy(temp_file,name);
strcpy(temp_uler,"File List");
return(k);
}

