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

un_rep.cpp 18.2 KB
Newer Older
1 2 3 4 5 6
/* Synchronet QWK replay (REP) packet unpacking routine */

/****************************************************************************
 * @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 23
 *																			*
 * 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"
#include "qwk.h"
24
#include "filedat.h"
25 26

/****************************************************************************/
rswindell's avatar
rswindell committed
27
/* Unpacks .REP packet, 'repfile' is the path and filename of the packet    */
28 29 30
/****************************************************************************/
bool sbbs_t::unpack_rep(char* repfile)
{
31
	char	str[MAX_PATH+1],fname[MAX_PATH+1];
32 33
	char	rep_fname[MAX_PATH+1];
	char	msg_fname[MAX_PATH+1];
34
	char 	tmp[512];
35
	char	error[256];
36
	char	inbox[MAX_PATH+1];
37
	char	block[QWK_BLOCK_LEN];
38 39
	int 	file;
	uint	i,j,k,lastsub=INVALID_SUB;
rswindell's avatar
rswindell committed
40 41
	uint	blocks;
	uint	usernum;
42 43 44
	long	l,size,misc;
	ulong	n;
	ulong	ex;
45
	ulong	tmsgs = 0;
46
	ulong	dupes = 0;
rswindell's avatar
rswindell committed
47
	ulong	errors = 0;
48
	node_t	node;
49 50 51
	FILE*	rep;
	DIR*	dir;
	DIRENT*	dirent;
rswindell's avatar
rswindell committed
52 53
	smbmsg_t	msg;
	str_list_t	headers=NULL;
54
	str_list_t	voting=NULL;
rswindell's avatar
rswindell committed
55 56 57 58
	str_list_t	ip_can=NULL;
	str_list_t	host_can=NULL;
	str_list_t	subject_can=NULL;
	str_list_t	twit_list=NULL;
59
	link_list_t user_list={0};
60
	const char* AttemptedToUploadREPpacket="Attempted to upload REP packet";
61

rswindell's avatar
rswindell committed
62
	memset(&msg,0,sizeof(msg));
63

64 65
	if(repfile!=NULL) {
		delfiles(cfg.temp_dir,ALLFILES);
66
		SAFECOPY(rep_fname,repfile);
67
	} else
68 69
		SAFEPRINTF2(rep_fname,"%s%s.rep",cfg.temp_dir,cfg.sys_id);
	if(!fexistcase(rep_fname)) {
70
		bputs(text[QWKReplyNotReceived]);
71 72
		logline(LOG_NOTICE,"U!",AttemptedToUploadREPpacket);
		logline(LOG_NOTICE,nulstr,"REP file not received");
73 74
		return(false); 
	}
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	long file_count = extract_files_from_archive(rep_fname
		,/* outdir: */cfg.temp_dir
		,/* allowed_filename_chars: */SAFEST_FILENAME_CHARS
		,/* with_path: */false
		,/* max_files */1000
		,/* file_list: */NULL /* all files */
		,error, sizeof(error));
	if(file_count > 0) {
		lprintf(LOG_DEBUG, "libarchive extracted %lu files from %s", file_count, rep_fname);
	} else {
		if(*error)
			lprintf(LOG_NOTICE, "libarchive error (%s) extracting %s", error, rep_fname);
		for(k=0;k<cfg.total_fextrs;k++)
			if(!stricmp(cfg.fextr[k]->ext,useron.tmpext) && chk_ar(cfg.fextr[k]->ar,&useron,&client))
				break;
		if(k>=cfg.total_fextrs)
			k=0;
		ex=EX_STDOUT;
		if(online!=ON_REMOTE)
			ex|=EX_OFFLINE;
		i=external(cmdstr(cfg.fextr[k]->cmd,rep_fname,ALLFILES,NULL),ex);
		if(i) {
			bputs(text[QWKExtractionFailed]);
			logline(LOG_NOTICE,"U!",AttemptedToUploadREPpacket);
			logline(LOG_NOTICE,nulstr,"Extraction failed");
			return(false); 
		}
102
	}
103 104
	SAFEPRINTF2(msg_fname,"%s%s.msg",cfg.temp_dir,cfg.sys_id);
	if(!fexistcase(msg_fname)) {
105
		bputs(text[QWKReplyNotReceived]);
106 107
		logline(LOG_NOTICE,"U!",AttemptedToUploadREPpacket);
		logline(LOG_NOTICE,nulstr,"MSG file not received");
108 109
		return(false); 
	}
110 111
	if((rep=fnopen(&file,msg_fname,O_RDONLY))==NULL) {
		errormsg(WHERE,ERR_OPEN,msg_fname,O_RDONLY);
112 113
		return(false); 
	}
114
	size=(long)filelength(file);
rswindell's avatar
rswindell committed
115 116 117

	SAFEPRINTF(fname,"%sHEADERS.DAT",cfg.temp_dir);
	if(fexistcase(fname)) {
118
		lprintf(LOG_DEBUG, "Reading %s", fname);
rswindell's avatar
rswindell committed
119 120 121 122 123 124 125 126 127 128
		FILE* fp;
		set_qwk_flag(QWK_HEADERS);
		if((fp=fopen(fname,"r")) == NULL)
			errormsg(WHERE,ERR_OPEN,fname,0);
		else {
			headers=iniReadFile(fp);
			fclose(fp);
		}
		remove(fname);
	}
129 130 131 132 133
	SAFEPRINTF(fname,"%sVOTING.DAT",cfg.temp_dir);
	if(fexistcase(fname)) {
		if(useron.rest&FLAG('V'))
			bputs(text[R_Voting]);
		else {
134
			lprintf(LOG_DEBUG, "Reading %s", fname);
135 136 137 138 139 140 141
			FILE* fp;
			set_qwk_flag(QWK_VOTING);
			if((fp=fopen(fname,"r")) == NULL)
				errormsg(WHERE,ERR_OPEN,fname,0);
			else {
				voting=iniReadFile(fp);
				fclose(fp);
rswindell's avatar
rswindell committed
142 143 144 145
#ifdef _DEBUG
				for(uint u=0; voting[u]; u++)
					lprintf(LOG_DEBUG, "VOTING.DAT: %s", voting[u]);
#endif
146 147 148 149
			}
		}
		remove(fname);
	}
rswindell's avatar
rswindell committed
150

151
	fread(block,QWK_BLOCK_LEN,1,rep);
152
	if(strnicmp((char *)block,cfg.sys_id,strlen(cfg.sys_id))) {
153
		iniFreeStringList(headers);
154
		iniFreeStringList(voting);
155 156
		fclose(rep);
		bputs(text[QWKReplyNotReceived]);
157 158
		logline(LOG_NOTICE,"U!",AttemptedToUploadREPpacket);
		logline(LOG_NOTICE,nulstr,"Incorrect QWK BBS ID");
159 160 161 162 163
		return(false); 
	}
	/********************/
	/* Process messages */
	/********************/
164 165 166 167
	if(online == ON_REMOTE) {
		logline("U+","Uploaded REP packet");
		bputs(text[QWKUnpacking]);
	}
168

rswindell's avatar
rswindell committed
169 170 171 172 173
	ip_can=trashcan_list(&cfg,"ip");
	host_can=trashcan_list(&cfg,"host");
	subject_can=trashcan_list(&cfg,"subject");

	SAFEPRINTF(fname,"%stwitlist.cfg",cfg.ctrl_dir);
174
	twit_list = findstr_list(fname);
rswindell's avatar
rswindell committed
175

176
	now=time(NULL);
rswindell's avatar
rswindell committed
177
	for(l=QWK_BLOCK_LEN;l<size;l+=blocks*QWK_BLOCK_LEN) {
178 179 180 181 182
		if(terminated) {
			bprintf("!Terminated");
			break;
		}

183 184
		lncntr=0;					/* defeat pause */
		if(fseek(rep,l,SEEK_SET)!=0) {
185
			errormsg(WHERE,ERR_SEEK,msg_fname,l);
rswindell's avatar
rswindell committed
186
			errors++;
187 188
			break;
		}
189
		if(fread(block,1,QWK_BLOCK_LEN,rep)!=QWK_BLOCK_LEN) {
190
			errormsg(WHERE,ERR_READ,msg_fname,(long)ftell(rep));
rswindell's avatar
rswindell committed
191
			errors++;
192 193 194
			break;
		}
		sprintf(tmp,"%.6s",block+116);
rswindell's avatar
rswindell committed
195
		blocks=atoi(tmp);  /* i = number of blocks */
196
		long confnum = atol((char *)block+1);
rswindell's avatar
rswindell committed
197
		if(blocks<2) {
198
			if(block[0] == 'V' && blocks == 1 && voting != NULL) {	/* VOTING DATA */
199 200
				if(qwk_msg_filtered(&msg, ip_can, host_can, subject_can, twit_list))
					continue;
201 202
				if(!qwk_voting(&voting, l, (useron.rest&FLAG('Q')) ? NET_QWK : NET_NONE, /* QWKnet ID : */useron.alias, confnum)) {
					lprintf(LOG_WARNING, "QWK vote failure, offset %ld of %s", l, getfname(msg_fname));
rswindell's avatar
rswindell committed
203
					errors++;
204
				}
205 206
				continue;
			}
207 208 209
			lprintf(LOG_WARNING
				, "%s msg blocks less than 2 (read '%c' at offset %ld, '%s' at offset %ld)"
				, getfname(msg_fname), block[0], l, tmp, l + 116);
210 211
			if(l > QWK_BLOCK_LEN)
				errors++;
rswindell's avatar
rswindell committed
212
			blocks=1;
213
			continue;
214
		}
215

216 217 218 219
		if(!qwk_new_msg(confnum, &msg, block, /* offset: */l, headers, /* parse_sender_hfields: */useron.rest&FLAG('Q') ? true:false)) {
			errors++;
			continue;
		}
rswindell's avatar
rswindell committed
220

221
		if(qwk_msg_filtered(&msg, ip_can, host_can, subject_can))
rswindell's avatar
rswindell committed
222 223
			continue;

224
		if(confnum == 0) {						/* E-mail */
225 226 227 228
			if(msg.from == NULL)
				bprintf("E-mail to %s: %s\r\n", msg.to, msg.subj);
			else
				bprintf("E-mail from %s to %s\r\n", msg.from, msg.to);
229 230 231
			if(useron.rest&FLAG('E')) {
				bputs(text[R_Email]);
				continue;
232
			}
233

234 235 236 237 238 239 240 241 242 243 244 245 246
			if(msg.to!=NULL) {
				if(stricmp(msg.to,"NETMAIL")==0) {  /* QWK to FidoNet NetMail */
					qwktonetmail(rep,block,NULL,0);
					continue; 
				}
				if(strchr(msg.to,'@')) {
					qwktonetmail(rep,block,msg.to,0);
					continue; 
				}
				if(!stricmp(msg.to,"SBBS")) {    /* to SBBS, config stuff */
					qwkcfgline(msg.subj,INVALID_SUB);
					continue; 
				}
247
			}
248

249
			if(useron.etoday>=cfg.level_emailperday[useron.level] && !(useron.exempt&FLAG('M'))
250 251
				&& !(useron.rest&FLAG('Q'))) {
				bputs(text[TooManyEmailsToday]);
252 253
				continue; 
			}
254 255 256 257 258 259 260 261
			usernum=0;
			if(msg.to!=NULL) {
				usernum=atoi(msg.to);
				if(usernum>lastuser(&cfg))
					usernum=0;
				if(!usernum)
					usernum=matchuser(&cfg,msg.to,TRUE /* sysop_alias */);
			}
rswindell's avatar
rswindell committed
262
			if(!usernum) {
263
				bputs(text[UnknownUser]);
264 265
				continue; 
			}
rswindell's avatar
rswindell committed
266
			if(usernum==1 && useron.rest&FLAG('S')) {
267
				bprintf(text[R_Feedback],cfg.sys_op);
268 269
				continue; 
			}
270

rswindell's avatar
rswindell committed
271
			getuserrec(&cfg,usernum,U_MISC,8,str);
272 273
			misc=ahtoul(str);
			if(misc&NETMAIL && cfg.sys_misc&SM_FWDTONET) {
rswindell's avatar
rswindell committed
274
				getuserrec(&cfg,usernum,U_NETMAIL,LEN_NETMAIL,str);
275
				qwktonetmail(rep,block,str,0);
276 277
				continue; 
			}
278

279
			SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
280 281 282 283
			smb.retry_time=cfg.smb_retry_time;

			if(lastsub!=INVALID_SUB) {
				smb_close(&smb);
284 285
				lastsub=INVALID_SUB; 
			}
286

287
			smb.subnum=INVALID_SUB;
288 289
			if((k=smb_open(&smb))!=0) {
				errormsg(WHERE,ERR_OPEN,smb.file,k,smb.last_error);
rswindell's avatar
rswindell committed
290
				errors++;
291 292
				continue; 
			}
293 294 295

			if(!filelength(fileno(smb.shd_fp))) {
				smb.status.max_crcs=cfg.mail_maxcrcs;
296
				smb.status.max_msgs=0;
297 298 299 300
				smb.status.max_age=cfg.mail_maxage;
				smb.status.attr=SMB_EMAIL;
				if((k=smb_create(&smb))!=0) {
					smb_close(&smb);
301
					errormsg(WHERE,ERR_CREATE,smb.file,k,smb.last_error);
rswindell's avatar
rswindell committed
302
					errors++;
303 304 305
					continue; 
				} 
			}
306 307 308

			if((k=smb_locksmbhdr(&smb))!=0) {
				smb_close(&smb);
309
				errormsg(WHERE,ERR_LOCK,smb.file,k,smb.last_error);
rswindell's avatar
rswindell committed
310
				errors++;
311 312
				continue; 
			}
313 314 315

			if((k=smb_getstatus(&smb))!=0) {
				smb_close(&smb);
316
				errormsg(WHERE,ERR_READ,smb.file,k,smb.last_error);
rswindell's avatar
rswindell committed
317
				errors++;
318 319
				continue; 
			}
320 321 322

			smb_unlocksmbhdr(&smb);

323
			bool dupe = false;
rswindell's avatar
rswindell committed
324
			if(qwk_import_msg(rep, block, blocks
325
				,/* fromhub: */0, &smb, /* touser: */usernum, &msg, &dupe)) {
rswindell's avatar
rswindell committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
				if(usernum==1) {
					useron.fbacks++;
					logon_fbacks++;
					putuserrec(&cfg,useron.number,U_FBACKS,5
						,ultoa(useron.fbacks,tmp,10)); 
				}
				else {
					useron.emails++;
					logon_emails++;
					putuserrec(&cfg,useron.number,U_EMAILS,5
						,ultoa(useron.emails,tmp,10)); 
				}
				useron.etoday++;
				putuserrec(&cfg,useron.number,U_ETODAY,5
					,ultoa(useron.etoday,tmp,10));
341
				bprintf(P_REMOTE, text[Emailed],username(&cfg,usernum,tmp),usernum);
342 343
				SAFEPRINTF2(str,"sent QWK e-mail to %s #%d"
					,username(&cfg,usernum,tmp),usernum);
rswindell's avatar
rswindell committed
344
				logline("E+",str);
345 346 347 348 349 350 351 352 353 354 355 356
				if(cfg.node_num) {
					for(k=1;k<=cfg.sys_nodes;k++) { /* Tell user, if online */
						getnodedat(k,&node,0);
						if(node.useron==usernum && !(node.misc&NODE_POFF)
							&& (node.status==NODE_INUSE
							|| node.status==NODE_QUIET)) {
							SAFEPRINTF2(str,text[EmailNodeMsg]
								,cfg.node_num,msg.from);
							putnmsg(&cfg,k,str);
							break; 
						} 
					}
rswindell's avatar
rswindell committed
357
				}
358
				if(cfg.node_num==0 || k>cfg.sys_nodes) {
rswindell's avatar
rswindell committed
359 360
					SAFEPRINTF(str,text[UserSentYouMail],msg.from);
					putsmsg(&cfg,usernum,str); 
361
				} 
362
				tmsgs++;
363 364 365 366 367 368
			} else {
				if(dupe)
					dupes++;
				else
					errors++;
			}
rswindell's avatar
rswindell committed
369
			smb_close(&smb);
370
		}    /* end of email */
371 372 373 374

				/**************************/
		else {	/* message on a sub-board */
				/**************************/
375
			if((n=resolve_qwkconf(confnum))==INVALID_SUB) {
376
				bprintf(P_REMOTE, text[QWKInvalidConferenceN],confnum);
377
				SAFEPRINTF(str,"Invalid QWK conference number %ld", confnum);
378
				logline(LOG_NOTICE,"P!",str);
rswindell's avatar
rswindell committed
379
				errors++;
380
				continue; 
381
			}
382 383 384

			/* if posting, add to new-scan config for QWKnet nodes automatically */
			if(useron.rest&FLAG('Q'))
385
				subscan[n].cfg|=SUB_CFG_NSCAN;
386

387 388 389 390 391
			if(msg.to!=NULL) {
				if(stricmp(msg.to,"SBBS")==0) {	/* to SBBS, config stuff */
					qwkcfgline(msg.subj,n);
					continue; 
				}
392
			}
393

rswindell's avatar
rswindell committed
394
#if 0	/* This stuff isn't really necessary anymore */
395 396 397 398 399 400
			if(!SYSOP && cfg.sub[n]->misc&SUB_QNET) {	/* QWK Netted */
				sprintf(str,"%-25.25s","DROP");         /* Drop from new-scan? */
				if(!strnicmp((char *)block+71,str,25))	/* don't allow post */
					continue;
				sprintf(str,"%-25.25s","ADD");          /* Add to new-scan? */
				if(!strnicmp((char *)block+71,str,25))	/* don't allow post */
401 402
					continue; 
			}
rswindell's avatar
rswindell committed
403
#endif
404 405 406

			if(useron.rest&FLAG('Q') && !(cfg.sub[n]->misc&SUB_QNET)) {
				bputs(text[CantPostOnSub]);
407
				logline(LOG_NOTICE,"P!","Attempted to post QWK message on non-QWKnet sub");
408 409
				continue; 
			}
410 411 412

			if(useron.rest&FLAG('P')) {
				bputs(text[R_Post]);
413
				logline(LOG_NOTICE,"P!","QWK Post attempted");
414 415
				continue; 
			}
416 417 418 419

			if(useron.ptoday>=cfg.level_postsperday[useron.level]
				&& !(useron.rest&FLAG('Q'))) {
				bputs(text[TooManyPostsToday]);
420 421
				continue; 
			}
422 423 424 425

			if(useron.rest&FLAG('N')
				&& cfg.sub[n]->misc&(SUB_FIDO|SUB_PNET|SUB_QNET|SUB_INET)) {
				bputs(text[CantPostOnSub]);
426
				logline(LOG_NOTICE,"P!","QWK Networked post attempted");
427 428
				continue; 
			}
429

rswindell's avatar
rswindell committed
430
			if(!chk_ar(cfg.sub[n]->post_ar,&useron,&client)) {
431
				bputs(text[CantPostOnSub]);
432
				logline(LOG_NOTICE,"P!","QWK Post attempted");
433 434
				continue; 
			}
435 436 437 438

			if((block[0]=='*' || block[0]=='+')
				&& !(cfg.sub[n]->misc&SUB_PRIV)) {
				bputs(text[PrivatePostsNotAllowed]);
439
				logline(LOG_NOTICE,"P!","QWK Private post attempt");
440 441
				continue; 
			}
442 443 444

			if(block[0]=='*' || block[0]=='+'           /* Private post */
				|| cfg.sub[n]->misc&SUB_PONLY) {
445 446 447
				if(msg.to==NULL || !msg.to[0]
					|| stricmp(msg.to,"All")==0) {		/* to blank */
					bputs(text[NoToUser]);				/* or all */
448 449 450
					continue; 
				} 
			}
451

rswindell's avatar
rswindell committed
452
#if 0	/* This stuff isn't really necessary anymore */
453 454 455 456
			if(!SYSOP && !(useron.rest&FLAG('Q'))) {
				sprintf(str,"%-25.25s","SYSOP");
				if(!strnicmp((char *)block+21,str,25)) {
					sprintf(str,"%-25.25s",username(&cfg,1,tmp));
457 458 459
					memcpy((char *)block+21,str,25);		/* change from sysop */
				}											/* to user name */
			}
rswindell's avatar
rswindell committed
460
#endif
461

462
			/* TWIT FILTER */
463 464
			if(qwk_msg_filtered(&msg, /* ip_can: */NULL, /* host_can: */NULL, /* subject_can: */NULL, twit_list))
				continue;
465 466 467 468 469

			if(n!=lastsub) {
				if(lastsub!=INVALID_SUB)
					smb_close(&smb);
				lastsub=INVALID_SUB;
470
				SAFEPRINTF2(smb.file,"%s%s",cfg.sub[n]->data_dir,cfg.sub[n]->code);
471
				smb.retry_time=cfg.smb_retry_time;
472
				smb.subnum=n;
473 474
				if((j=smb_open(&smb))!=0) {
					errormsg(WHERE,ERR_OPEN,smb.file,j,smb.last_error);
rswindell's avatar
rswindell committed
475
					errors++;
476 477
					continue; 
				}
478 479 480 481 482 483 484 485 486

				if(!filelength(fileno(smb.shd_fp))) {
					smb.status.max_crcs=cfg.sub[n]->maxcrcs;
					smb.status.max_msgs=cfg.sub[n]->maxmsgs;
					smb.status.max_age=cfg.sub[n]->maxage;
					smb.status.attr=cfg.sub[n]->misc&SUB_HYPER ? SMB_HYPERALLOC:0;
					if((j=smb_create(&smb))!=0) {
						smb_close(&smb);
						lastsub=INVALID_SUB;
487
						errormsg(WHERE,ERR_CREATE,smb.file,j,smb.last_error);
rswindell's avatar
rswindell committed
488
						errors++;
489 490 491
						continue; 
					} 
				}
492 493 494 495

				if((j=smb_locksmbhdr(&smb))!=0) {
					smb_close(&smb);
					lastsub=INVALID_SUB;
496
					errormsg(WHERE,ERR_LOCK,smb.file,j,smb.last_error);
rswindell's avatar
rswindell committed
497
					errors++;
498 499
					continue; 
				}
500 501 502
				if((j=smb_getstatus(&smb))!=0) {
					smb_close(&smb);
					lastsub=INVALID_SUB;
503
					errormsg(WHERE,ERR_READ,smb.file,j,smb.last_error);
rswindell's avatar
rswindell committed
504
					errors++;
505 506
					continue; 
				}
507
				smb_unlocksmbhdr(&smb);
508 509
				lastsub=n; 
			}
510

511
			bool dupe = false;
rswindell's avatar
rswindell committed
512
			if(qwk_import_msg(rep, block, blocks
513
				,/* fromhub: */0, &smb, /* touser: */0, &msg, &dupe)) {
rswindell's avatar
rswindell committed
514 515
				logon_posts++;
				user_posted_msg(&cfg, &useron, 1);
516 517
				bprintf(P_REMOTE, text[Posted],cfg.grp[cfg.sub[n]->grp]->sname
					,cfg.sub[n]->lname);
518 519
				SAFEPRINTF2(str,"posted QWK message on %s %s"
					,cfg.grp[cfg.sub[n]->grp]->sname,cfg.sub[n]->lname);
rswindell's avatar
rswindell committed
520
				signal_sub_sem(&cfg,n);
521 522 523 524 525
				logline("P+",str);
				int destuser = lookup_user(&cfg, &user_list, msg.to);
				if(destuser > 0) {
					SAFEPRINTF4(str, text[MsgPostedToYouVia]
						,msg.from
526 527
						,(useron.rest&FLAG('Q')) ? useron.alias : "QWK"
						,cfg.grp[cfg.sub[n]->grp]->sname, cfg.sub[n]->lname);
528 529
					putsmsg(&cfg, destuser, str);
				}
rswindell's avatar
rswindell committed
530 531
				if(!(useron.rest&FLAG('Q')))
					user_event(EVENT_POST);
532
				tmsgs++;
533 534 535 536 537 538
			} else {
				if(dupe)
					dupes++;
				else
					errors++;
			}
539 540
		}   /* end of public message */
	}
541

542 543
	qwk_handle_remaining_votes(&voting, (useron.rest&FLAG('Q')) ? NET_QWK : NET_NONE, /* QWKnet ID : */useron.alias);

544 545
	update_qwkroute(NULL);			/* Write ROUTE.DAT */

546 547
	smb_freemsgmem(&msg);

rswindell's avatar
rswindell committed
548
	iniFreeStringList(headers);
549
	iniFreeStringList(voting);
550

rswindell's avatar
rswindell committed
551 552 553 554
	strListFree(&ip_can);
	strListFree(&host_can);
	strListFree(&subject_can);
	strListFree(&twit_list);
555
	listFree(&user_list);
rswindell's avatar
rswindell committed
556

557 558 559 560
	if(lastsub!=INVALID_SUB)
		smb_close(&smb);
	fclose(rep);

561 562 563
	/* QWKE support */
	SAFEPRINTF(fname,"%sTODOOR.EXT",cfg.temp_dir);
	if(fexistcase(fname)) {
564
		set_qwk_flag(QWK_EXT);
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
		FILE* fp=fopen(fname,"r");
		char* p;
		if(fp!=NULL) {
			while(!feof(fp)) {
				if(!fgets(str,sizeof(str)-1,fp))
					break;
				if(strnicmp(str,"AREA ",5)==0) {
					p=str+5;
					SKIP_WHITESPACE(p);
					if((n=resolve_qwkconf(atoi(p))) != INVALID_SUB) {
						FIND_WHITESPACE(p);
						SKIP_WHITESPACE(p);
						if(strchr(p,'D'))
							subscan[n].cfg&=~SUB_CFG_NSCAN;
						else if(strchr(p,'a') || strchr(p,'g'))
							subscan[n].cfg|=SUB_CFG_NSCAN;
						else if(strchr(p,'p'))
							subscan[n].cfg|=SUB_CFG_NSCAN|SUB_CFG_YSCAN;
					}
					continue;
				}
				if(strnicmp(str,"RESET ",6)==0) {
					p=str+6;
					SKIP_WHITESPACE(p);
					if((n=resolve_qwkconf(atoi(p))) != INVALID_SUB) {
						FIND_WHITESPACE(p);
						SKIP_WHITESPACE(p);
						/* If the [#ofmessages] is blank then the pointer should be set back to the start of the message base */
						if(*p==0)
							subscan[n].ptr=0;
						else {
							/* otherwise it should be set back [#ofmessages] back from the end of the message base. */
							uint32_t last=0;
							getlastmsg(n,&last,/* time_t* */NULL);
							l=last-atol(p);
							if(l<0)
								l=0;
							subscan[n].ptr=l;
						}
					}
				}
			}
			fclose(fp);
		}
	}

611
	if(useron.rest&FLAG('Q')) {             /* QWK Net Node */
612 613 614 615 616
		if(fexistcase(msg_fname))
			remove(msg_fname);
		SAFEPRINTF(fname,"%sATTXREF.DAT",cfg.temp_dir);
		if(fexistcase(fname))
			remove(fname);
617

618
		dir=opendir(cfg.temp_dir);
619
		while(dir!=NULL && (dirent=readdir(dir))!=NULL) {				/* Extra files */
620
			// Move files
621
			SAFEPRINTF2(str,"%s%s",cfg.temp_dir,dirent->d_name);
622 623
			if(isdir(str))
				continue;
624

625 626 627 628 629
			if(::trashcan(&cfg, dirent->d_name, "file")) {
				lprintf(LOG_NOTICE, "Ignored blocked filename: %s", dirent->d_name);
				continue;
			}

630
			// Create directory if necessary
631
			SAFEPRINTF2(inbox,"%sqnet/%s.in",cfg.data_dir,useron.alias);
632
			(void)MKDIR(inbox);
633

634
			SAFEPRINTF2(fname,"%s/%s",inbox,dirent->d_name);
635
			mv(str,fname,1);
636
			SAFEPRINTF2(str,text[ReceivedFileViaQWK],dirent->d_name,useron.alias);
637
			putsmsg(&cfg,1,str);
638 639
			lprintf(LOG_NOTICE, "Received file: %s", dirent->d_name);
		}
640 641
		if(dir!=NULL)
			closedir(dir);
642 643
		SAFEPRINTF(fname,"%sqnet-rep.now",cfg.data_dir);
		ftouch(fname);
644 645
	}

646 647 648 649 650 651 652 653
	if(online == ON_REMOTE) {
		bputs(text[QWKUnpacked]);
		CRLF;
		/**********************************************/
		/* Hang-up now if that's what the user wanted */
		/**********************************************/
		autohangup();
	} else
654
		lprintf(LOG_INFO, "Unpacking completed: %s (%lu msgs, %lu errors, %lu dupes)", rep_fname, tmsgs, errors, dupes);
655

rswindell's avatar
rswindell committed
656
	return errors == 0;
657
}