sbbsecho.c 152 KB
Newer Older
1
/* sbbsecho.c */
2

3
/* Synchronet FidoNet EchoMail Scanning/Tossing and NetMail Tossing Utility */
4

5
6
7
8
9
10
/* $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
 *																			*
 * 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.	*
 ****************************************************************************/
37

38
/* Portions written by Allen Christiansen 1994-1996 						*/
39
40
41
42
43
44
45
46
47

#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
48
#include <sys/stat.h>
49

rswindell's avatar
rswindell committed
50
#include "conwrap.h"		/* getch() */
51
52
53
54
55
56
#include "sbbs.h"			/* load_cfg() */
#include "sbbsdefs.h"
#include "smblib.h"
#include "scfglib.h"
#include "lzh.h"
#include "sbbsecho.h"
deuce's avatar
deuce committed
57
#include "genwrap.h"		/* PLATFORM_DESC */
rswindell's avatar
rswindell committed
58
59
60
#include "xpendian.h"

#define MAX_OPEN_SMBS	10
61
62

smb_t *smb,*email;
rswindell's avatar
rswindell committed
63
64
65
66
67
68
69
70
71
72
73
bool opt_import_packets		= true;
bool opt_import_netmail		= true;
bool opt_import_echomail	= true;
bool opt_export_echomail	= true;
bool opt_export_netmail		= true;
bool opt_pack_netmail		= true;
bool opt_gen_notify_list	= false;
bool opt_export_ftn_echomail= false;	/* Export msgs previously imported from FidoNet */
bool opt_update_msgptrs		= false;
bool opt_ignore_msgptrs		= false;
bool opt_leave_msgptrs		= false;
74
bool opt_dump_area_file		= false;
rswindell's avatar
rswindell committed
75

rswindell's avatar
rswindell committed
76
77
78
79
80
81
82
/* statistics */
ulong netmail=0;	/* imported */
ulong echomail=0;	/* imported */
ulong exported_netmail=0;
ulong exported_echomail=0;
ulong packed_netmail=0;

rswindell's avatar
rswindell committed
83
int cur_smb=0;
84
FILE *fidologfile=NULL;
rswindell's avatar
rswindell committed
85
bool twit_list;
86

rswindell's avatar
rswindell committed
87
88
89
90
91
fidoaddr_t		sys_faddr = {1,1,1,0};		/* Default system address: 1:1/1.0 */
sbbsecho_cfg_t	cfg;
scfg_t			scfg;
char			revision[16];
char			compiler[32];
92

rswindell's avatar
rswindell committed
93
94
95
96
bool pause_on_exit=false;
bool pause_on_abend=false;
bool mtxfile_locked=false;
bool terminated=false;
97

98
99
str_list_t	locked_bso_nodes;

rswindell's avatar
rswindell committed
100
101
102
int mv(const char *insrc, const char *indest, bool copy);
void export_echomail(const char *sub_code, const nodecfg_t*, bool rescan);

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const char default_domain[] = "fidonet";

const char* zone_domain(uint16_t zone)
{
	struct zone_mapping *i;

	if (!cfg.use_ftn_domains)
		return default_domain;

	for (i=cfg.zone_map; i; i=i->next)
		if (i->zone == zone)
			return i->domain;

	return default_domain;
}

const char* zone_root_outbound(uint16_t zone)
{
	struct zone_mapping *i;

	if (!cfg.use_ftn_domains)
		return cfg.outbound;

	for (i=cfg.zone_map; i; i=i->next)
		if (i->zone == zone)
			return i->root;

	return cfg.outbound;
}

133
134
135
136
137
138
139
140
141
142
143
/* FTN-compliant "Program Identifier"/PID (also used as a "Tosser Identifier"/TID) */
const char* sbbsecho_pid(void)
{
	static char str[256];
	
	sprintf(str, "SBBSecho %u.%02u-%s r%s %s %s"
		,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,revision,__DATE__,compiler);

	return str;
}

rswindell's avatar
rswindell committed
144
145
/* for *.bsy file contents: */
const char* program_id(void)
146
{
rswindell's avatar
rswindell committed
147
148
149
	static char str[256];
	
	SAFEPRINTF2(str, "%u %s", getpid(), sbbsecho_pid());
150

rswindell's avatar
rswindell committed
151
	return str;
152
153
}

rswindell's avatar
rswindell committed
154
155
156
157
/**********************/
/* Log print function */
/**********************/
int lprintf(int level, char *fmt, ...)
rswindell's avatar
rswindell committed
158
159
{
	va_list argptr;
rswindell's avatar
rswindell committed
160
	char sbuf[1024];
rswindell's avatar
rswindell committed
161
162
	int chcount;

rswindell's avatar
rswindell committed
163
164
	va_start(argptr,fmt);
	chcount=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
165
	sbuf[sizeof(sbuf)-1]=0;
rswindell's avatar
rswindell committed
166
	va_end(argptr);
167
168
	truncsp(sbuf);
	printf("%s\n",sbuf);
169

rswindell's avatar
rswindell committed
170
171
172
173
	if(fidologfile!=NULL && level<=cfg.log_level) {
	    time_t now = time(NULL);
		struct tm *tm;
		struct tm tmbuf = {0};
174
		char timestamp[128];
rswindell's avatar
rswindell committed
175
176
177
		strip_ctrl(sbuf, sbuf);
		if((tm = localtime(&now)) == NULL)
			tm = &tmbuf;
178
179
180
181
182
		if(strftime(timestamp, sizeof(timestamp), cfg.logtime, tm) <= 0)
			snprintf(timestamp, sizeof(timestamp)-1, "%u-%02u-%02u %02u:%02u:%02u"
				,1900+tm->tm_year, tm->tm_mon+1, tm->tm_mday
				,tm->tm_hour, tm->tm_min, tm->tm_sec);
		fprintf(fidologfile, "%s %s\n", timestamp, sbuf);
rswindell's avatar
rswindell committed
183
184
		fflush(fidologfile);
	}
rswindell's avatar
rswindell committed
185
186
187
	return(chcount);
}

rswindell's avatar
rswindell committed
188
bool delfile(const char *filename, int line)
189
{
rswindell's avatar
rswindell committed
190
	lprintf(LOG_DEBUG, "Deleting %s (from line %u)", filename, line);
191
	if(remove(filename) != 0) {
192
193
		lprintf(LOG_ERR, "ERROR %u (%s) line %u removing file %s"
			,errno, strerror(errno), line, filename);
rswindell's avatar
rswindell committed
194
		return false;
195
	}
rswindell's avatar
rswindell committed
196
	return true;
197
198
}

199
200
201
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments             */
/*****************************************************************************/
rswindell's avatar
rswindell committed
202
char *mycmdstr(scfg_t* cfg, const char *instr, const char *fpath, const char *fspec)
203
{
204
    static char cmd[MAX_PATH+1];
205
206
207
    char str[256],str2[128];
    int i,j,len;

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
	len=strlen(instr);
	for(i=j=0;i<len && j<128;i++) {
		if(instr[i]=='%') {
			i++;
			cmd[j]=0;
			switch(toupper(instr[i])) {
				case 'F':   /* File path */
					strcat(cmd,fpath);
					break;
				case 'G':   /* Temp directory */
					if(cfg->temp_dir[0]!='\\' 
						&& cfg->temp_dir[0]!='/' 
						&& cfg->temp_dir[1]!=':') {
						strcpy(str,cfg->node_dir);
						strcat(str,cfg->temp_dir);
						if(FULLPATH(str2,str,40))
							strcpy(str,str2);
						backslash(str);
						strcat(cmd,str);}
					else
						strcat(cmd,cfg->temp_dir);
					break;
				case 'J':
					if(cfg->data_dir[0]!='\\' 
						&& cfg->data_dir[0]!='/' 
						&& cfg->data_dir[1]!=':') {
						strcpy(str,cfg->node_dir);
						strcat(str,cfg->data_dir);
						if(FULLPATH(str2,str,40))
							strcpy(str,str2);
						backslash(str);
239
240
						strcat(cmd,str); 
					}
241
242
243
244
245
246
247
248
249
250
251
252
					else
						strcat(cmd,cfg->data_dir);
					break;
				case 'K':
					if(cfg->ctrl_dir[0]!='\\' 
						&& cfg->ctrl_dir[0]!='/' 
						&& cfg->ctrl_dir[1]!=':') {
						strcpy(str,cfg->node_dir);
						strcat(str,cfg->ctrl_dir);
						if(FULLPATH(str2,str,40))
							strcpy(str,str2);
						backslash(str);
253
254
						strcat(cmd,str); 
					}
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
					else
						strcat(cmd,cfg->ctrl_dir);
					break;
				case 'N':   /* Node Directory (same as SBBSNODE environment var) */
					strcat(cmd,cfg->node_dir);
					break;
				case 'O':   /* SysOp */
					strcat(cmd,cfg->sys_op);
					break;
				case 'Q':   /* QWK ID */
					strcat(cmd,cfg->sys_id);
					break;
				case 'S':   /* File Spec */
					strcat(cmd,fspec);
					break;
				case '!':   /* EXEC Directory */
271
					strcat(cmd,cfg->exec_dir);
272
					break;
273
274
275
276
277
                case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
                    strcat(cmd,cfg->exec_dir);
#endif
                    break;
278
279
280
281
282
283
284
285
286
287
288
				case '#':   /* Node number (same as SBBSNNUM environment var) */
					sprintf(str,"%d",cfg->node_num);
					strcat(cmd,str);
					break;
				case '*':
					sprintf(str,"%03d",cfg->node_num);
					strcat(cmd,str);
					break;
				case '%':   /* %% for percent sign */
					strcat(cmd,"%");
					break;
289
290
291
292
293
294
295
296
297
298
				case '.':	/* .exe for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
					strcat(cmd,".exe");
#endif
					break;
				case '?':	/* Platform */
					strcpy(str,PLATFORM_DESC);
					strlwr(str);
					strcat(cmd,str);
					break;
299
				default:    /* unknown specification */
300
					lprintf(LOG_ERR,"ERROR Checking Command Line '%s'",instr);
301
					bail(1);
302
303
304
305
					break; 
			}
			j=strlen(cmd); 
		}
306
		else
307
308
			cmd[j++]=instr[i]; 
}
309
	cmd[j]=0;
310

311
	return(cmd);
312
313
314
}

/****************************************************************************/
rswindell's avatar
rswindell committed
315
/* Runs an external program directly using system()							*/
316
317
318
/****************************************************************************/
int execute(char *cmdline)
{
rswindell's avatar
rswindell committed
319
320
321
322
323
324
	int retval;
	
	lprintf(LOG_DEBUG, "Executing: %s", cmdline);
	if((retval = system(cmdline)) != 0)
		lprintf(LOG_ERR,"ERROR executing '%s' system returned: %d, errno: %d (%s)"
			,cmdline, retval, errno, strerror(errno));
325

rswindell's avatar
rswindell committed
326
	return retval;
327
}
328

329
330
331
/******************************************************************************
 Returns the system address with the same zone as the address passed
******************************************************************************/
rswindell's avatar
rswindell committed
332
fidoaddr_t getsysfaddr(short zone)
333
334
{
	int i;
335

336
337
338
	for(i=0;i<scfg.total_faddrs;i++)
		if(scfg.faddr[i].zone==zone)
			return(scfg.faddr[i]);
339
	return(sys_faddr);
340
}
341

rswindell's avatar
rswindell committed
342
int get_outbound(fidoaddr_t dest, char* outbound, size_t maxlen, bool fileboxes)
343
{
rswindell's avatar
rswindell committed
344
345
	nodecfg_t*	nodecfg;

346
	strncpy(outbound,zone_root_outbound(dest.zone),maxlen);
rswindell's avatar
rswindell committed
347
348
349
350
351
352
	if(fileboxes &&
		(nodecfg = findnodecfg(&cfg, dest, /* exact */true)) != NULL
		&& nodecfg->outbox[0])
		strncpy(outbound, nodecfg->outbox, maxlen);
	else if(cfg.flo_mailer) {
		if(dest.zone != sys_faddr.zone)	{ /* Inter-zone outbound is "OUTBOUND.ZZZ/" */
353
354
355
			char* p = lastchar(outbound);
			if(IS_PATH_DELIM(*p))
				*p = 0;
rswindell's avatar
rswindell committed
356
357
358
359
360
361
			safe_snprintf(outbound+strlen(outbound), maxlen,".%03x", dest.zone);
		}
		if(dest.point != 0) {			/* Point destination is "OUTBOUND[.ZZZ]/NNNNnnnn.pnt/" */
			backslash(outbound);
			safe_snprintf(outbound+strlen(outbound), maxlen, "%04x%04x.pnt", dest.net, dest.node);
		}
362
363
	}
	backslash(outbound);
364
365
366
	if(isdir(outbound))
		return 0;
	lprintf(LOG_DEBUG, "Creating outbound directory for %s: %s", smb_faddrtoa(&dest, NULL), outbound);
367
368
369
	return mkpath(outbound);
}

rswindell's avatar
rswindell committed
370
371
372
373
374
375
376
377
const char* get_current_outbound(fidoaddr_t dest, bool fileboxes)
{
	static char outbound[MAX_PATH+1];
	get_outbound(dest, outbound, sizeof(outbound)-1, fileboxes);
	return outbound;
}

bool bso_lock_node(fidoaddr_t dest)
378
{
rswindell's avatar
rswindell committed
379
	const char* outbound;
380
381
	char fname[MAX_PATH+1];

rswindell's avatar
rswindell committed
382
383
	if(!cfg.flo_mailer)
		return true;
384

rswindell's avatar
rswindell committed
385
	outbound = get_current_outbound(dest, /* fileboxes: */false);
386
387
388
389
390
391

	if(dest.point)
		SAFEPRINTF2(fname,"%s%08x.bsy",outbound,dest.point);
	else
		SAFEPRINTF3(fname,"%s%04x%04x.bsy",outbound,dest.net,dest.node);

rswindell's avatar
rswindell committed
392
393
394
395
396
	if(strListFind(locked_bso_nodes, fname, /* case_sensitive: */true) >= 0)
		return true;
	for(unsigned attempt=0;;) {
		if(fmutex(fname, program_id(), cfg.bsy_timeout))
			break;
397
		lprintf(LOG_NOTICE, "Node (%s) externally locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
398
399
400
401
402
403
404
		if(++attempt >= cfg.bso_lock_attempts) {
			lprintf(LOG_WARNING, "Giving up after %u attempts to lock node %s", attempt, smb_faddrtoa(&dest, NULL));
			return false;
		}
		if(terminated)
			return false;
		SLEEP(cfg.bso_lock_delay*1000);
405
406
407
	}
	strListPush(&locked_bso_nodes, fname);
	lprintf(LOG_DEBUG, "Node (%s) successfully locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
408
409
410
411
412
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
438
439
440
441
	return true;
}

const char* bso_flo_filename(fidoaddr_t dest)
{
	nodecfg_t* nodecfg;
	char ch='f';
	const char* outbound;
	static char filename[MAX_PATH+1];

	if((nodecfg=findnodecfg(&cfg, dest, /* exact: */false)) != NULL) {
		switch(nodecfg->status) {
			case MAIL_STATUS_CRASH:	ch='c';	break;
			case MAIL_STATUS_HOLD:	ch='h';	break;
			default:
				if(nodecfg->direct)
					ch='d';
				break;
		}
	}
	outbound = get_current_outbound(dest, /* fileboxes: */false);

	if(dest.point)
		SAFEPRINTF3(filename,"%s%08x.%clo",outbound,dest.point,ch);
	else
		SAFEPRINTF4(filename,"%s%04x%04x.%clo",outbound,dest.net,dest.node,ch);
	return filename;
}

bool bso_file_attached_to_flo(const char* attachment, fidoaddr_t dest, bool bundle)
{
	char searchstr[MAX_PATH+1];
	SAFEPRINTF2(searchstr,"%c%s",(bundle && cfg.trunc_bundles) ? '#':'^', attachment);
	return findstr(searchstr, bso_flo_filename(dest));
442
443
}

444
445
446
447
448
/******************************************************************************
 This function creates or appends on existing Binkley compatible .?LO file
 attach file.
 Returns 0 on success.
******************************************************************************/
rswindell's avatar
rswindell committed
449
int write_flofile(const char *infile, fidoaddr_t dest, bool bundle, bool use_outbox)
450
{
rswindell's avatar
rswindell committed
451
452
	const char* flo_filename;
	char attachment[MAX_PATH+1];
453
	char searchstr[MAX_PATH+1];
rswindell's avatar
rswindell committed
454
455
	FILE *fp;
	nodecfg_t* nodecfg;
456

rswindell's avatar
rswindell committed
457
458
459
460
	if(use_outbox && (nodecfg=findnodecfg(&cfg, dest, /* exact: */false)) != NULL) {
		if(nodecfg->outbox[0])
			return 0;
	}
461
462
463
464

	if(!bso_lock_node(dest))
		return 1;

rswindell's avatar
rswindell committed
465
466
467
468
469
	flo_filename = bso_flo_filename(dest);
	SAFECOPY(attachment, infile);
	if(!fexistcase(attachment))	{ /* just in-case it's the wrong case for a Unix file system */
		lprintf(LOG_ERR, "ERROR line %u, attachment file not found: %s", __LINE__, attachment);
		return -1;
470
	}
rswindell's avatar
rswindell committed
471
472
473
474
475
476
477
478
479
480
481
482
	SAFEPRINTF2(searchstr,"%c%s",bundle && (cfg.trunc_bundles) ? '#':'^', attachment);
	if(findstr(searchstr,flo_filename))	/* file already in FLO file */
		return 0;
	if((fp=fopen(flo_filename,"a"))==NULL) {
		lprintf(LOG_ERR,"ERROR %u (%s) opening %s",errno, strerror(errno), flo_filename);
		return -1; 
	}
	fprintf(fp,"%s\n",searchstr);
	fclose(fp);
	lprintf(LOG_INFO, "File (%s, %1.1fKB) for %s added to BSO/FLO file: %s"
		,attachment, flength(attachment)/1024.0, smb_faddrtoa(&dest,NULL), flo_filename);
	return 0;
483
484
}

485
/* Writes text buffer to file, expanding sole LFs to CRLFs */
rswindell's avatar
rswindell committed
486
size_t fwrite_crlf(const char* buf, size_t len, FILE* fp)
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
{
	char	ch,last_ch=0;
	size_t	i;
	size_t	wr=0;	/* total chars written (may be > len) */

	for(i=0;i<len;i++) {
		ch=*buf++;
		if(ch=='\n' && last_ch!='\r') {
			if(fputc('\r',fp)==EOF)
				return(wr);
			wr++;
		}
		if(fputc(ch,fp)==EOF)
			return(wr);
		wr++;
		last_ch=ch;
	}

	return(wr);
}

rswindell's avatar
rswindell committed
508
bool fidoctrl_line_exists(const smbmsg_t* msg, const char* prefix)
509
{
510
	if(msg==NULL || prefix==NULL)
rswindell's avatar
rswindell committed
511
		return false;
512
513
514
	for(int i=0; i<msg->total_hfields; i++) {
		if(msg->hfield[i].type == FIDOCTRL
			&& strncmp((char*)msg->hfield_dat[i], prefix, strlen(prefix)) == 0)
rswindell's avatar
rswindell committed
515
			return true;
516
	}
rswindell's avatar
rswindell committed
517
	return false;
518
519
}

rswindell's avatar
rswindell committed
520
fidoaddr_t fmsghdr_srcaddr(const fmsghdr_t* hdr)
521
522
523
524
525
526
527
528
529
530
531
{
	fidoaddr_t addr;

	addr.zone	= hdr->origzone;
	addr.net	= hdr->orignet;
	addr.node	= hdr->orignode;
	addr.point	= hdr->origpoint;

	return addr;
}

rswindell's avatar
rswindell committed
532
const char* fmsghdr_srcaddr_str(const fmsghdr_t* hdr)
533
534
535
536
537
538
539
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_srcaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

rswindell's avatar
rswindell committed
540
fidoaddr_t fmsghdr_destaddr(const fmsghdr_t* hdr)
541
542
543
544
545
546
547
548
549
550
551
{
	fidoaddr_t addr;

	addr.zone	= hdr->destzone;
	addr.net	= hdr->destnet;
	addr.node	= hdr->destnode;
	addr.point	= hdr->destpoint;

	return addr;
}

rswindell's avatar
rswindell committed
552
const char* fmsghdr_destaddr_str(const fmsghdr_t* hdr)
553
554
555
556
557
558
559
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_destaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

rswindell's avatar
rswindell committed
560
561
562
563
564
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
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
bool parse_pkthdr(const fpkthdr_t* hdr, fidoaddr_t* orig_addr, fidoaddr_t* dest_addr, enum pkt_type* pkt_type)
{
	fidoaddr_t	orig;
	fidoaddr_t	dest;
	enum pkt_type type = PKT_TYPE_2_0;

	if(hdr->type2.pkttype != 2)
		return false;

	orig.zone	= hdr->type2.origzone;
	orig.net	= hdr->type2.orignet;
	orig.node	= hdr->type2.orignode;
	orig.point	= 0;

	dest.zone	= hdr->type2.destzone;
	dest.net	= hdr->type2.destnet;
	dest.node	= hdr->type2.destnode;
	dest.point	= 0;				/* No point info in the 2.0 hdr! */

	if(hdr->type2plus.cword == BYTE_SWAP_16(hdr->type2plus.cwcopy)  /* 2+ Packet Header (FSC-39) */
		&& (hdr->type2plus.cword&1)) {
		type = PKT_TYPE_2_PLUS;
		dest.point = hdr->type2plus.destpoint;
		if(hdr->type2plus.origpoint!=0 && orig.net == 0xffff) {	/* see FSC-0048 for details */
			orig.net = hdr->type2plus.auxnet;
			orig.point = hdr->type2plus.origpoint;
		}
	} else if(hdr->type2_2.subversion==2) {					/* Type 2.2 Packet Header (FSC-45) */
		type = PKT_TYPE_2_2;
		orig.point = hdr->type2_2.origpoint;
		dest.point = hdr->type2_2.destpoint; 
	}

	if(pkt_type != NULL)
		*pkt_type = type;
	if(dest_addr != NULL)
		*dest_addr = dest;
	if(orig_addr != NULL)
		*orig_addr = orig;
	
	return true;
}

bool new_pkthdr(fpkthdr_t* hdr, fidoaddr_t orig, fidoaddr_t dest, const nodecfg_t* nodecfg)
{
	enum pkt_type pkt_type = PKT_TYPE_2_0;
	struct tm* tm;
	time_t now = time(NULL);

	if(nodecfg != NULL)
		pkt_type = nodecfg->pkt_type;

	memset(hdr, 0, sizeof(fpkthdr_t));

	if((tm = localtime(&now)) != NULL) {
		hdr->type2.year	= tm->tm_year+1900;
		hdr->type2.month= tm->tm_mon;
		hdr->type2.day	= tm->tm_mday;
		hdr->type2.hour	= tm->tm_hour;
		hdr->type2.min	= tm->tm_min;
		hdr->type2.sec	= tm->tm_sec;
	}

	hdr->type2.origzone = orig.zone;
	hdr->type2.orignet	= orig.net;
	hdr->type2.orignode	= orig.node;
	hdr->type2.destzone	= dest.zone;
	hdr->type2.destnet	= dest.net;
	hdr->type2.destnode	= dest.node;
	
	hdr->type2.pkttype	= 2;
	hdr->type2.prodcode	= SBBSECHO_PRODUCT_CODE&0xff;
	hdr->type2.sernum	= SBBSECHO_VERSION_MAJOR;

	if(nodecfg != NULL && nodecfg->pktpwd[0] != 0)
635
		strncpy((char*)hdr->type2.password, nodecfg->pktpwd, sizeof(hdr->type2.password));
rswindell's avatar
rswindell committed
636
637
638
639
640
641

	if(pkt_type == PKT_TYPE_2_0)
		return true;

	if(pkt_type == PKT_TYPE_2_2) {
		hdr->type2_2.subversion = 2;	/* 2.2 */
642
643
		strncpy((char*)hdr->type2_2.origdomn,zone_domain(orig.zone),sizeof(hdr->type2_2.origdomn));
		strncpy((char*)hdr->type2_2.destdomn,zone_domain(dest.zone),sizeof(hdr->type2_2.destdomn));
rswindell's avatar
rswindell committed
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
		return true;
	}
	
	/* 2+ */
	if(pkt_type != PKT_TYPE_2_PLUS) {
		lprintf(LOG_ERR, "UNSUPPORTED PACKET TYPE: %u", pkt_type);
		return false;
	}

	if(orig.point != 0) {
		hdr->type2plus.orignet	= 0xffff;
		hdr->type2plus.auxnet	= orig.net; 
	}
	hdr->type2plus.cword		= 0x0001;
	hdr->type2plus.cwcopy		= 0x0100;
	hdr->type2plus.prodcodeHi	= SBBSECHO_PRODUCT_CODE>>8;
	hdr->type2plus.prodrevMinor	= SBBSECHO_VERSION_MINOR;
	hdr->type2plus.origzone		= orig.zone;
	hdr->type2plus.destzone		= dest.zone;
	hdr->type2plus.origpoint	= orig.point;
	hdr->type2plus.destpoint	= dest.point;

	return true;
}

669
670
671
672
673
/******************************************************************************
 This function will create a netmail message (.MSG format).
 If file is non-zero, will set file attachment bit (for bundles).
 Returns 0 on success.
******************************************************************************/
rswindell's avatar
rswindell committed
674
675
int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, const char *body, fidoaddr_t dest
					,bool file_attached)
676
{
677
	FILE *fp;
678
	char tmp[256];
679
	char fname[MAX_PATH+1];
680
	char* from=NULL;
681
682
	uint i;
	static uint startmsg;
rswindell's avatar
rswindell committed
683
	fidoaddr_t	faddr;
684
	fmsghdr_t hdr;
685
	time_t t;
686
	struct tm *tm;
687
	when_t when_written;
rswindell's avatar
rswindell committed
688
689
	nodecfg_t* nodecfg;
	bool	direct=false;
690

691
	if(msg==NULL) {
rswindell's avatar
rswindell committed
692
		when_written.time = time32(NULL);
693
694
695
696
697
		when_written.zone = sys_timezone(&scfg);
	} else {
		from = msg->from;
		when_written = msg->hdr.when_written;
	}
698
699
700
701
	if(from==NULL)
		from="SBBSecho";
	if(to==NULL)
		to="Sysop";
702
	if(!startmsg) startmsg=1;
rswindell's avatar
rswindell committed
703
704
705
	if((nodecfg=findnodecfg(&cfg, dest, 0)) != NULL) {
		if(nodecfg->status == MAIL_STATUS_NORMAL && !nodecfg->direct)
			nodecfg=findnodecfg(&cfg, dest, /* skip exact match: */2);
706
	}
707

708
	MKDIR(scfg.netmail_dir);
709
710
711
712
713
714
715
716
717
718
	for(i=startmsg;i;i++) {
		sprintf(fname,"%s%u.msg",scfg.netmail_dir,i);
		if(!fexistcase(fname))
			break; 
	}
	if(!i) {
		lprintf(LOG_WARNING,"Directory full: %s",scfg.netmail_dir);
		return(-1); 
	}
	startmsg=i+1;
719
	if((fp=fnopen(NULL,fname,O_RDWR|O_CREAT))==NULL) {
720
721
722
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,fname);
		return(-1); 
	}
723

724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
	faddr=getsysfaddr(dest.zone);
	memset(&hdr,0,sizeof(fmsghdr_t));
	hdr.origzone=faddr.zone;
	hdr.orignet=faddr.net;
	hdr.orignode=faddr.node;
	hdr.origpoint=faddr.point;
	hdr.destzone=dest.zone;
	hdr.destnet=dest.net;
	hdr.destnode=dest.node;
	hdr.destpoint=dest.point;

	hdr.attr=(FIDO_PRIVATE|FIDO_KILLSENT|FIDO_LOCAL);
	if(file_attached)
		hdr.attr|=FIDO_FILE;

rswindell's avatar
rswindell committed
739
740
741
742
	if(nodecfg != NULL) {
		switch(nodecfg->status) {
			case MAIL_STATUS_HOLD:	hdr.attr|=FIDO_HOLD;	break;
			case MAIL_STATUS_CRASH:	hdr.attr|=FIDO_CRASH;	break;
743
			case MAIL_STATUS_NORMAL:						break;
rswindell's avatar
rswindell committed
744
745
746
		}
		direct = nodecfg->direct;
	}
747

748
	t = when_written.time;
rswindell's avatar
rswindell committed
749
	tm = localtime(&t);
750
751
752
753
754
755
756
757
758
759
760
761
762
	sprintf(hdr.time,"%02u %3.3s %02u  %02u:%02u:%02u"
		,tm->tm_mday,mon[tm->tm_mon],TM_YEAR(tm->tm_year)
		,tm->tm_hour,tm->tm_min,tm->tm_sec);

	SAFECOPY(hdr.to,to);
	SAFECOPY(hdr.from,from);
	SAFECOPY(hdr.subj,subject);

	fwrite(&hdr,sizeof(fmsghdr_t),1,fp);
	fprintf(fp,"\1INTL %hu:%hu/%hu %hu:%hu/%hu\r"
		,hdr.destzone,hdr.destnet,hdr.destnode
		,hdr.origzone,hdr.orignet,hdr.orignode);

763
764
765
766
767
768
769
770
771
	if(!fidoctrl_line_exists(msg, "TZUTC:")) {
		/* TZUTC (FSP-1001) */
		int tzone=smb_tzutc(when_written.zone);
		char* minus="";
		if(tzone<0) {
			minus="-";
			tzone=-tzone;
		}
		fprintf(fp,"\1TZUTC: %s%02d%02u\r", minus, tzone/60, tzone%60);
772
	}
773
774
	/* Add FSC-53 FLAGS kludge */
	fprintf(fp,"\1FLAGS");
rswindell's avatar
rswindell committed
775
	if(direct)
776
777
		fprintf(fp," DIR");
	if(file_attached) {
rswindell's avatar
rswindell committed
778
		if(cfg.trunc_bundles)
779
			fprintf(fp," TFS");
780
		else
781
782
783
			fprintf(fp," KFS");
	}
	fprintf(fp,"\r");
784

785
786
787
788
	if(hdr.destpoint)
		fprintf(fp,"\1TOPT %hu\r",hdr.destpoint);
	if(hdr.origpoint)
		fprintf(fp,"\1FMPT %hu\r",hdr.origpoint);
789
790
791
792
793
794
795
	fprintf(fp,"\1PID: %s\r", (msg==NULL || msg->ftn_pid==NULL) ? sbbsecho_pid() : msg->ftn_pid);
	if(msg != NULL) {
		/* Unknown kludge lines are added here */
		for(int i=0; i<msg->total_hfields; i++)
			if(msg->hfield[i].type == FIDOCTRL)
				fprintf(fp,"\1%.512s\r",(char*)msg->hfield_dat[i]);
	}
rswindell's avatar
rswindell committed
796
	if(!file_attached || (!direct && file_attached))
797
798
799
		fwrite_crlf(body,strlen(body)+1,fp);	/* Write additional NULL */
	else
		fwrite("\0",1,1,fp);               /* Write NULL */
rswindell's avatar
rswindell committed
800
801
802
	lprintf(LOG_INFO, "Created NetMail (%s)%s from %s (%s) to %s (%s), attr: %04hX, subject: %s"
		,getfname(fname), file_attached ? " with attachment" : ""
		,from, smb_faddrtoa(&faddr, tmp), to, smb_faddrtoa(&dest, NULL), hdr.attr, subject);
803
	return fclose(fp);
804
805
806
807
808
809
}

/******************************************************************************
 This function takes the contents of 'infile' and puts it into a netmail
 message bound for addr.
******************************************************************************/
rswindell's avatar
rswindell committed
810
void file_to_netmail(FILE* infile, const char* title, fidoaddr_t dest, const char* to)
811
812
813
814
{
	char *buf,*p;
	long l,m,len;

815
816
817
818
	l=len=ftell(infile);
	if(len>8192L)
		len=8192L;
	rewind(infile);
deuce's avatar
deuce committed
819
	if((buf=(char *)malloc(len+1))==NULL) {
820
		lprintf(LOG_ERR,"ERROR line %d allocating %lu for file to netmail buf",__LINE__,len);
821
822
		return; 
	}
823
824
825
	while((m=fread(buf,1,(len>8064L) ? 8064L:len,infile))>0) {
		buf[m]=0;
		if(l>8064L && (p=strrchr(buf,'\n'))!=NULL) {
826
			p++;
827
828
			if(*p) {
				*p=0;
829
				p++;
830
831
832
				fseek(infile,-1L,SEEK_CUR);
				while(*p) { 			/* Seek back to end of last line */
					p++;
833
834
835
836
					fseek(infile,-1L,SEEK_CUR); 
				} 
			} 
		}
837
838
		if(ftell(infile)<l)
			strcat(buf,"\r\nContinued in next message...\r\n");
rswindell's avatar
rswindell committed
839
		create_netmail(to, /* msg: */NULL, title, buf, dest, /* attachment: */false); 
840
	}
deuce's avatar
deuce committed
841
	free(buf);
842
}
843

rswindell's avatar
rswindell committed
844
845
/* Returns true if area is linked with specified node address */
bool area_is_linked(unsigned area_num, const fidoaddr_t* addr)
846
{
847
	unsigned i;
rswindell's avatar
rswindell committed
848
849
850
851
	for(i=0;i<cfg.area[area_num].links;i++)
		if(!memcmp(addr,&cfg.area[area_num].link[i],sizeof(fidoaddr_t)))
			return true;
	return false;
852
853
}

854
855
/* Returns area index */
uint find_area(const char* echotag)
rswindell's avatar
rswindell committed
856
{
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
	unsigned u;

	for(u=0; u < cfg.areas; u++)
		if(stricmp(cfg.area[u].name, echotag) == 0)
			break;

	return u;
}

bool area_is_valid(uint areanum)
{
	return areanum < cfg.areas;
}

/* Returns subnum */
uint find_linked_area(const char* echotag, fidoaddr_t addr)
{
	unsigned area;

	if(area_is_valid(area = find_area(echotag)) && area_is_linked(area, &addr))
			return cfg.area[area].sub;
rswindell's avatar
rswindell committed
878
879
880
881

	return INVALID_SUB;
}

882
883
884
885
886
/******************************************************************************
 This function sends a notify list to applicable nodes, this list includes the
 settings configured for the node, as well as a list of areas the node is
 connected to.
******************************************************************************/
rswindell's avatar
rswindell committed
887
void gen_notify_list(void)
888
889
890
{
	FILE *	tmpf;
	char	str[256];
deuce's avatar
deuce committed
891
	uint	i,k;
892

rswindell's avatar
rswindell committed
893
	for(k=0;k<cfg.nodecfgs && !terminated;k++) {
894

rswindell's avatar
rswindell committed
895
		if(!cfg.nodecfg[k].send_notify)
896
897
898
			continue;

		if((tmpf=tmpfile())==NULL) {
899
			lprintf(LOG_ERR,"ERROR line %d couldn't open tmpfile",__LINE__);
900
901
			return; 
		}
902
903
904
905

		fprintf(tmpf,"Following are the options set for your system and a list "
			"of areas\r\nyou are connected to.  Please make sure everything "
			"is correct.\r\n\r\n");
rswindell's avatar
rswindell committed
906
		fprintf(tmpf,"Packet Type       %s\r\n", pktTypeStringList[cfg.nodecfg[k].pkt_type]);
907
		fprintf(tmpf,"Archive Type      %s\r\n"
rswindell's avatar
rswindell committed
908
909
910
911
			,cfg.nodecfg[k].archive == SBBSECHO_ARCHIVE_NONE ? "None" : cfg.nodecfg[k].archive->name);
		fprintf(tmpf,"Mail Status       %s\r\n", mailStatusStringList[cfg.nodecfg[k].status]);
		fprintf(tmpf,"Direct            %s\r\n", cfg.nodecfg[k].direct ? "Yes":"No");
		fprintf(tmpf,"Passive           %s\r\n", cfg.nodecfg[k].passive ? "Yes":"No");
912
913
914
915
916
917
918
919
		fprintf(tmpf,"Remote AreaMgr    %s\r\n\r\n"
			,cfg.nodecfg[k].password[0] ? "Yes" : "No");

		fprintf(tmpf,"Connected Areas\r\n---------------\r\n");
		for(i=0;i<cfg.areas;i++) {
			sprintf(str,"%s\r\n",cfg.area[i].name);
			if(str[0]=='*')
				continue;
rswindell's avatar
rswindell committed
920
			if(area_is_linked(i,&cfg.nodecfg[k].addr))
921
922
				fprintf(tmpf,"%s",str); 
		}
923
924

		if(ftell(tmpf))
rswindell's avatar
rswindell committed
925
			file_to_netmail(tmpf,"SBBSecho Notify List",cfg.nodecfg[k].addr, /* To: */NULL);
926
927
		fclose(tmpf); 
	}
928
}
929

930
931
932
933
/******************************************************************************
 This function creates a netmail to addr showing a list of available areas (0),
 a list of connected areas (1), or a list of removed areas (2).
******************************************************************************/
934
935
936
937
938
enum arealist_type {
	 AREALIST_ALL			// %LIST
	,AREALIST_CONNECTED		// %QUERY
	,AREALIST_UNLINKED		// %UNLINKED
};
rswindell's avatar
rswindell committed
939
void netmail_arealist(enum arealist_type type, fidoaddr_t addr, const char* to)
940
{
941
	char str[256],title[128],match,*p,*tp;
942
	unsigned k,x;
943
	unsigned u;
944
	str_list_t	area_list;
945

946
	if(type == AREALIST_ALL)
947
		strcpy(title,"List of Available Areas");
948
	else if(type == AREALIST_CONNECTED)
949
950
951
952
		strcpy(title,"List of Connected Areas");
	else
		strcpy(title,"List of Unlinked Areas");

953
954
	if((area_list=strListInit()) == NULL) {
		lprintf(LOG_ERR,"ERROR line %d couldn't allocate string list",__LINE__);
955
956
		return; 
	}
957

958
	/* Include relevant areas from the area file (e.g. areas.bbs): */
959
960
	for(u=0;u<cfg.areas;u++) {
		if((type == AREALIST_CONNECTED || cfg.add_from_echolists_only) && !area_is_linked(u,&addr))
961
			continue;
962
		if(type == AREALIST_UNLINKED && area_is_linked(u,&addr))
963
			continue;
964
		strListPush(&area_list, cfg.area[u].name); 
965
	} 
966

967
	if(type != AREALIST_CONNECTED) {
rswindell's avatar
rswindell committed
968
969
		nodecfg_t* nodecfg=findnodecfg(&cfg, addr,0);
		if(nodecfg != NULL) {
970
			for(u=0;u<cfg.listcfgs;u++) {
971
				match=0;
972
				for(k=0; cfg.listcfg[u].keys[k]; k++) {
973
					if(match) break;
rswindell's avatar
rswindell committed
974
					for(x=0; nodecfg->keys[x]; x++) {
975
						if(!stricmp(cfg.listcfg[u].keys[k]
rswindell's avatar
rswindell committed
976
							,nodecfg->keys[x])) {
977
							FILE* fp;
978
							if((fp=fopen(cfg.listcfg[u].listpath,"r"))==NULL) {
979
								lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
980
									,errno,strerror(errno),__LINE__,cfg.listcfg[u].listpath);
981
								match=1;
982
983
								break; 
							}
984
							while(!feof(fp)) {
985
								memset(str,0,sizeof(str));
986
								if(!fgets(str,sizeof(str),fp))
987
									break;
988
								truncsp(str);
989
								p=str;
990
								SKIP_WHITESPACE(p);
991
								if(*p==0 || *p==';')     /* Ignore Blank and Comment Lines */
992
									continue;
993
994
995
								tp=p;
								FIND_WHITESPACE(tp);
								*tp=0;
996
								if(find_linked_area(p, addr) == INVALID_SUB) {
997
998
999
									if(strListFind(area_list, p, /* case_sensitive */false) < 0)
										strListPush(&area_list, p);
								}
1000
							}
1001
							fclose(fp);
1002
							match=1;
1003
							break; 
1004
1005
						}
					}
1006
1007
1008
1009
				} 
			} 
		} 
	}
1010
1011
	strListSortAlpha(area_list);
	if(!strListCount(area_list))
rswindell's avatar
rswindell committed
1012
		create_netmail(to,/* msg: */NULL,title,"None.",addr,/* attachment: */false);
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
	else {
		FILE* fp;
		if((fp=tmpfile())==NULL) {
			lprintf(LOG_ERR,"ERROR line %d couldn't open tmpfile",__LINE__);
		} else {
			strListWriteFile(fp, area_list, "\r\n");
			file_to_netmail(fp,title,addr,to);
			fclose(fp);
		}
	}
	lprintf(LOG_INFO,"Created AreaFix response netmail with %s (%u areas)", title, strListCount(area_list));
	strListFree(&area_list);
1025
}
1026

rswindell's avatar
rswindell committed
1027
int check_elists(const char *areatag, fidoaddr_t addr)
1028
1029
{
	FILE *stream;
1030
	char str[1025],quit=0,*p,*tp;
1031
1032
	unsigned k,x,match=0;
	unsigned u;
1033

rswindell's avatar
rswindell committed
1034
1035
	nodecfg_t* nodecfg=findnodecfg(&cfg, addr,0);
	if(nodecfg!=NULL) {
1036
		for(u=0;u<cfg.listcfgs;u++) {
rswindell's avatar
rswindell committed
1037
			quit=0;
1038
			for(k=0; cfg.listcfg[u].keys[k]; k++) {
rswindell's avatar
rswindell committed
1039
				if(quit) break;
rswindell's avatar
rswindell committed
1040
				for(x=0; nodecfg->keys[x] ;x++)
1041
					if(!stricmp(cfg.listcfg[u].keys[k]
rswindell's avatar
rswindell committed
1042
						,nodecfg->keys[x])) {
1043
						if((stream=fopen(cfg.listcfg[u].listpath,"r"))==NULL) {
1044
							lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
1045
								,errno,strerror(errno),__LINE__,cfg.listcfg[u].listpath);
rswindell's avatar
rswindell committed
1046
							quit=1;
1047
1048
							break; 
						}
rswindell's avatar
rswindell committed
1049
						while(!feof(stream)) {
1050
							if(!fgets(str,sizeof(str),stream))
rswindell's avatar
rswindell committed
1051
1052
								break;
							p=str;
1053
							SKIP_WHITESPACE(p);
rswindell's avatar
rswindell committed
1054
1055
							if(*p==';')     /* Ignore Comment Lines */
								continue;
1056
1057
1058
1059
							tp=p;
							FIND_WHITESPACE(tp);
							*tp=0;
							if(!stricmp(areatag,p)) {
rswindell's avatar
rswindell committed
1060
								match=1;
1061
1062
1063
								break; 
							} 
						}
rswindell's avatar
rswindell committed
1064
						fclose(stream);
1065
						quit=1;
rswindell's avatar
rswindell committed
1066
1067
						if(match)
							return(match);
1068
1069
1070
1071
1072
						break; 
					} 
			} 
		} 
	}
rswindell's avatar
rswindell committed
1073
	return(match);
1074
1075
1076
1077
}
/******************************************************************************
 Used by AREAFIX to add/remove/change areas in the areas file
******************************************************************************/
rswindell's avatar
rswindell committed
1078
void alter_areas(str_list_t add_area, str_list_t del_area, fidoaddr_t addr, const char* to)
1079
1080
{
	FILE *nmfile,*afilein,*afileout,*fwdfile;
1081
1082
	char str[1024],fields[1024],field1[256],field2[256],field3[256]
		,outpath[MAX_PATH+1]
1083
		,*outname,*p,*tp,nomatch=0,match=0;
1084
1085
	unsigned j,k,x,y;
	unsigned u;
rswindell's avatar
rswindell committed
1086
1087
	size_t add_count;
	size_t del_count;
1088

rswindell's avatar
rswindell committed
1089
1090
1091
1092
1093
1094
1095
1096
	if((add_count = strListCount(add_area)) !=0 )
		lprintf(LOG_DEBUG,"Adding %u areas for %s to %s"
			,add_count, smb_faddrtoa(&addr,NULL), cfg.areafile);

	if((del_count = strListCount(del_area)) != 0)
		lprintf(LOG_DEBUG,"Removing %u areas for %s from %s"
			,del_count, smb_faddrtoa(&addr,NULL), cfg.areafile);

1097
1098
	SAFECOPY(outpath,cfg.areafile);
	*getfname(outpath)=0;
1099
	if((outname=tempnam(outpath,"AREAS"))==NULL) {
1100
		lprintf(LOG_ERR,"ERROR tempnam(%s,AREAS)",outpath);
1101
1102
		return; 
	}
1103
	if((nmfile=tmpfile())==NULL) {
1104
		lprintf(LOG_ERR,"ERROR in tmpfile()");
1105
		free(outname);
1106
1107
		return; 
	}
1108
	if((afileout=fopen(outname,"w+"))==NULL) {
1109
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,outname);
1110
1111
		fclose(nmfile);
		free(outname);
1112
1113
		return; 
	}
1114
	if((afilein=fopen(cfg.areafile,"r"))==NULL) {
1115
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,cfg.areafile);
1116
1117
1118
		fclose(afileout);
		fclose(nmfile);
		free(outname);
1119
1120
		return; 
	}
1121
	while(!feof(afilein)) {
1122
		if(!fgets(fields,sizeof(fields),afilein))
1123
1124
1125
			break;
		truncsp(fields);
		p=fields;
1126
		SKIP_WHITESPACE(p);
1127
		if(*p==';') {    /* Skip Comment Lines */
1128
			fprintf(afileout,"%s\n",fields);
1129
1130
			continue; 
		}
1131
		SAFECOPY(field1,p);         /* Internal Code Field */
1132
		truncstr(field1," \t\r\n");
1133
1134
1135
		FIND_WHITESPACE(p);
		SKIP_WHITESPACE(p);
		SAFECOPY(field2,p);         /* Areatag Field */