download.cpp 14.3 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 2015 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
53
    char		str[256],fname[13];
	char 		tmp[512];
    int			i,file;
	long		mod;
	long		length;
    ulong		l;
	user_t		uploader;
54
55
56
57

	getfiledat(&cfg,f); /* Get current data - right after download */
	if((length=f->size)<0L)
		length=0L;
58
59
60
61
	if(!(cfg.dir[f->dir]->misc&DIR_NOSTAT)) {
		logon_dlb+=length;  /* Update 'this call' stats */
		logon_dls++;
	}
62
	bprintf(text[FileNBytesSent],f->name,ultoac(length,tmp));
63
64
65
	sprintf(str,"%s downloaded %s from %s %s"
		,useron.alias
		,f->name,cfg.lib[cfg.dir[f->dir]->lib]->sname
66
67
68
69
70
		,cfg.dir[f->dir]->sname);
	logline("D-",str);
	/****************************/
	/* Update Downloader's Info */
	/****************************/
71
	user_downloaded(&cfg, &useron, 1, length);
rswindell's avatar
rswindell committed
72
	if(!is_download_free(&cfg,f->dir,&useron,&client))
73
74
75
76
		subtract_cdt(&cfg,&useron,f->cdt);
	/**************************/
	/* Update Uploader's Info */
	/**************************/
77
	i=matchuser(&cfg,f->uler,TRUE /*sysop_alias*/);
78
79
80
81
82
83
84
85
86
87
	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);
88
		} else {
89
90
91
			mod=(ulong)(l*(cfg.dir[f->dir]->dn_pct/100.0));
			adjustuserrec(&cfg,i,U_CDT,10,mod);
			ultoac(mod,tmp);
92
93
		}
		if(!(cfg.dir[f->dir]->misc&DIR_QUIET)) {
94
95
96
			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
97
98
99
100
				,useron.alias,tmp); 
			putsmsg(&cfg,i,str); 
		}
	}
101
102
103
	/*******************/
	/* Update IXB File */
	/*******************/
104
	f->datedled=time32(NULL);
105
	sprintf(str,"%s%s.ixb",cfg.dir[f->dir]->data_dir,cfg.dir[f->dir]->code);
106
107
	if((file=nopen(str,O_RDWR))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDWR);
108
109
		return; 
	}
110
	length=(long)filelength(file);
111
112
113
	if(length%F_IXBSIZE) {
		close(file);
		errormsg(WHERE,ERR_LEN,str,length);
114
115
		return; 
	}
116
117
118
119
120
121
	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;
122
		if(!stricmp(fname,str)) 
123
124
			break; 
	}
125
126
127
	if(l>=(ulong)length) {
		close(file);
		errormsg(WHERE,ERR_CHK,f->name,0);
128
129
		return; 
	}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
	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 */
144
			remove(getfilepath(&cfg,f,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

	sprintf(str,"Estimated Time: %s  Transfer Time: %s"
		,sectostr(cur_cps ? size/cur_cps : 0,tmp)
163
		,sectostr((uint)(end-start),tmp2));
164
165
166
167
	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) {
168
169
		lprintf(LOG_ERR, "Node %d Possible use of leech protocol (leech=%u  downloads=%u)"
			,cfg.node_num, useron.leech+1,useron.dls);
170
171
		useron.leech=(uchar)adjustuserrec(&cfg,useron.number,U_LEECH,2,1); 
	}
172
173
}

174
const char* sbbs_t::protcmdline(prot_t* prot, enum XFER_TYPE type)
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
{
	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);
		case XFER_BIDIR:
			return(prot->bicmd);
	}

	return("invalid transfer type");
}
191
192
193
194

/****************************************************************************/
/* Handles start and stop routines for transfer protocols                   */
/****************************************************************************/
195
196
int sbbs_t::protocol(prot_t* prot, enum XFER_TYPE type
					 ,char *fpath, char *fspec, bool cd)
197
{
rswindell's avatar
rswindell committed
198
	char	protlog[256],*p;
199
	char*	cmdline;
200
	char	msg[256];
201
    int		i;
202
	long	ex_mode;
203
204
	FILE*	stream;

205
206
	sprintf(protlog,"%sPROTOCOL.LOG",cfg.node_dir);
	remove(protlog);                        /* Deletes the protocol log */
207
	if(useron.misc&AUTOHANG)
208
209
		autohang=true;
	else if(text[HangUpAfterXferQ][0])
210
		autohang=yesno(text[HangUpAfterXferQ]);
211
212
	else
		autohang=false;
213
	if(sys_status&SS_ABORT || !online) {	/* if ctrl-c or hangup */
214
		autohang=false;
215
216
		return(-1); 
	}
217
218
219
	bputs(text[StartXferNow]);
	if(cd) 
		p=cfg.temp_dir;
rswindell's avatar
rswindell committed
220
221
	else
		p=NULL;
222
	cmdline=cmdstr(protcmdline(prot,type),fpath,fspec,NULL);
rswindell's avatar
rswindell committed
223
	sprintf(msg,"Transferring %s",cmdline);
224
	spymsg(msg);
225
226
	sys_status|=SS_FILEXFER;	/* disable spy during file xfer */
	/* enable telnet binary transmission in both directions */
227
228
	request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
	request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);
229
	ex_mode=0;
230
	if(prot->misc&PROT_NATIVE)
231
232
		ex_mode|=EX_NATIVE;
#ifdef __unix__		/* file xfer progs must use stdio on Unix */
233
	if(!(prot->misc&PROT_SOCKET))
deuce's avatar
deuce committed
234
		ex_mode|=(EX_STDIO|EX_BIN);
235
#endif
236
237

	i=external(cmdline,ex_mode,p);
238
	/* Got back to Text/NVT mode */
239
240
	request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX);
	request_telnet_opt(TELNET_WONT,TELNET_BINARY_TX);
241

242
	sys_status&=~SS_FILEXFER;
243
244
245
246
	if(online==ON_REMOTE)
		rioctl(IOFB);

	// Save DSZLOG to logfile
247
	if((stream=fnopen(NULL,protlog,O_RDONLY))!=NULL) {
248
		while(!feof(stream) && !ferror(stream)) {
249
			if(!fgets(protlog,sizeof(protlog),stream))
250
				break;
251
			truncsp(protlog);
252
			logline(LOG_DEBUG,nulstr,protlog);
253
254
255
256
257
258
259
260
261
262
263
264
265
266
		}
		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()
{
267
268
    char	a,c,k;
	char 	tmp[512];
269
270
271
272
273
274
275
276
277
278
279
280
281
282

	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);
283
		bputs(ultoa(c,tmp,10));
284
285
		attr(GREEN);
		outchar(']');
286
		while((k=inkey(K_NONE,DELAY_AUTOHG))!=0 && online) {
287
288
			if(toupper(k)=='H') {
				c=0;
289
290
				break; 
			}
291
292
			if(toupper(k)=='A') {
				a=1;
293
294
295
				break; 
			} 
		}
296
297
		if(!a) {
			outchar(BS);
298
299
300
			outchar(BS); 
		} 
	}
301
302
	if(c==-1) {
		bputs(text[Disconnected]);
303
304
		hangup(); 
	}
305
306
307
308
	else
		CRLF;
}

309
310
311
bool sbbs_t::checkdszlog(file_t* f)
{
	char	path[MAX_PATH+1];
312
313
314
315
316
317
318
	char	str[MAX_PATH+128];
	char	fname[MAX_PATH+1];
	char	rpath[MAX_PATH+1];
	char*	p;
	char*	rname;
	char	code;
	ulong	bytes;
319
	FILE*	fp;
320
	bool	success=false;
321
322
323
324
325
326

	sprintf(path,"%sPROTOCOL.LOG",cfg.node_dir);
	if((fp=fopen(path,"r"))==NULL)
		return(false);

	unpadfname(f->name,fname);
327
328

	getfilepath(&cfg,f,rpath);
329
	fexistcase(rpath);	/* incase of long filename */
330
331
	rname=getfname(rpath);

332
333
334
	while(!ferror(fp)) {
		if(!fgets(str,sizeof(str),fp))
			break;
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
		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 */
361
		if(stricmp(p,fname)==0 || stricmp(p,rname)==0) {
362
			/* E for Error or L for Lost Carrier or s for Skipped */
363
			/* or only sent 0 bytes! */
364
			if(code!='E' && code!='L' && code!='s' && bytes!=0)
365
366
367
				success=true;
			break;
		}
368
369
	}
	fclose(fp);
370
	return(success);
371
372
373
}


374
375
376
377
/****************************************************************************/
/* Checks dsz compatible log file for errors in transfer                    */
/* Returns 1 if the file in the struct file_t was successfuly transfered    */
/****************************************************************************/
378
bool sbbs_t::checkprotresult(prot_t* prot, int error, file_t* f)
379
{
380
381
382
383
384
385
386
387
	char str[512];
	char tmp[128];
	bool success;

	if(prot->misc&PROT_DSZLOG)
		success=checkdszlog(f);
	else
		success=(error==0);
388

389
	if(!success) {
390
391
		bprintf(text[FileNotSent],f->name);
		if(f->dir<cfg.total_dirs)
392
393
			sprintf(str,"%s attempted to download %s (%s) from %s %s"
				,useron.alias
394
				,f->name,ultoac(f->size,tmp)
395
396
				,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
		else if(f->dir==cfg.total_dirs)
397
398
			sprintf(str,"%s attempted to download QWK packet"
				,useron.alias);
399
		else if(f->dir==cfg.total_dirs+1)
400
401
			sprintf(str,"%s attempted to download attached file: %s"
				,useron.alias,f->name);
402
		logline(LOG_NOTICE,"D!",str);
403
		return(false); 
404
	}
405
	return(true);
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
}



/************************************************************************/
/* 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;
426
		getnodedat(cfg.node_num,&thisnode,true);	/* open and lock this record */
427
428
		for(i=1;i<=cfg.sys_nodes;i++) {
			if(i==cfg.node_num) continue;
429
430
431
432
433
434
435
436
437
			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); 
			}
		}
438
439
440
441
		if(i>cfg.sys_nodes) {
			thisnode.action=NODE_RFSD;
			thisnode.aux=devnum;
			putnodedat(cfg.node_num,&thisnode);	/* write devnum, unlock, and ret */
442
443
			return; 
		}
444
445
446
447
		putnodedat(cfg.node_num,&thisnode);
		if(!loop)
			bprintf(text[WaitingForDeviceN],devnum);
		loop=1;
448
449
		mswait(100); 
	}
450
451

}
452

rswindell's avatar
rswindell committed
453
bool sbbs_t::sendfile(char* fname, char prot, const char* desc)
454
455
456
457
458
459
{
	char	keys[128];
	char	ch;
	size_t	i;
	bool	result=false;

460
	if(prot > ' ')
461
462
463
464
		ch=toupper(prot);
	else {
		xfer_prot_menu(XFER_DOWNLOAD);
		mnemonics(text[ProtocolOrQuit]);
465
		sprintf(keys,"%c",text[YNQP][2]);
466
		for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
467
			if(cfg.prot[i]->dlcmd[0] && chk_ar(cfg.prot[i]->ar,&useron,&client))
468
				sprintf(keys+strlen(keys),"%c",cfg.prot[i]->mnemonic);
469

470
		ch=(char)getkeys(keys,0);
471

472
		if(ch==text[YNQP][2] || sys_status&SS_ABORT)
473
474
			return(false); 
	}
475
	for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
476
		if(cfg.prot[i]->mnemonic==ch && chk_ar(cfg.prot[i]->ar,&useron,&client))
477
478
479
480
			break;
	if(i<cfg.total_prots) {
		if(protocol(cfg.prot[i],XFER_DOWNLOAD,fname,fname,false)==0)
			result=true;
rswindell's avatar
rswindell committed
481
482
483
484
485
486
487
488
489
		off_t length = flength(fname);
		logon_dlb += length;	/* Update stats */
		logon_dls++;
		useron.dls = (ushort)adjustuserrec(&cfg, useron.number, U_DLS, 5, 1);
		useron.dlb = adjustuserrec(&cfg,useron.number, U_DLB, 10, length);
		char bytes[32];
		ultoac(length, bytes);
		bprintf(text[FileNBytesSent], getfname(fname), bytes);
		char str[128];
rswindell's avatar
rswindell committed
490
		SAFEPRINTF4(str, "%s downloaded %s: %s (%s bytes)"
rswindell's avatar
rswindell committed
491
492
			,useron.alias, desc == NULL ? "file" : desc, fname, bytes);
		logline("D-",str); 
493
494
495
496
497
		autohangup(); 
	}

	return(result);
}