un_qwk.cpp 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
/* Synchronet QWK unpacking routine */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10
11
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
 *																			*
 * 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"
#include "qwk.h"

/****************************************************************************/
/* Unpacks .QWK packet, hubnum is the number of the QWK net hub 			*/
/****************************************************************************/
42
bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
43
{
44
	char	str[MAX_PATH+1],fname[MAX_PATH+1];
45
	char 	tmp[512];
46
	char	inbox[MAX_PATH+1];
47
	uchar	block[QWK_BLOCK_LEN];
48
49
	int 	k,file;
	uint	i,j,n,lastsub=INVALID_SUB;
rswindell's avatar
rswindell committed
50
	uint	usernum;
51
52
	uint	blocks;
	long	l,size,misc;
53
54
	ulong	t;
	ulong	msgs=0;
55
	ulong	tmsgs=0;
56
	time_t	start;
57
	time_t	startsub;
58
59
60
	DIR*	dir;
	DIRENT*	dirent;
	FILE*	qwk;
rswindell's avatar
rswindell committed
61
62
63
	FILE*	fp;
	smbmsg_t	msg;
	str_list_t	headers=NULL;
64
	str_list_t	voting=NULL;
rswindell's avatar
rswindell committed
65
66
67
68
	str_list_t	ip_can=NULL;
	str_list_t	host_can=NULL;
	str_list_t	subject_can=NULL;
	str_list_t	twit_list=NULL;
69
	const char* hostname;
70

rswindell's avatar
rswindell committed
71
	memset(&msg,0,sizeof(msg));
72

73
	start=time(NULL);
74
	if((l=(long)flength(packet))<1) {
75
		errormsg(WHERE,ERR_LEN,packet,l);
76
		return(false);
77
	}
78
	delfiles(cfg.temp_dir,ALLFILES);
79
	i=external(cmdstr(cfg.qhub[hubnum]->unpack,packet,ALLFILES,NULL),EX_OFFLINE);
80
	if(i) {
81
		errormsg(WHERE,ERR_EXEC,cmdstr(cfg.qhub[hubnum]->unpack,packet,ALLFILES,NULL),i);
82
83
		return(false); 
	}
84
	SAFEPRINTF(str,"%sMESSAGES.DAT",cfg.temp_dir);
85
	if(!fexistcase(str)) {
86
		lprintf(LOG_WARNING,"%s doesn't contain MESSAGES.DAT (%s)",packet,str);
87
88
		return(false); 
	}
89
90
	if((qwk=fnopen(&file,str,O_RDONLY))==NULL) {
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
91
92
		return(false); 
	}
93
	size=(long)filelength(file);
rswindell's avatar
rswindell committed
94

95
96
	SAFEPRINTF(fname,"%sHEADERS.DAT",cfg.temp_dir);
	if(fexistcase(fname)) {
97
		lprintf(LOG_DEBUG, "Reading %s", fname);
98
99
		if((fp=fopen(fname,"r")) == NULL)
			errormsg(WHERE,ERR_OPEN,fname,O_RDONLY);
rswindell's avatar
rswindell committed
100
101
102
103
		else {
			headers=iniReadFile(fp);
			fclose(fp);
		}
104
		remove(fname);
rswindell's avatar
rswindell committed
105
	}
106
107
	SAFEPRINTF(fname, "%sVOTING.DAT", cfg.temp_dir);
	if(fexistcase(fname)) {
108
		lprintf(LOG_DEBUG, "Reading %s", fname);
109
110
		if((fp=fopen(fname,"r")) == NULL)
			errormsg(WHERE,ERR_OPEN,fname,O_RDONLY);
111
112
113
114
		else {
			voting=iniReadFile(fp);
			fclose(fp);
		}
115
		remove(fname);
116
	}
rswindell's avatar
rswindell committed
117

118
119
120
	/********************/
	/* Process messages */
	/********************/
121
	eprintf(LOG_INFO,"Importing QWK Network Packet: %s",packet);
122

rswindell's avatar
rswindell committed
123
124
125
126
127
128
129
130
131
132
	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);
	if((fp=fopen(fname,"r"))!=NULL) {
		twit_list=strListReadFile(fp,NULL,128);
		fclose(fp);
	}

133
	for(l=QWK_BLOCK_LEN;l<size;l+=blocks*QWK_BLOCK_LEN) {
134
135
136
137
		if(terminated) {
			eprintf(LOG_NOTICE,"!Terminated");
			break;
		}
138
		fseek(qwk,l,SEEK_SET);
139
		fread(block,QWK_BLOCK_LEN,1,qwk);
140
		if(block[0]<' ' || block[0]&0x80) {
141
			eprintf(LOG_NOTICE,"!Invalid QWK message status (%02X) at offset %lu in %s"
142
143
144
145
146
				,block[0], l, packet);
			blocks=1;
			continue;
		}
		sprintf(tmp,"%.6s",block+116);
147
		blocks=atoi(tmp);  /* i = number of blocks */
148
		if(blocks<2) {
149
			if(block[0] == 'V' && blocks == 1 && voting != NULL) {	/* VOTING DATA */
150
				qwk_voting(&voting, l, NET_QWK, cfg.qhub[hubnum]->id, hubnum);
151
152
				continue;
			}
153
			eprintf(LOG_NOTICE,"!Invalid number of QWK blocks (%d) at offset %lu in %s"
154
155
156
157
158
159
160
161
162
				,blocks, l+116, packet);
			blocks=1;
			continue; 
		}
		/*********************************/
		/* public message on a sub-board */
		/*********************************/
		n=(uint)block[123]|(((uint)block[124])<<8);  /* conference number */

163
		qwk_new_msg(n, &msg,(char*)block,/* offset: */l,headers,/* parse_sender_hfields: */true);
rswindell's avatar
rswindell committed
164

165
166
167
168
169
170
171
172
		if(cfg.max_qwkmsgage && msg.hdr.when_written.time < (uint32_t)now
			&& (now-msg.hdr.when_written.time)/(24*60*60) > cfg.max_qwkmsgage) {
			eprintf(LOG_NOTICE,"!Filtering QWK message from %s due to age: %u days"
				,msg.from
				,(now-msg.hdr.when_written.time)/(24*60*60)); 
			continue;
		}

rswindell's avatar
rswindell committed
173
		if(findstr_in_list(msg.from_ip,ip_can)) {
174
			eprintf(LOG_NOTICE,"!Filtering QWK message from %s due to blocked IP: %s"
rswindell's avatar
rswindell committed
175
176
177
178
179
				,msg.from
				,msg.from_ip); 
			continue;
		}

180
181
		hostname=getHostNameByAddr(msg.from_host);
		if(findstr_in_list(hostname,host_can)) {
182
			eprintf(LOG_NOTICE,"!Filtering QWK message from %s due to blocked hostname: %s"
rswindell's avatar
rswindell committed
183
				,msg.from
184
				,hostname); 
rswindell's avatar
rswindell committed
185
186
187
188
			continue;
		}

		if(findstr_in_list(msg.subj,subject_can)) {
189
			eprintf(LOG_NOTICE,"!Filtering QWK message from %s due to filtered subject: %s"
rswindell's avatar
rswindell committed
190
191
192
193
194
				,msg.from
				,msg.subj); 
			continue;
		}

195
		if(!n) {		/* NETMAIL */
196
			eprintf(LOG_INFO,"QWK NetMail from %s to %s", cfg.qhub[hubnum]->id, msg.to);
rswindell's avatar
rswindell committed
197
			if(!stricmp(msg.to,"NETMAIL")) {  /* QWK to FidoNet NetMail */
198
				qwktonetmail(qwk,(char *)block,NULL,hubnum+1);
199
200
				continue; 
			}
rswindell's avatar
rswindell committed
201
202
			if(strchr(msg.to,'@')) {
				qwktonetmail(qwk,(char *)block,msg.to,hubnum+1);
203
204
				continue; 
			}
rswindell's avatar
rswindell committed
205
206
207
208
209
210
			usernum=atoi(msg.to);
			if(usernum && usernum>lastuser(&cfg))
				usernum=0;
			if(!usernum)
				usernum=matchuser(&cfg,msg.to,TRUE /* sysop_alias */);
			if(!usernum) {
211
				eprintf(LOG_NOTICE,"!QWK NetMail from %s to UNKNOWN USER: %s", cfg.qhub[hubnum]->id, msg.to);
212
213
				continue; 
			}
214

rswindell's avatar
rswindell committed
215
			getuserrec(&cfg,usernum,U_MISC,8,str);
216
217
			misc=ahtoul(str);
			if(misc&NETMAIL && cfg.sys_misc&SM_FWDTONET) {
rswindell's avatar
rswindell committed
218
				getuserrec(&cfg,usernum,U_NETMAIL,LEN_NETMAIL,str);
219
				qwktonetmail(qwk,(char*)block,str,hubnum+1);
220
221
				continue; 
			}
222
223

			smb_stack(&smb,SMB_STACK_PUSH);
224
			sprintf(smb.file,"%smail",cfg.data_dir);
225
			smb.retry_time=cfg.smb_retry_time;
226
			smb.subnum=INVALID_SUB;
227
228
229
			if((k=smb_open(&smb))!=0) {
				errormsg(WHERE,ERR_OPEN,smb.file,k,smb.last_error);
				smb_stack(&smb,SMB_STACK_POP);
230
231
				continue; 
			}
232
233
			if(!filelength(fileno(smb.shd_fp))) {
				smb.status.max_crcs=cfg.mail_maxcrcs;
234
				smb.status.max_msgs=0;
235
236
237
238
				smb.status.max_age=cfg.mail_maxage;
				smb.status.attr=SMB_EMAIL;
				if((k=smb_create(&smb))!=0) {
					smb_close(&smb);
239
					errormsg(WHERE,ERR_CREATE,smb.file,k,smb.last_error);
240
					smb_stack(&smb,SMB_STACK_POP);
241
242
243
					continue; 
				} 
			}
244
245
			if((k=smb_locksmbhdr(&smb))!=0) {
				smb_close(&smb);
246
				errormsg(WHERE,ERR_LOCK,smb.file,k,smb.last_error);
247
				smb_stack(&smb,SMB_STACK_POP);
248
249
				continue; 
			}
250
251
			if((k=smb_getstatus(&smb))!=0) {
				smb_close(&smb);
252
				errormsg(WHERE,ERR_READ,smb.file,k,smb.last_error);
253
				smb_stack(&smb,SMB_STACK_POP);
254
255
				continue; 
			}
256
			smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
257
258
259
			if(qwk_import_msg(qwk,(char *)block,blocks,hubnum+1,INVALID_SUB,usernum,&msg)) {
				SAFEPRINTF(str,text[UserSentYouMail],msg.from);
				putsmsg(&cfg,usernum,str);
260
				tmsgs++;
261
			}
262
263
264
			smb_close(&smb);
			smb_stack(&smb,SMB_STACK_POP);
			continue;
265
		}
266

267
		if((j = resolve_qwkconf(n, hubnum)) == INVALID_SUB) {	/* ignore messages for subs not in config */
268
			eprintf(LOG_NOTICE,"!Message from %s on UNKNOWN QWK CONFERENCE NUMBER: %u"
269
270
271
272
				,cfg.qhub[hubnum]->id, n);
			continue;
		}

273
		/* TWIT FILTER */
rswindell's avatar
rswindell committed
274
		if(findstr_in_list(msg.from,twit_list) || findstr_in_list(msg.to,twit_list)) {
275
			eprintf(LOG_NOTICE,"!Filtering QWK post from %s to %s on %s %s"
rswindell's avatar
rswindell committed
276
277
278
279
				,msg.from
				,msg.to
				,cfg.grp[cfg.sub[j]->grp]->sname,cfg.sub[j]->lname); 
			continue; 
280
281
		}

282
		if(j!=lastsub) {
283

284
			if(msgs) {
285
				t=(ulong)(time(NULL)-startsub);
286
287
				if(t<1)
					t=1;
288
				eprintf(LOG_INFO,"Imported %lu QWK msgs in %lu seconds (%lu msgs/sec)", msgs,t,msgs/t);
289
290
291
292
			}
			msgs=0;
			startsub=time(NULL);

293
			eprintf(LOG_INFO,"Importing QWK messages from %s into %s %s"
294
295
				,cfg.qhub[hubnum]->id, cfg.grp[cfg.sub[j]->grp]->sname,cfg.sub[j]->lname);

296
297
298
299
300
			if(lastsub!=INVALID_SUB)
				smb_close(&smb);
			lastsub=INVALID_SUB;
			sprintf(smb.file,"%s%s",cfg.sub[j]->data_dir,cfg.sub[j]->code);
			smb.retry_time=cfg.smb_retry_time;
301
			smb.subnum=j;
302
303
			if((k=smb_open(&smb))!=0) {
				errormsg(WHERE,ERR_OPEN,smb.file,k,smb.last_error);
304
305
				continue; 
			}
306
307
308
309
310
311
312
			if(!filelength(fileno(smb.shd_fp))) {
				smb.status.max_crcs=cfg.sub[j]->maxcrcs;
				smb.status.max_msgs=cfg.sub[j]->maxmsgs;
				smb.status.max_age=cfg.sub[j]->maxage;
				smb.status.attr=cfg.sub[j]->misc&SUB_HYPER ? SMB_HYPERALLOC :0;
				if((k=smb_create(&smb))!=0) {
					smb_close(&smb);
313
					errormsg(WHERE,ERR_CREATE,smb.file,k,smb.last_error);
314
315
316
					continue; 
				} 
			}
317
318
			if((k=smb_locksmbhdr(&smb))!=0) {
				smb_close(&smb);
319
				errormsg(WHERE,ERR_LOCK,smb.file,k,smb.last_error);
320
321
				continue; 
			}
322
323
			if((k=smb_getstatus(&smb))!=0) {
				smb_close(&smb);
324
				errormsg(WHERE,ERR_READ,smb.file,k,smb.last_error);
325
326
				continue; 
			}
327
			smb_unlocksmbhdr(&smb);
328
329
			lastsub=j; 
		}
330

rswindell's avatar
rswindell committed
331
332
333
334
		if(qwk_import_msg(qwk,(char *)block,blocks,hubnum+1,/*subnum: */j,/*touser: */0,&msg)) {
			signal_sub_sem(&cfg,j);
			msgs++;
			tmsgs++;
335
336
337
		}
	}

338
339
	qwk_handle_remaining_votes(&voting, NET_QWK, cfg.qhub[hubnum]->id, hubnum);

340
341
	update_qwkroute(NULL);		/* Write ROUTE.DAT */

rswindell's avatar
rswindell committed
342
	smb_freemsgmem(&msg);
343
	fclose(qwk);
rswindell's avatar
rswindell committed
344
345

	iniFreeStringList(headers);
346
	iniFreeStringList(voting);
347

rswindell's avatar
rswindell committed
348
349
350
351
352
	strListFree(&ip_can);
	strListFree(&host_can);
	strListFree(&subject_can);
	strListFree(&twit_list);

353
354
355
356
	if(lastsub!=INVALID_SUB)
		smb_close(&smb);

	delfiles(cfg.temp_dir,"*.NDX");
357
	SAFEPRINTF(str,"%sMESSAGES.DAT",cfg.temp_dir);
358
	removecase(str);
359
	SAFEPRINTF(str,"%sDOOR.ID",cfg.temp_dir);
360
	removecase(str);
361
	SAFEPRINTF(str,"%sCONTROL.DAT",cfg.temp_dir);
362
	removecase(str);
363
	SAFEPRINTF(str,"%sNETFLAGS.DAT",cfg.temp_dir);
364
365
366
	removecase(str);
	SAFEPRINTF(str,"%sTOREADER.EXT",cfg.temp_dir);
	removecase(str);
367

368
	dir=opendir(cfg.temp_dir);
369
	while(dir!=NULL && (dirent=readdir(dir))!=NULL) {
370
371
372
		sprintf(str,"%s%s",cfg.temp_dir,dirent->d_name);
		if(isdir(str))	/* sub-dir */
			continue;
373
374
375
376
377

		// Create directory if necessary
		sprintf(inbox,"%sqnet/%s.in",cfg.data_dir,cfg.qhub[hubnum]->id);
		MKDIR(inbox);

378
		// Copy files
379
		sprintf(fname,"%s/%s",inbox,dirent->d_name);
380
381
382
		mv(str,fname,1 /* overwrite */);
		sprintf(str,text[ReceivedFileViaQWK],dirent->d_name,cfg.qhub[hubnum]->id);
		putsmsg(&cfg,1,str);
383
		eprintf(LOG_INFO,"Received %s from %s", dirent->d_name, cfg.qhub[hubnum]->id);
384
	}
385
386
	if(dir!=NULL)
		closedir(dir);
387

388
	t=(ulong)(time(NULL)-start);
389
	if(tmsgs) {
390
391
		if(t<1)
			t=1;
392
		eprintf(LOG_INFO,"Finished Importing QWK Network Packet from %s: "
393
			"(%lu msgs) in %lu seconds (%lu msgs/sec)"
394
			,cfg.qhub[hubnum]->id, tmsgs, t, tmsgs/t);
395
396
397
		/* trigger timed event with internal code of 'qnet-qwk' to run */
		sprintf(str,"%sqnet-qwk.now",cfg.data_dir);
		ftouch(str);
398
	}
399
	delfiles(cfg.temp_dir,ALLFILES);
400
	return(true);
401
}