download.cpp 12.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* download.cpp */

/* Synchronet file download routines */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2003 Rob Swindell - http://www.synchro.net/copyright.html		*
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *																			*
 * This program 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			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
39
#include "telnet.h"
40 41 42 43 44 45 46

/****************************************************************************/
/* Updates downloader, uploader and downloaded file data                    */
/* Must have offset, dir and name fields filled prior to call.              */
/****************************************************************************/
void sbbs_t::downloadfile(file_t* f)
{
47 48 49 50 51 52
    char	str[256],fname[13];
	char 	tmp[512];
    int		i,file;
	long	length,mod;
    ulong	l;
	user_t	uploader;
53 54 55 56 57 58 59

	getfiledat(&cfg,f); /* Get current data - right after download */
	if((length=f->size)<0L)
		length=0L;
	logon_dlb+=length;  /* Update 'this call' stats */
	logon_dls++;
	bprintf(text[FileNBytesSent],f->name,ultoac(length,tmp));
60 61 62
	sprintf(str,"%s downloaded %s from %s %s"
		,useron.alias
		,f->name,cfg.lib[cfg.dir[f->dir]->lib]->sname
63 64 65 66 67 68 69
		,cfg.dir[f->dir]->sname);
	logline("D-",str);
	/****************************/
	/* Update Downloader's Info */
	/****************************/
	useron.dls=(ushort)adjustuserrec(&cfg,useron.number,U_DLS,5,1);
	useron.dlb=adjustuserrec(&cfg,useron.number,U_DLB,10,length);
70
	if(!is_download_free(&cfg,f->dir,&useron))
71 72 73 74
		subtract_cdt(&cfg,&useron,f->cdt);
	/**************************/
	/* Update Uploader's Info */
	/**************************/
75
	i=matchuser(&cfg,f->uler,TRUE /*sysop_alias*/);
76 77 78 79 80 81 82 83 84 85
	uploader.number=i;
	getuserdat(&cfg,&uploader);
	if(i && i!=useron.number && uploader.firston<f->dateuled) {
		l=f->cdt;
		if(!(cfg.dir[f->dir]->misc&DIR_CDTDL))	/* Don't give credits on d/l */
			l=0;
		if(cfg.dir[f->dir]->misc&DIR_CDTMIN && cur_cps) { /* Give min instead of cdt */
			mod=((ulong)(l*(cfg.dir[f->dir]->dn_pct/100.0))/cur_cps)/60;
			adjustuserrec(&cfg,i,U_MIN,10,mod);
			sprintf(tmp,"%lu minute",mod);
86
		} else {
87 88 89
			mod=(ulong)(l*(cfg.dir[f->dir]->dn_pct/100.0));
			adjustuserrec(&cfg,i,U_CDT,10,mod);
			ultoac(mod,tmp);
90 91
		}
		if(!(cfg.dir[f->dir]->misc&DIR_QUIET)) {
92 93 94
			sprintf(str,text[DownloadUserMsg]
				,!strcmp(cfg.dir[f->dir]->code,"TEMP") ? temp_file : f->name
				,!strcmp(cfg.dir[f->dir]->code,"TEMP") ? text[Partially] : nulstr
95 96 97 98
				,useron.alias,tmp); 
			putsmsg(&cfg,i,str); 
		}
	}
99 100 101 102
	/*******************/
	/* Update IXB File */
	/*******************/
	f->datedled=time(NULL);
103
	sprintf(str,"%s%s.ixb",cfg.dir[f->dir]->data_dir,cfg.dir[f->dir]->code);
104 105
	if((file=nopen(str,O_RDWR))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDWR);
106 107
		return; 
	}
108 109 110 111
	length=filelength(file);
	if(length%F_IXBSIZE) {
		close(file);
		errormsg(WHERE,ERR_LEN,str,length);
112 113
		return; 
	}
114 115 116 117 118 119
	strcpy(fname,f->name);
	for(i=8;i<12;i++)   /* Turn FILENAME.EXT into FILENAMEEXT */
		fname[i]=fname[i+1];
	for(l=0;l<(ulong)length;l+=F_IXBSIZE) {
		read(file,str,F_IXBSIZE);      /* Look for the filename in the IXB file */
		str[11]=0;
120 121 122
		if(!strcmp(fname,str)) 
			break; 
	}
123 124 125
	if(l>=(ulong)length) {
		close(file);
		errormsg(WHERE,ERR_CHK,f->name,0);
126 127
		return; 
	}
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
	lseek(file,l+18,SEEK_SET);
	write(file,&f->datedled,4);  /* Write the current time stamp for datedled */
	close(file);
	/*******************/
	/* Update DAT File */
	/*******************/
	f->timesdled++;
	putfiledat(&cfg,f);
	/******************************************/
	/* Update User to User index if necessary */
	/******************************************/
	if(f->dir==cfg.user_dir) {
		rmuserxfers(&cfg,0,useron.number,f->name);
		if(!getuserxfers(0,0,f->name)) { /* check if any ixt entries left */
			sprintf(str,"%s%s",f->altpath>0 && f->altpath<=cfg.altpaths ?
				cfg.altpath[f->altpath-1] : cfg.dir[f->dir]->path,unpadfname(f->name,tmp));
			remove(str);
145 146 147
			removefiledat(&cfg,f); 
		} 
	}
148 149

	user_event(EVENT_DOWNLOAD);
150 151 152 153 154 155 156 157
}

/****************************************************************************/
/* This function is called when a file is unsuccessfully downloaded.        */
/* It logs the tranfer time and checks for possible leech protocol use.     */
/****************************************************************************/
void sbbs_t::notdownloaded(ulong size, time_t start, time_t end)
{
158 159
    char	str[256],tmp2[256];
	char 	tmp[512];
160 161 162 163 164 165 166 167 168 169 170

	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(cfg.leech_pct && cur_cps                 /* leech detection */
		&& end-start>=cfg.leech_sec
		&& end-start>=(double)(size/cur_cps)*(double)cfg.leech_pct/100.0) {
		sprintf(str,"Possible use of leech protocol (leech=%u  downloads=%u)"
			,useron.leech+1,useron.dls);
		errorlog(str);
171 172
		useron.leech=(uchar)adjustuserrec(&cfg,useron.number,U_LEECH,2,1); 
	}
173 174 175 176 177 178
}


/****************************************************************************/
/* Handles start and stop routines for transfer protocols                   */
/****************************************************************************/
rswindell's avatar
rswindell committed
179
int sbbs_t::protocol(char *cmdline, bool cd)
180
{
rswindell's avatar
rswindell committed
181
	char	protlog[256],*p;
182
	char	msg[256];
183 184 185
    int		i;
	FILE*	stream;

186 187
	sprintf(protlog,"%sPROTOCOL.LOG",cfg.node_dir);
	remove(protlog);                        /* Deletes the protocol log */
188 189 190 191 192 193
	if(useron.misc&AUTOHANG)
		autohang=1;
	else
		autohang=yesno(text[HangUpAfterXferQ]);
	if(sys_status&SS_ABORT) {				/* if ctrl-c */
		autohang=0;
194 195
		return(-1); 
	}
196 197 198 199 200
	bputs(text[StartXferNow]);
	RIOSYNC(0);
	//lprintf("%s",cmdline);
	if(cd) 
		p=cfg.temp_dir;
rswindell's avatar
rswindell committed
201 202
	else
		p=NULL;
rswindell's avatar
rswindell committed
203
	sprintf(msg,"Transferring %s",cmdline);
204
	spymsg(msg);
205 206 207 208 209 210 211
	sys_status|=SS_FILEXFER;	/* disable spy during file xfer */
	/* enable telnet binary transmission in both directions */
	if(!(telnet_mode&TELNET_MODE_BIN_RX)) {
		send_telnet_cmd(TELNET_DO,TELNET_BINARY);
		telnet_mode|=TELNET_MODE_BIN_RX;
	}
	send_telnet_cmd(TELNET_WILL,TELNET_BINARY);
212 213 214
	i=external(cmdline
		,EX_OUTL
#ifdef __unix__		/* file xfer progs use stdio on Unix */
215
		|EX_INR|EX_OUTR|EX_BIN
216 217
#endif
		,p);
218 219 220 221 222 223 224 225
	/* disable telnet binary transmission mode */
	send_telnet_cmd(TELNET_WONT,TELNET_BINARY);
	/* Got back to Text/NVT mode */
	if(telnet_mode&TELNET_MODE_BIN_RX) {
		send_telnet_cmd(TELNET_DONT,TELNET_BINARY);
		telnet_mode&=~TELNET_MODE_BIN_RX;
	}

226
	sys_status&=~SS_FILEXFER;
227 228 229 230
	if(online==ON_REMOTE)
		rioctl(IOFB);

	// Save DSZLOG to logfile
231
	if((stream=fnopen(NULL,protlog,O_RDONLY))!=NULL) {
232
		while(!feof(stream) && !ferror(stream)) {
233
			if(!fgets(protlog,sizeof(protlog),stream))
234
				break;
235 236
			truncsp(protlog);
			logline(nulstr,protlog);
237 238 239 240 241 242 243 244 245 246 247 248 249 250
		}
		fclose(stream);
	}

	CRLF;
	if(autohang) sys_status|=SS_PAUSEOFF;	/* Pause off after download */
	return(i);
}

/****************************************************************************/
/* Invokes the timed auto-hangup after transfer routine                     */
/****************************************************************************/
void sbbs_t::autohangup()
{
251 252
    char	a,c,k;
	char 	tmp[512];
253 254 255 256 257 258 259 260 261 262 263 264 265 266

	if(online!=ON_REMOTE)
		return;
	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);
267
		bputs(ultoa(c,tmp,10));
268 269
		attr(GREEN);
		outchar(']');
270
		while((k=inkey(K_NONE,DELAY_AUTOHG))!=0 && online) {
271 272
			if(toupper(k)=='H') {
				c=0;
273 274
				break; 
			}
275 276
			if(toupper(k)=='A') {
				a=1;
277 278 279
				break; 
			} 
		}
280 281
		if(!a) {
			outchar(BS);
282 283 284
			outchar(BS); 
		} 
	}
285 286
	if(c==-1) {
		bputs(text[Disconnected]);
287 288
		hangup(); 
	}
289 290 291 292 293 294 295 296 297 298
	else
		CRLF;
}

/****************************************************************************/
/* Checks dsz compatible log file for errors in transfer                    */
/* Returns 1 if the file in the struct file_t was successfuly transfered    */
/****************************************************************************/
bool sbbs_t::checkprotlog(file_t* f)
{
299 300 301 302
	char	str[256],size[128];
	char 	tmp[512];
    int		file;
    FILE *	stream;
303 304 305 306 307

	sprintf(str,"%sPROTOCOL.LOG",cfg.node_dir);
	if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
		bprintf(text[FileNotSent],f->name);
		if(f->dir<cfg.total_dirs)
308 309
			sprintf(str,"%s attempted to download %s (%s) from %s %s"
				,useron.alias
310 311 312
				,f->name,ultoac(f->size,size)
				,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
		else if(f->dir==cfg.total_dirs)
313 314
			sprintf(str,"%s attempted to download QWK packet"
				,useron.alias);
315
		else if(f->dir==cfg.total_dirs+1)
316 317
			sprintf(str,"%s attempted to download attached file: %s"
				,useron.alias,f->name);
318
		logline("D!",str);
319 320
		return(0); 
	}
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
	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.     */
	strupr(tmp);
	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); now handled by protocol()
			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! */
336 337 338
			return(true); 
		} 
	}
339 340 341
	fclose(stream);
	bprintf(text[FileNotSent],f->name);
	if(f->dir<cfg.total_dirs)
342
		sprintf(str,"%s attempted to download %s (%s) from %s %s"
343
			,useron.alias
344 345 346 347
			,f->name
			,ultoac(f->size,tmp)
			,cfg.lib[cfg.dir[f->dir]->lib]->sname
			,cfg.dir[f->dir]->sname);
348
	else
349
		sprintf(str,"%s attempted to download QWK packet",useron.alias);
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	logline("D!",str);
	return(false);
}



/************************************************************************/
/* Wait (for a limited period of time) for sequential dev to become 	*/
/* available for file retrieval 										*/
/************************************************************************/
void sbbs_t::seqwait(uint devnum)
{
	char loop=0;
	int i;
	time_t start;
	node_t node;

	if(!devnum)
		return;
	for(start=now=time(NULL);online && now-start<90;now=time(NULL)) {
		if(msgabort())				/* max wait ^^^^ sec */
			break;
372
		getnodedat(cfg.node_num,&thisnode,true);	/* open and lock this record */
373 374
		for(i=1;i<=cfg.sys_nodes;i++) {
			if(i==cfg.node_num) continue;
375 376 377 378 379 380 381 382 383
			if(getnodedat(i,&node,true)==0) {
				if((node.status==NODE_INUSE || node.status==NODE_QUIET)
					&& node.action==NODE_RFSD && node.aux==devnum) {
					putnodedat(i,&node);
					break; 
				}
				putnodedat(i,&node); 
			}
		}
384 385 386 387
		if(i>cfg.sys_nodes) {
			thisnode.action=NODE_RFSD;
			thisnode.aux=devnum;
			putnodedat(cfg.node_num,&thisnode);	/* write devnum, unlock, and ret */
388 389
			return; 
		}
390 391 392 393
		putnodedat(cfg.node_num,&thisnode);
		if(!loop)
			bprintf(text[WaitingForDeviceN],devnum);
		loop=1;
394 395
		mswait(100); 
	}
396 397

}