download.cpp 12.4 KB
Newer Older
1 2 3 4 5 6
/* Synchronet file download routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "telnet.h"
24
#include "filedat.h"
25 26

/****************************************************************************/
27
/* Call this function *AFTER* a file has been successfully downloaded		*/
28 29 30
/* Updates downloader, uploader and downloaded file data                    */
/* Must have offset, dir and name fields filled prior to call.              */
/****************************************************************************/
31
void sbbs_t::downloadedfile(file_t* f)
32
{
33
    char		str[MAX_PATH+1];
34
	char 		tmp[512];
35 36 37 38 39
	off_t		length;

	length = getfilesize(&cfg, f);
	if(length > 0 && !(cfg.dir[f->dir]->misc&DIR_NOSTAT)) {
		logon_dlb += length;  /* Update 'this call' stats */
40 41
		logon_dls++;
	}
42
	bprintf(text[FileNBytesSent],f->name,ultoac((ulong)length,tmp));
43
	SAFEPRINTF3(str,"downloaded %s from %s %s"
44
		,f->name,cfg.lib[cfg.dir[f->dir]->lib]->sname
45 46
		,cfg.dir[f->dir]->sname);
	logline("D-",str);
47 48

	user_downloaded_file(&cfg, &useron, &client, f->dir, f->name, length);
49 50

	user_event(EVENT_DOWNLOAD);
51 52 53 54
}

/****************************************************************************/
/* This function is called when a file is unsuccessfully downloaded.        */
55
/* It logs the transfer time and checks for possible leech protocol use.    */
56
/****************************************************************************/
57
void sbbs_t::notdownloaded(off_t size, time_t start, time_t end)
58
{
59 60
    char	str[256],tmp2[256];
	char 	tmp[512];
61

62
	SAFEPRINTF2(str,"Estimated Time: %s  Transfer Time: %s"
63
		,sectostr(cur_cps ? (uint)(size/cur_cps) : 0,tmp)
64
		,sectostr((uint)(end-start),tmp2));
65 66 67 68
	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) {
69 70
		lprintf(LOG_ERR, "Node %d Possible use of leech protocol (leech=%u  downloads=%u)"
			,cfg.node_num, useron.leech+1,useron.dls);
71
		useron.leech=(uchar)adjustuserrec(&cfg,useron.number,U_LEECH,2,1);
72
	}
73 74
}

75
const char* sbbs_t::protcmdline(prot_t* prot, enum XFER_TYPE type)
76 77 78 79 80 81 82 83 84 85 86 87 88 89
{
	switch(type) {
		case XFER_UPLOAD:
			return(prot->ulcmd);
		case XFER_DOWNLOAD:
			return(prot->dlcmd);
		case XFER_BATCH_UPLOAD:
			return(prot->batulcmd);
		case XFER_BATCH_DOWNLOAD:
			return(prot->batdlcmd);
	}

	return("invalid transfer type");
}
90 91 92 93

/****************************************************************************/
/* Handles start and stop routines for transfer protocols                   */
/****************************************************************************/
94
int sbbs_t::protocol(prot_t* prot, enum XFER_TYPE type
95
					 ,const char *fpath, const char *fspec, bool cd, bool autohangup)
96
{
rswindell's avatar
rswindell committed
97
	char	protlog[256],*p;
98
	char*	cmdline;
99
	char	msg[256];
100
    int		i;
101
	long	ex_mode;
102 103
	FILE*	stream;

104
	SAFEPRINTF(protlog,"%sPROTOCOL.LOG",cfg.node_dir);
105
	remove(protlog);                        /* Deletes the protocol log */
106 107 108 109 110 111 112
	autohang=false;
	if(autohangup) {
		if(useron.misc&AUTOHANG)
			autohang=true;
		else if(text[HangUpAfterXferQ][0])
			autohang=yesno(text[HangUpAfterXferQ]);
	}
113
	if(sys_status&SS_ABORT || !online) {	/* if ctrl-c or hangup */
114
		return(-1);
115
	}
116
	bputs(text[StartXferNow]);
117
	if(cd)
118
		p=cfg.temp_dir;
rswindell's avatar
rswindell committed
119 120
	else
		p=NULL;
121

122
	ex_mode = EX_BIN;
123
	if(prot->misc&PROT_NATIVE)
124
		ex_mode|=EX_NATIVE;
125
#ifdef __unix__		/* file xfer progs may use stdio on Unix */
126
	if(!(prot->misc&PROT_SOCKET))
127
		ex_mode|=EX_STDIO;
128
#endif
129

130 131 132 133 134 135 136 137
	cmdline=cmdstr(protcmdline(prot,type), fpath, fspec, NULL, ex_mode);
	SAFEPRINTF(msg,"Transferring %s",cmdline);
	spymsg(msg);
	sys_status|=SS_FILEXFER;	/* disable spy during file xfer */
	/* enable telnet binary transmission in both directions */
	request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
	request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);

138
	i=external(cmdline,ex_mode,p);
139
	/* Got back to Text/NVT mode */
140 141
	request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
	request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
142

143
	sys_status&=~SS_FILEXFER;
144 145

	// Save DSZLOG to logfile
146
	if((stream=fnopen(NULL,protlog,O_RDONLY))!=NULL) {
147
		while(!feof(stream) && !ferror(stream)) {
148
			if(!fgets(protlog,sizeof(protlog),stream))
149
				break;
150
			truncsp(protlog);
151
			logline(LOG_DEBUG,nulstr,protlog);
152 153 154 155 156 157 158 159 160 161 162 163 164 165
		}
		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()
{
166 167
    int		a,c;
	char	k;
168
	char 	tmp[512];
169 170 171 172 173 174 175 176 177 178 179 180 181 182

	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);
183
		bputs(ultoa(c,tmp,10));
184 185
		attr(GREEN);
		outchar(']');
186
		while((k=inkey(K_NONE,DELAY_AUTOHG))!=0 && online) {
187 188
			if(toupper(k)=='H') {
				c=0;
189
				break;
190
			}
191 192
			if(toupper(k)=='A') {
				a=1;
193 194
				break;
			}
195
		}
196 197
		if(!a) {
			outchar(BS);
198 199
			outchar(BS);
		}
200
	}
201 202
	if(c==-1) {
		bputs(text[Disconnected]);
203
		hangup();
204
	}
205 206 207 208
	else
		CRLF;
}

209
bool sbbs_t::checkdszlog(const char* fpath)
210 211
{
	char	path[MAX_PATH+1];
212
	char	rpath[MAX_PATH+1];
213
	char	str[MAX_PATH+128];
214 215 216
	char*	p;
	char	code;
	ulong	bytes;
217
	FILE*	fp;
218
	bool	success=false;
219

220
	SAFEPRINTF(path,"%sPROTOCOL.LOG",cfg.node_dir);
221
	if((fp=fopen(path,"r"))==NULL)
222
		return false;
223

224 225
	SAFECOPY(rpath, fpath);
	fexistcase(rpath);
226

227 228 229
	while(!ferror(fp)) {
		if(!fgets(str,sizeof(str),fp))
			break;
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		if((p=strrchr(str,' '))!=NULL)
			*p=0;	/* we don't care about no stinking serial number */
		p=str;
		code=*p;
		FIND_WHITESPACE(p);	// Skip code
		SKIP_WHITESPACE(p);
		bytes=strtoul(p,NULL,10);
		FIND_WHITESPACE(p);	// Skip bytes
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip DTE rate
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip "bps" rate
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip CPS
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip "cps"
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip errors
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip "errors"
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip flows
		SKIP_WHITESPACE(p);
		FIND_WHITESPACE(p);	// Skip block size
		SKIP_WHITESPACE(p);
		p=getfname(p);	/* DSZ stores fullpath, BiModem doesn't */
256
		if(stricmp(p, getfname(fpath))==0 || stricmp(p, getfname(rpath))==0) {
257
			/* E for Error or L for Lost Carrier or s for Skipped */
258
			/* or only sent 0 bytes! */
259
			if(code!='E' && code!='L' && code!='s' && bytes!=0)
260 261 262
				success=true;
			break;
		}
263 264
	}
	fclose(fp);
265
	return(success);
266 267 268
}


269 270
/****************************************************************************/
/* Checks dsz compatible log file for errors in transfer                    */
271
/* Returns 1 if the file in the struct file_t was successfully transfered   */
272
/****************************************************************************/
273
bool sbbs_t::checkprotresult(prot_t* prot, int error, const char* fpath)
274
{
275 276 277
	bool success;

	if(prot->misc&PROT_DSZLOG)
278
		success=checkdszlog(fpath);
279 280
	else
		success=(error==0);
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295
	if(!success)
		bprintf(text[FileNotSent], getfname(fpath));

	return success;
}

bool sbbs_t::checkprotresult(prot_t* prot, int error, file_t* f)
{
	char str[512];
	char tmp[128];
	char fpath[MAX_PATH+1];

	getfilepath(&cfg, f, fpath);
	if(!checkprotresult(prot, error, fpath)) {
296
		if(f->dir<cfg.total_dirs)
297
			SAFEPRINTF4(str,"attempted to download %s (%s) from %s %s"
298
				,f->name,ultoac((ulong)f->size,tmp)
299 300
				,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
		else if(f->dir==cfg.total_dirs)
301
			SAFECOPY(str,"attempted to download QWK packet");
302
		else if(f->dir == cfg.total_dirs + 1)
303
			SAFEPRINTF(str,"attempted to download attached file: %s"
304
				,f->name);
305 306 307
		else
			SAFEPRINTF2(str,"attempted to download file (%s) from unknown dir: %ld"
				,f->name, (long)f->dir);
308
		logline(LOG_NOTICE,"D!",str);
309
		return false; 
310
	}
311
	return true;
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
}


/************************************************************************/
/* 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;
331
		getnodedat(cfg.node_num,&thisnode,true);	/* open and lock this record */
332 333
		for(i=1;i<=cfg.sys_nodes;i++) {
			if(i==cfg.node_num) continue;
334 335 336 337
			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);
338
					break;
339
				}
340
				putnodedat(i,&node);
341 342
			}
		}
343 344 345 346
		if(i>cfg.sys_nodes) {
			thisnode.action=NODE_RFSD;
			thisnode.aux=devnum;
			putnodedat(cfg.node_num,&thisnode);	/* write devnum, unlock, and ret */
347
			return;
348
		}
349 350 351 352
		putnodedat(cfg.node_num,&thisnode);
		if(!loop)
			bprintf(text[WaitingForDeviceN],devnum);
		loop=1;
353
		mswait(100);
354
	}
355 356

}
357

358
bool sbbs_t::sendfile(char* fname, char prot, const char* desc, bool autohang)
359 360 361 362
{
	char	keys[128];
	char	ch;
	size_t	i;
363 364
	int		error;
	bool	result;
365

366
	if(prot > ' ')
367 368 369 370
		ch=toupper(prot);
	else {
		xfer_prot_menu(XFER_DOWNLOAD);
		mnemonics(text[ProtocolOrQuit]);
371
		sprintf(keys,"%c",text[YNQP][2]);
372
		for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
373
			if(cfg.prot[i]->dlcmd[0] && chk_ar(cfg.prot[i]->ar,&useron,&client))
374
				sprintf(keys+strlen(keys),"%c",cfg.prot[i]->mnemonic);
375

376
		ch=(char)getkeys(keys,0);
377

378
		if(ch==text[YNQP][2] || sys_status&SS_ABORT)
379
			return false; 
380
	}
381
	for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
382
		if(cfg.prot[i]->mnemonic==ch && chk_ar(cfg.prot[i]->ar,&useron,&client))
383
			break;
384 385
	if(i >= cfg.total_prots)
		return false;
386
	error = protocol(cfg.prot[i], XFER_DOWNLOAD, fname, fname, false, autohang);
387 388 389 390 391 392
	if(cfg.prot[i]->misc&PROT_DSZLOG)
		result = checkdszlog(fname);
	else
		result = (error == 0);

	if(result) {
rswindell's avatar
rswindell committed
393 394 395 396
		off_t length = flength(fname);
		logon_dlb += length;	/* Update stats */
		logon_dls++;
		useron.dls = (ushort)adjustuserrec(&cfg, useron.number, U_DLS, 5, 1);
397
		useron.dlb = adjustuserrec(&cfg,useron.number, U_DLB, 10, (long)length);
rswindell's avatar
rswindell committed
398
		char bytes[32];
399
		ultoac((ulong)length, bytes);
rswindell's avatar
rswindell committed
400 401
		bprintf(text[FileNBytesSent], getfname(fname), bytes);
		char str[128];
402 403
		SAFEPRINTF3(str, "downloaded %s: %s (%s bytes)"
			,desc == NULL ? "file" : desc, fname, bytes);
404 405
		logline("D-",str);
		autohangup();
406 407 408
	} else {
		char str[128];
		bprintf(text[FileNotSent], getfname(fname));
409
		SAFEPRINTF2(str,"attempted to download %s: %s", desc == NULL ? "file" : desc, fname);
410
		logline(LOG_NOTICE,"D!",str);
411
	}
412
	return result;
413
}
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437

// contains some copy/pasta from downloadedfile()
bool sbbs_t::sendfile(file_t* f, char prot, bool autohang)
{
	char path[MAX_PATH + 1];
	char str[256];

	SAFEPRINTF2(str, "from %s %s"
		,cfg.lib[cfg.dir[f->dir]->lib]->sname
		,cfg.dir[f->dir]->sname);
	bool result = sendfile(getfilepath(&cfg, f, path), prot, str, autohang);
	if(result == true) {
		if(cfg.dir[f->dir]->misc&DIR_TFREE && cur_cps)
			starttime += f->size / (ulong)cur_cps;
		off_t length = getfilesize(&cfg, f);
		if(length > 0 && !(cfg.dir[f->dir]->misc&DIR_NOSTAT)) {
			logon_dlb += length;  /* Update 'this call' stats */
			logon_dls++;
		}
		user_downloaded_file(&cfg, &useron, &client, f->dir, f->name, length);
		user_event(EVENT_DOWNLOAD);
	}
	return result;
}