Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

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

/* Synchronet file upload-related 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 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 39 40 41 42 43 44 45 46 47
 *																			*
 * 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"

/****************************************************************************/
/* Adds file data in 'f' to DIR#.DAT and DIR#.IXB   and updates user        */
/* info for uploader. Must have .name, .desc and .dir fields filled prior   */
/* to a call to this function.                                              */
/* Returns 1 if file uploaded sucessfully, 0 if not.                        */
/****************************************************************************/
bool sbbs_t::uploadfile(file_t *f)
{
48 49 50 51 52 53 54 55 56
	char	path[MAX_PATH+1];
	char	str[MAX_PATH+1],fname[25];
	char	ext[F_EXBSIZE+1];
	char	desc[F_EXBSIZE+1];
	char	tmp[MAX_PATH+1];
	int		file;
    uint	i;
    long	length;
	FILE*	stream;
57

58
	// f->misc=0; Removed AUG-22-2001 - broken anonymous uploads
59 60 61
	curdirnum=f->dir;
	if(findfile(&cfg,f->dir,f->name)) {
		errormsg(WHERE,ERR_CHK,f->name,f->dir);
62 63
		return(0); 
	}
64 65 66
	sprintf(path,"%s%s",f->altpath>0 && f->altpath<=cfg.altpaths
		? cfg.altpath[f->altpath-1]
		: cfg.dir[f->dir]->path,unpadfname(f->name,fname));
67
	sprintf(tmp,"%s%s",cfg.temp_dir,fname);
68
	if(!fexistcase(path) && fexistcase(tmp))
69
		mv(tmp,path,0);
70
	if(!fexistcase(path)) {
71
		bprintf(text[FileNotReceived],f->name);
72
		sprintf(str,"attempted to upload %s to %s %s (Not received)"
73
			,f->name
74
			,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
75
		logline(LOG_NOTICE,"U!",str);
76 77
		return(0); 
	}
78 79 80
	strcpy(tmp,f->name);
	truncsp(tmp);
	for(i=0;i<cfg.total_ftests;i++)
81
		if(cfg.ftest[i]->ext[0]=='*' || !stricmp(tmp+9,cfg.ftest[i]->ext)) {
rswindell's avatar
rswindell committed
82
			if(!chk_ar(cfg.ftest[i]->ar,&useron,&client))
83 84 85 86
				continue;
			attr(LIGHTGRAY);
			bputs(cfg.ftest[i]->workstr);

87
			sprintf(sbbsfilename,"SBBSFILENAME=%.64s",unpadfname(f->name,fname));
88 89 90
			putenv(sbbsfilename);
			sprintf(sbbsfiledesc,"SBBSFILEDESC=%.*s",LEN_FDESC,f->desc);
			putenv(sbbsfiledesc);
91
			sprintf(str,"%ssbbsfile.nam",cfg.node_dir);
92 93
			if((stream=fopen(str,"w"))!=NULL) {
				fwrite(fname,1,strlen(fname),stream);
94 95
				fclose(stream); 
			}
96
			sprintf(str,"%ssbbsfile.des",cfg.node_dir);
97 98
			if((stream=fopen(str,"w"))!=NULL) {
				fwrite(f->desc,1,strlen(f->desc),stream);
99 100
				fclose(stream); 
			}
101
			if(external(cmdstr(cfg.ftest[i]->cmd,path,f->desc,NULL),EX_OFFLINE)) {
102
				sprintf(str,"attempted to upload %s to %s %s (%s Errors)"
103
					,f->name
104
					,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname,cfg.ftest[i]->ext);
105
				logline(LOG_NOTICE,"U!",str);
106 107 108 109 110 111 112
#if 0
				sprintf(str,"Failed test: %s", cmdstr(cfg.ftest[i]->cmd,path,f->desc,NULL));
				logline("  ",str);
#endif
				bprintf(text[FileHadErrors],f->name,cfg.ftest[i]->ext);
				if(!SYSOP || yesno(text[DeleteFileQ]))
					remove(path);
113 114
				return(0); 
			} else {
115
				sprintf(str,"%ssbbsfile.nam",cfg.node_dir);
116 117 118 119 120 121 122 123
				if((stream=fopen(str,"r"))!=NULL) {
					if(fgets(str,128,stream)) {
						truncsp(str);
						padfname(str,f->name);
						strcpy(tmp,f->name);
						truncsp(tmp);
						sprintf(path,"%s%s",f->altpath>0 && f->altpath<=cfg.altpaths
							? cfg.altpath[f->altpath-1] : cfg.dir[f->dir]->path
124 125
							,unpadfname(f->name,fname)); 
					}
126 127
					fclose(stream);
					}
128
				sprintf(str,"%ssbbsfile.des",cfg.node_dir);
129 130 131
				if((stream=fopen(str,"r"))!=NULL) {
					if(fgets(str,128,stream)) {
						truncsp(str);
132 133 134 135 136 137 138
						sprintf(f->desc,"%.*s",LEN_FDESC,str); 
					}
					fclose(stream); 
				}
				CRLF; 
			} 
		}
139

140
	if((length=(long)flength(path))==0L) {
141 142
		bprintf(text[FileZeroLength],f->name);
		remove(path);
143
		sprintf(str,"attempted to upload %s to %s %s (Zero length)"
144
			,f->name
145
			,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
146
		logline(LOG_NOTICE,"U!",str);
147 148
		return(0); 
	}
149 150
	if(cfg.dir[f->dir]->misc&DIR_DIZ) {
		for(i=0;i<cfg.total_fextrs;i++)
rswindell's avatar
rswindell committed
151
			if(!stricmp(cfg.fextr[i]->ext,tmp+9) && chk_ar(cfg.fextr[i]->ar,&useron,&client))
152 153 154
				break;
		if(i<cfg.total_fextrs) {
			sprintf(str,"%sFILE_ID.DIZ",cfg.temp_dir);
155 156
			if(fexistcase(str))
				remove(str);
157
			external(cmdstr(cfg.fextr[i]->cmd,path,"FILE_ID.DIZ",NULL),EX_OFFLINE);
158
			if(!fexistcase(str)) {
159
				sprintf(str,"%sDESC.SDI",cfg.temp_dir);
160 161
				if(fexistcase(str))
					remove(str);
162
				external(cmdstr(cfg.fextr[i]->cmd,path,"DESC.SDI",NULL),EX_OFFLINE); 
163 164
				fexistcase(str);
			}
165
			if((file=nopen(str,O_RDONLY))!=-1) {
166 167 168
				memset(ext,0,F_EXBSIZE+1);
				read(file,ext,F_EXBSIZE);
				for(i=F_EXBSIZE;i;i--)
169
					if(ext[i-1]>' ')
170 171 172 173
						break;
				ext[i]=0;
				if(!f->desc[0]) {
					strcpy(desc,ext);
174 175
					strip_exascii(desc, desc);
					prep_file_desc(desc, desc);
176 177 178
					for(i=0;desc[i];i++)
						if(isalnum(desc[i]))
							break;
179 180
					sprintf(f->desc,"%.*s",LEN_FDESC,desc+i); 
				}
181 182
				close(file);
				remove(str);
183 184 185 186
				f->misc|=FM_EXTDESC; 
			} 
		} 
	}
187

188 189 190 191
	if(!(cfg.dir[f->dir]->misc&DIR_NOSTAT)) {
		logon_ulb+=length;  /* Update 'this call' stats */
		logon_uls++;
	}
192 193 194
	if(cfg.dir[f->dir]->misc&DIR_AONLY)  /* Forced anonymous */
		f->misc|=FM_ANON;
	f->cdt=length;
195
	f->dateuled=time32(NULL);
196 197 198 199 200
	f->timesdled=0;
	f->datedled=0L;
	f->opencount=0;
	strcpy(f->uler,useron.alias);
	bprintf(text[FileNBytesReceived],f->name,ultoac(length,tmp));
201 202 203 204 205 206
	if(!f->desc[0]) {
		if(stricmp(f->name,getfname(path)))
			SAFECOPY(f->desc,getfname(path));
		else
			sprintf(f->desc,"%.*s",LEN_FDESC,text[NoDescription]);
	}
207 208 209 210
	if(!addfiledat(&cfg,f))
		return(0);

	if(f->misc&FM_EXTDESC)
211
		putextdesc(&cfg,f->dir,f->datoffset,ext);
212

213
	sprintf(str,"uploaded %s to %s %s"
214
		,f->name,cfg.lib[cfg.dir[f->dir]->lib]->sname
215 216
		,cfg.dir[f->dir]->sname);
	if(cfg.dir[f->dir]->upload_sem[0])
217
		ftouch(cmdstr(cfg.dir[f->dir]->upload_sem,nulstr,nulstr,NULL));
218 219 220 221
	logline("U+",str);
	/**************************/
	/* Update Uploader's Info */
	/**************************/
222
	user_uploaded(&cfg, &useron, 1, length);
223 224 225 226 227 228
	if(cfg.dir[f->dir]->up_pct && cfg.dir[f->dir]->misc&DIR_CDTUL) { /* credit for upload */
		if(cfg.dir[f->dir]->misc&DIR_CDTMIN && cur_cps)    /* Give min instead of cdt */
			useron.min=adjustuserrec(&cfg,useron.number,U_MIN,10
				,((ulong)(length*(cfg.dir[f->dir]->up_pct/100.0))/cur_cps)/60);
		else
			useron.cdt=adjustuserrec(&cfg,useron.number,U_CDT,10
229 230 231 232
				,(ulong)(f->cdt*(cfg.dir[f->dir]->up_pct/100.0))); 
	}

	user_event(EVENT_UPLOAD);
233 234 235 236 237 238 239

	return(true);
}

/****************************************************************************/
/* Uploads files                                                            */
/****************************************************************************/
240
bool sbbs_t::upload(uint dirnum)
241
{
242
	char	descbeg[25]={""},descend[25]={""}
243
				,fname[13],keys[256],ch,*p;
244 245 246
	char	str[MAX_PATH+1];
	char	path[MAX_PATH+1];
	char	spath[MAX_PATH+1];
247
	char 	tmp[512];
248 249 250 251 252 253 254 255
    time_t	start,end;
    uint	i,j,k,destuser[MAX_USERXFER],destusers=0;
	int		file;
	ulong	space;
    file_t	f;
    user_t	user;
    node_t	node;

256 257 258 259 260 261 262 263 264
	/* Security Checks */
	if(useron.rest&FLAG('U')) {
		bputs(text[R_Upload]);
		return(false); 
	}
	if(dirnum==INVALID_DIR) {
		bputs(text[CantUploadHere]);
		return(false);
	}
265 266 267 268 269 270 271 272 273 274
	if(!(useron.exempt&FLAG('U')) && !dir_op(dirnum)) {
		if(!chk_ar(cfg.dir[dirnum]->ul_ar,&useron,&client)) {
			bputs(dirnum==cfg.user_dir ? text[CantUploadToUser] : 
				dirnum==cfg.sysop_dir ? text[CantUploadToSysop] : text[CantUploadHere]);
			return(false); 
		}
		if(cfg.dir[dirnum]->maxfiles && getfiles(&cfg,dirnum)>=cfg.dir[dirnum]->maxfiles) {
			bputs(dirnum==cfg.user_dir ? text[UserDirFull] : text[DirFull]);
			return(false);
		}
275 276
	}

277 278 279 280 281 282 283
	if(sys_status&SS_EVENT && online==ON_REMOTE && !dir_op(dirnum))
		bprintf(text[UploadBeforeEvent],timeleft/60);
	if(altul)
		strcpy(path,cfg.altpath[altul-1]);
	else
		strcpy(path,cfg.dir[dirnum]->path);

284 285
	if(!isdir(path)) {
		bprintf(text[DirectoryDoesNotExist], path);
286
		lprintf(LOG_ERR,"File directory does not exist: %s", path);
287 288
		return(false);
	}
289 290

	/* get free disk space */
291 292
	space=getfreediskspace(path,1024);
	if(space<(ulong)cfg.min_dspace) {
293
		bputs(text[LowDiskSpace]);
294
		lprintf(LOG_ERR,"Diskspace is low: %s (%lu kilobytes)",path,space);
295
		if(!dir_op(dirnum))
296 297
			return(false); 
	}
298 299 300 301 302 303
	bprintf(text[DiskNBytesFree],ultoac(space,tmp));

	f.dir=curdirnum=dirnum;
	f.misc=0;
	f.altpath=altul;
	bputs(text[Filename]);
304
	if(!getstr(fname,12,0) || strchr(fname,'?') || strchr(fname,'*')
305
		|| !checkfname(fname) || (trashcan(fname,"file") && !dir_op(dirnum))) {
306 307
		if(fname[0])
			bputs(text[BadFilename]);
308 309
		return(false); 
	}
310 311 312 313 314 315 316
	if(dirnum==cfg.sysop_dir)
		sprintf(str,text[UploadToSysopDirQ],fname);
	else if(dirnum==cfg.user_dir)
		sprintf(str,text[UploadToUserDirQ],fname);
	else
		sprintf(str,text[UploadToCurDirQ],fname,cfg.lib[cfg.dir[dirnum]->lib]->sname
			,cfg.dir[dirnum]->sname);
317
	if(!yesno(str)) return(false);
318 319
	action=NODE_ULNG;
	sprintf(str,"%s%s",path,fname);
320
	if(fexistcase(str)) {   /* File is on disk */
321 322 323 324 325 326
#ifdef _WIN32
		GetShortPathName(str, spath, sizeof(spath));
#else
		SAFECOPY(spath,str);
#endif
		SAFECOPY(fname,getfname(spath));
327 328
		if(!dir_op(dirnum) && online!=ON_LOCAL) {		 /* local users or sysops */
			bprintf(text[FileAlreadyThere],fname);
329 330
			return(false); 
		}
331
		if(!yesno(text[FileOnDiskAddQ]))
332 333
			return(false); 
	}
334
	padfname(fname,f.name);
335 336 337 338 339 340 341 342
	strcpy(str,cfg.dir[dirnum]->exts);
	strcpy(tmp,f.name);
	truncsp(tmp);
	j=strlen(str);
	for(i=0;i<j;i+=ch+1) { /* Check extension of upload with allowable exts */
		p=strchr(str+i,',');
		if(p!=NULL)
			*p=0;
343
		ch=(char)strlen(str+i);
344
		if(!stricmp(tmp+9,str+i))
345 346
			break; 
	}
347 348 349 350
	if(j && i>=j) {
		bputs(text[TheseFileExtsOnly]);
		bputs(cfg.dir[dirnum]->exts);
		CRLF;
351 352
		if(!dir_op(dirnum)) return(false); 
	}
353
	bputs(text[SearchingForDupes]);
354 355 356 357 358
	if(findfile(&cfg,dirnum,f.name)) {
		bputs(text[SearchedForDupes]);
		bprintf(text[FileAlreadyOnline],fname);
		return(false); 	 /* File is already in database */
	}
359
	for(i=k=0;i<usrlibs;i++) {
360 361 362 363
		for(j=0;j<usrdirs[i];j++,k++) {
			outchar('.');
			if(k && !(k%5))
				bputs("\b\b\b\b\b     \b\b\b\b\b");
364 365 366
			if(usrdir[i][j]==dirnum)
				continue;	/* we already checked this dir */
			if(cfg.dir[usrdir[i][j]]->misc&DIR_DUPES
367 368
				&& findfile(&cfg,usrdir[i][j],f.name)) {
				bputs(text[SearchedForDupes]);
369
				bprintf(text[FileAlreadyOnline],fname);
370
				if(!dir_op(dirnum))
371
					return(false); 	 /* File is in database for another dir */
372 373 374
			}
		}
	}
375 376 377 378 379
	bputs(text[SearchedForDupes]);
	if(dirnum==cfg.user_dir) {  /* User to User transfer */
		bputs(text[EnterAfterLastDestUser]);
		while((!dir_op(dirnum) && destusers<cfg.max_userxfer) || destusers<MAX_USERXFER) {
			bputs(text[SendFileToUser]);
380
			if(!getstr(str,LEN_ALIAS,cfg.uq&UQ_NOUPRLWR ? K_NONE:K_UPRLWR))
381 382 383 384
				break;
			if((user.number=finduser(str))!=0) {
				if(!dir_op(dirnum) && user.number==useron.number) {
					bputs(text[CantSendYourselfFiles]);
385 386
					continue; 
				}
387 388 389 390 391
				for(i=0;i<destusers;i++)
					if(user.number==destuser[i])
						break;
				if(i<destusers) {
					bputs(text[DuplicateUser]);
392 393
					continue; 
				}
394 395
				getuserdat(&cfg,&user);
				if((user.rest&(FLAG('T')|FLAG('D')))
rswindell's avatar
rswindell committed
396 397
					|| !chk_ar(cfg.lib[cfg.dir[cfg.user_dir]->lib]->ar,&user,/* client: */NULL)
					|| !chk_ar(cfg.dir[cfg.user_dir]->dl_ar,&user,/* client: */NULL)) {
398 399
					bprintf(text[UserWontBeAbleToDl],user.alias); 
				} else {
400
					bprintf(text[UserAddedToDestList],user.alias);
401 402 403
					destuser[destusers++]=user.number; 
				} 
			}
404
			else {
405 406 407
				CRLF; 
			} 
		}
408
		if(!destusers)
409 410
			return(false); 
	}
411 412 413 414 415
	if(cfg.dir[dirnum]->misc&DIR_RATE) {
		SYNC;
		bputs(text[RateThisFile]);
		ch=getkey(K_ALPHA);
		if(!isalpha(ch) || sys_status&SS_ABORT)
416
			return(false);
417
		CRLF;
418 419
		sprintf(descbeg,text[Rated],toupper(ch)); 
	}
420 421 422 423
	if(cfg.dir[dirnum]->misc&DIR_ULDATE) {
		now=time(NULL);
		if(descbeg[0])
			strcat(descbeg," ");
424
		sprintf(str,"%s  ",unixtodstr(&cfg,(time32_t)now,tmp));
425 426
		strcat(descbeg,str); 
	}
427 428 429 430
	if(cfg.dir[dirnum]->misc&DIR_MULT) {
		SYNC;
		if(!noyes(text[MultipleDiskQ])) {
			bputs(text[HowManyDisksTotal]);
431
			if((int)(i=getnum(99))<2)
432
				return(false);
433
			bputs(text[NumberOfFile]);
434
			if((int)(j=getnum(i))<1)
435
				return(false);
436 437 438 439 440
			if(j==1)
				upload_lastdesc[0]=0;
			if(i>9)
				sprintf(descend,text[FileOneOfTen],j,i);
			else
441 442 443 444
				sprintf(descend,text[FileOneOfTwo],j,i); 
		} else
			upload_lastdesc[0]=0; 
	}
445 446 447 448 449 450
	else
		upload_lastdesc[0]=0;
	bputs(text[EnterDescNow]);
	i=LEN_FDESC-(strlen(descbeg)+strlen(descend));
	getstr(upload_lastdesc,i,K_LINE|K_EDIT|K_AUTODEL);
	if(sys_status&SS_ABORT)
451
		return(false);
452 453 454 455 456 457 458 459
	if(descend[0])      /* end of desc specified, so pad desc with spaces */
		sprintf(f.desc,"%s%-*s%s",descbeg,i,upload_lastdesc,descend);
	else                /* no end specified, so string ends at desc end */
		sprintf(f.desc,"%s%s",descbeg,upload_lastdesc);

	if(cfg.dir[dirnum]->misc&DIR_ANON && !(cfg.dir[dirnum]->misc&DIR_AONLY)
		&& (dir_op(dirnum) || useron.exempt&FLAG('A'))) {
		if(!noyes(text[AnonymousQ]))
460 461
			f.misc|=FM_ANON; 
	}
462
	sprintf(str,"%s%s",path,fname);
463
	if(fexistcase(str)) {   /* File is on disk */
464
		if(!uploadfile(&f))
465 466
			return(false); 
	} else {
467
		xfer_prot_menu(XFER_UPLOAD);
468
		SYNC;
469
		sprintf(keys,"%c",text[YNQP][2]);
470 471 472 473
		if(dirnum==cfg.user_dir || !cfg.max_batup)  /* no batch user to user xfers */
			mnemonics(text[ProtocolOrQuit]);
		else {
			mnemonics(text[ProtocolBatchOrQuit]);
474 475
			strcat(keys,"B"); 
		}
476
		for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
477
			if(cfg.prot[i]->ulcmd[0] && chk_ar(cfg.prot[i]->ar,&useron,&client)) {
478
				sprintf(tmp,"%c",cfg.prot[i]->mnemonic);
479 480
				strcat(keys,tmp); 
			}
481
		ch=(char)getkeys(keys,0);
482
		if(ch==text[YNQP][2] || (sys_status&SS_ABORT))
483
			return(false);
484 485 486 487 488 489
		if(ch=='B') {
			if(batup_total>=cfg.max_batup)
				bputs(text[BatchUlQueueIsFull]);
			else {
				for(i=0;i<batup_total;i++)
					if(!strcmp(batup_name[i],f.name)) {
490
						bprintf(text[FileAlreadyInQueue],fname);
491 492
						return(false); 
					}
493 494 495 496 497 498 499
				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]
500
					,fname,batup_total,cfg.max_batup); 
501 502
			} 
		} else {
503 504
			for(i=0;i<cfg.total_prots;i++)
				if(cfg.prot[i]->ulcmd[0] && cfg.prot[i]->mnemonic==ch
rswindell's avatar
rswindell committed
505
					&& chk_ar(cfg.prot[i]->ar,&useron,&client))
506 507 508
					break;
			if(i<cfg.total_prots) {
				start=time(NULL);
509
				protocol(cfg.prot[i],XFER_UPLOAD,str,nulstr,true);
510 511 512 513 514 515
				end=time(NULL);
				if(!(cfg.dir[dirnum]->misc&DIR_ULTIME)) /* Don't deduct upload time */
					starttime+=end-start;
				ch=uploadfile(&f);
				autohangup();
				if(!ch)  /* upload failed, don't process user to user xfer */
516 517 518 519
					return(false); 
			} 
		} 
	}
520
	if(dirnum==cfg.user_dir) {  /* Add files to XFER.IXT in INDX dir */
521
		sprintf(str,"%sxfer.ixt",cfg.data_dir);
522 523
		if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) {
			errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_APPEND);
524 525
			return(false); 
		}
526 527 528 529 530 531
		for(j=0;j<destusers;j++) {
			for(i=1;i<=cfg.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],cfg.node_num,useron.alias);
532
					putnmsg(&cfg,i,str);
533 534 535
					break; 
				} 
			}
536 537
			if(i>cfg.sys_nodes) {   /* User not online */
				sprintf(str,text[UserSentYouFile],useron.alias);
538 539
				putsmsg(&cfg,destuser[j],str); 
			}
540 541
			sprintf(str,"%4.4u %12.12s %4.4u\r\n"
				,destuser[j],f.name,useron.number);
542 543
			write(file,str,strlen(str)); 
		}
544 545
		close(file); 
	}
546
	return(true);
547 548 549 550 551 552 553 554 555
}

/****************************************************************************/
/* 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.                                 */
/****************************************************************************/
bool sbbs_t::bulkupload(uint dirnum)
{
556 557 558
    char	str[MAX_PATH+1];
	char	path[MAX_PATH+1];
	char	spath[MAX_PATH+1];
559
    file_t	f;
560 561
	DIR*	dir;
	DIRENT*	dirent;
562 563 564 565 566 567 568 569 570

	memset(&f,0,sizeof(file_t));
	f.dir=dirnum;
	f.altpath=altul;
	bprintf(text[BulkUpload],cfg.lib[cfg.dir[dirnum]->lib]->sname,cfg.dir[dirnum]->sname);
	strcpy(path,altul>0 && altul<=cfg.altpaths ? cfg.altpath[altul-1]
		: cfg.dir[dirnum]->path);
	action=NODE_ULNG;
	SYNC;
571
	dir=opendir(path);
572
	while(dir!=NULL && (dirent=readdir(dir))!=NULL && !msgabort()) {
573 574 575
		sprintf(str,"%s%s",path,dirent->d_name);
		if(isdir(str))
			continue;
576 577 578 579 580
#ifdef _WIN32
		/* Skip hidden/system files on Win32 */
		if(getfattr(str)&(_A_HIDDEN|_A_SYSTEM))
			continue;
#endif
581 582 583 584 585 586
#ifdef _WIN32
		GetShortPathName(str,spath,sizeof(spath));
#else
		strcpy(spath,str);
#endif
		padfname(getfname(spath),str);
587

588
		if(findfile(&cfg,f.dir,str)==0) {
589
			f.misc=0;
590
			strcpy(f.name,str);
591
			f.cdt=(long)flength(spath);
592
			bprintf(text[BulkUploadDescPrompt],f.name,f.cdt/1024);
593 594 595
			getstr(f.desc,LEN_FDESC,K_LINE);
			if(sys_status&SS_ABORT)
				break;
596 597
			if(strcmp(f.desc,"-")==0)	/* don't add this file */
				continue;
598 599 600
			uploadfile(&f); 
		}
	}
601 602
	if(dir!=NULL)
		closedir(dir);
603 604
	if(sys_status&SS_ABORT)
		return(true);
605 606 607
	return(false);
}

608
bool sbbs_t::recvfile(char *fname, char prot)
609 610 611 612 613 614
{
	char	keys[32];
	char	ch;
	size_t	i;
	bool	result=false;

615 616 617 618 619
	if(prot)
		ch=toupper(prot);
	else {
		xfer_prot_menu(XFER_UPLOAD);
		mnemonics(text[ProtocolOrQuit]);
620
		sprintf(keys,"%c",text[YNQP][2]);
621
		for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
622
			if(cfg.prot[i]->ulcmd[0] && chk_ar(cfg.prot[i]->ar,&useron,&client))
623
				sprintf(keys+strlen(keys),"%c",cfg.prot[i]->mnemonic);
624

625
		ch=(char)getkeys(keys,0);
626

627
		if(ch==text[YNQP][2] || sys_status&SS_ABORT)
628 629
			return(false); 
	}
630
	for(i=0;i<cfg.total_prots;i++)
rswindell's avatar
rswindell committed
631
		if(cfg.prot[i]->mnemonic==ch && chk_ar(cfg.prot[i]->ar,&useron,&client))
632 633 634 635 636 637 638 639 640
			break;
	if(i<cfg.total_prots) {
		if(protocol(cfg.prot[i],XFER_UPLOAD,fname,fname,true)==0)
			result=true;
		autohangup(); 
	}

	return(result);
}