sbbsecho.c 154 KB
Newer Older
1
/* Synchronet FidoNet EchoMail Scanning/Tossing and NetMail Tossing Utility */
2

3
/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

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

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

#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
47
#include <sys/stat.h>
48
49
50
#if defined(__unix__)
	#include <signal.h>
#endif
51

rswindell's avatar
rswindell committed
52
#include "conwrap.h"		/* getch() */
53
54
55
56
57
58
#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
59
#include "genwrap.h"		/* PLATFORM_DESC */
rswindell's avatar
rswindell committed
60
61
62
#include "xpendian.h"

#define MAX_OPEN_SMBS	10
63
64

smb_t *smb,*email;
rswindell's avatar
rswindell committed
65
66
bool opt_import_packets		= true;
bool opt_import_netmail		= true;
rswindell's avatar
rswindell committed
67
bool opt_delete_netmail		= true;		/* delete after importing (no effect on exported netmail) */
rswindell's avatar
rswindell committed
68
69
70
71
72
73
74
75
76
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;
77
bool opt_dump_area_file		= false;
rswindell's avatar
rswindell committed
78

rswindell's avatar
rswindell committed
79
80
81
82
83
84
85
/* 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
86
int cur_smb=0;
87
FILE *fidologfile=NULL;
rswindell's avatar
rswindell committed
88
bool twit_list;
89

rswindell's avatar
rswindell committed
90
91
92
93
94
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];
95

rswindell's avatar
rswindell committed
96
97
98
99
bool pause_on_exit=false;
bool pause_on_abend=false;
bool mtxfile_locked=false;
bool terminated=false;
100

101
102
str_list_t	locked_bso_nodes;

rswindell's avatar
rswindell committed
103
104
105
int mv(const char *insrc, const char *indest, bool copy);
void export_echomail(const char *sub_code, const nodecfg_t*, bool rescan);

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
133
134
135
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;
}

136
137
138
139
140
141
142
143
144
145
146
/* 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
147
148
/* for *.bsy file contents: */
const char* program_id(void)
149
{
rswindell's avatar
rswindell committed
150
151
152
	static char str[256];
	
	SAFEPRINTF2(str, "%u %s", getpid(), sbbsecho_pid());
153

rswindell's avatar
rswindell committed
154
	return str;
155
156
}

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

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

rswindell's avatar
rswindell committed
173
174
175
176
	if(fidologfile!=NULL && level<=cfg.log_level) {
	    time_t now = time(NULL);
		struct tm *tm;
		struct tm tmbuf = {0};
177
		char timestamp[128];
rswindell's avatar
rswindell committed
178
179
180
		strip_ctrl(sbuf, sbuf);
		if((tm = localtime(&now)) == NULL)
			tm = &tmbuf;
181
182
183
184
185
		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
186
187
		fflush(fidologfile);
	}
rswindell's avatar
rswindell committed
188
189
190
	return(chcount);
}

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

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

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
239
240
241
	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);
242
243
						strcat(cmd,str); 
					}
244
245
246
247
248
249
250
251
252
253
254
255
					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);
256
257
						strcat(cmd,str); 
					}
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
					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 */
274
					strcat(cmd,cfg->exec_dir);
275
					break;
276
277
278
279
280
                case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
                    strcat(cmd,cfg->exec_dir);
#endif
                    break;
281
282
283
284
285
286
287
288
289
290
291
				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;
292
293
294
295
296
297
298
299
300
301
				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;
302
				default:    /* unknown specification */
303
					lprintf(LOG_ERR,"ERROR Checking Command Line '%s'",instr);
304
					bail(1);
305
306
307
308
					break; 
			}
			j=strlen(cmd); 
		}
309
		else
310
311
			cmd[j++]=instr[i]; 
}
312
	cmd[j]=0;
313

314
	return(cmd);
315
316
317
}

/****************************************************************************/
rswindell's avatar
rswindell committed
318
/* Runs an external program directly using system()							*/
319
320
321
/****************************************************************************/
int execute(char *cmdline)
{
rswindell's avatar
rswindell committed
322
323
324
325
326
327
	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));
328

rswindell's avatar
rswindell committed
329
	return retval;
330
}
331

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

339
340
341
	for(i=0;i<scfg.total_faddrs;i++)
		if(scfg.faddr[i].zone==zone)
			return(scfg.faddr[i]);
342
	return(sys_faddr);
343
}
344

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

349
	strncpy(outbound,zone_root_outbound(dest.zone),maxlen);
rswindell's avatar
rswindell committed
350
351
352
353
354
355
	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/" */
356
357
358
			char* p = lastchar(outbound);
			if(IS_PATH_DELIM(*p))
				*p = 0;
rswindell's avatar
rswindell committed
359
360
361
362
363
364
			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);
		}
365
366
	}
	backslash(outbound);
367
368
369
	if(isdir(outbound))
		return 0;
	lprintf(LOG_DEBUG, "Creating outbound directory for %s: %s", smb_faddrtoa(&dest, NULL), outbound);
370
371
372
	return mkpath(outbound);
}

rswindell's avatar
rswindell committed
373
374
375
376
377
378
379
380
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)
381
{
rswindell's avatar
rswindell committed
382
	const char* outbound;
383
384
	char fname[MAX_PATH+1];

rswindell's avatar
rswindell committed
385
386
	if(!cfg.flo_mailer)
		return true;
387

rswindell's avatar
rswindell committed
388
	outbound = get_current_outbound(dest, /* fileboxes: */false);
389
390
391
392
393
394

	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
395
396
397
398
399
	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;
400
		lprintf(LOG_NOTICE, "Node (%s) externally locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
401
402
403
404
405
406
407
		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);
408
409
410
	}
	strListPush(&locked_bso_nodes, fname);
	lprintf(LOG_DEBUG, "Node (%s) successfully locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
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
442
443
444
	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));
445
446
}

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

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

	if(!bso_lock_node(dest))
		return 1;

rswindell's avatar
rswindell committed
468
469
470
471
472
	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;
473
	}
rswindell's avatar
rswindell committed
474
475
476
477
478
479
480
481
482
483
484
485
	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;
486
487
}

488
/* Writes text buffer to file, expanding sole LFs to CRLFs */
rswindell's avatar
rswindell committed
489
size_t fwrite_crlf(const char* buf, size_t len, FILE* fp)
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
{
	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
511
bool fidoctrl_line_exists(const smbmsg_t* msg, const char* prefix)
512
{
513
	if(msg==NULL || prefix==NULL)
rswindell's avatar
rswindell committed
514
		return false;
515
516
517
	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
518
			return true;
519
	}
rswindell's avatar
rswindell committed
520
	return false;
521
522
}

rswindell's avatar
rswindell committed
523
fidoaddr_t fmsghdr_srcaddr(const fmsghdr_t* hdr)
524
525
526
527
528
529
530
531
532
533
534
{
	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
535
const char* fmsghdr_srcaddr_str(const fmsghdr_t* hdr)
536
537
538
539
540
541
542
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_srcaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

rswindell's avatar
rswindell committed
543
fidoaddr_t fmsghdr_destaddr(const fmsghdr_t* hdr)
544
545
546
547
548
549
550
551
552
553
554
{
	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
555
const char* fmsghdr_destaddr_str(const fmsghdr_t* hdr)
556
557
558
559
560
561
562
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_destaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

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
bool parse_origin(const char* fmsgbuf, fmsghdr_t* hdr)
{
	char* p;
	fidoaddr_t origaddr;
	
	if((p = strstr(fmsgbuf, FIDO_ORIGIN_PREFIX_FORM_1)) == NULL)
		p = strstr(fmsgbuf, FIDO_ORIGIN_PREFIX_FORM_2);
	if(p == NULL)
		return false;

	p += FIDO_ORIGIN_PREFIX_LEN;
	p = strrchr(p, '(');
	if(p == NULL)
		return false;
	p++;
	origaddr = atofaddr(p);
	if(origaddr.zone == 0 
		|| origaddr.zone == 0xffff
		|| origaddr.net == 0xffff
		|| origaddr.node == 0xffff
		|| origaddr.point == 0xffff)
		return false;
	hdr->origzone	= origaddr.zone;
	hdr->orignet	= origaddr.net;
	hdr->orignode	= origaddr.node;
	hdr->origpoint	= origaddr.point;
	return true;
}

rswindell's avatar
rswindell committed
592
593
594
595
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;
rswindell's avatar
rswindell committed
596
	enum pkt_type type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
597
598
599
600
601
602
603
604
605
606
607
608
609
610

	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! */

rswindell's avatar
rswindell committed
611
612
613
614
	if(hdr->type2plus.cword == BYTE_SWAP_16(hdr->type2plus.cwcopy)  /* 2e Packet Header (FSC-39) */
		&& (hdr->type2plus.cword&1)) {	/* Some call this a Type-2+ packet, but it's not (yet) FSC-48 conforming */
		type = PKT_TYPE_2_EXT;
		orig.point = hdr->type2plus.origpoint;
rswindell's avatar
rswindell committed
615
		dest.point = hdr->type2plus.destpoint;
rswindell's avatar
rswindell committed
616
617
618
619
620
621
		if(orig.zone == 0) orig.zone = hdr->type2plus.origzone;
		if(dest.zone == 0) dest.zone = hdr->type2plus.destzone;
		if(hdr->type2plus.auxnet != 0) {	/* strictly speaking, auxnet may be 0 and a valid 2+ packet */
			type = PKT_TYPE_2_PLUS;
			if(orig.point != 0 && orig.net == 0xffff) 	/* see FSC-0048 for details */
				orig.net = hdr->type2plus.auxnet;
rswindell's avatar
rswindell committed
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
		}
	} 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)
{
rswindell's avatar
rswindell committed
641
	enum pkt_type pkt_type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
642
643
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
669
670
	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)
671
		strncpy((char*)hdr->type2.password, nodecfg->pktpwd, sizeof(hdr->type2.password));
rswindell's avatar
rswindell committed
672

rswindell's avatar
rswindell committed
673
	if(pkt_type == PKT_TYPE_2)
rswindell's avatar
rswindell committed
674
675
676
677
		return true;

	if(pkt_type == PKT_TYPE_2_2) {
		hdr->type2_2.subversion = 2;	/* 2.2 */
678
679
		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
680
681
682
		return true;
	}
	
rswindell's avatar
rswindell committed
683
684
	/* 2e and 2+ */
	if(pkt_type != PKT_TYPE_2_EXT && pkt_type != PKT_TYPE_2_PLUS) {
rswindell's avatar
rswindell committed
685
686
687
688
		lprintf(LOG_ERR, "UNSUPPORTED PACKET TYPE: %u", pkt_type);
		return false;
	}

rswindell's avatar
rswindell committed
689
690
691
692
	if(pkt_type == PKT_TYPE_2_PLUS) {
		if(orig.point != 0)
			hdr->type2plus.orignet	= 0xffff;
		hdr->type2plus.auxnet	= orig.net; /* Squish always copies the orignet here */
rswindell's avatar
rswindell committed
693
694
695
696
697
698
699
700
701
702
703
704
705
	}
	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;
}

706
707
708
709
710
/******************************************************************************
 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
711
712
int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, const char *body, fidoaddr_t dest
					,bool file_attached)
713
{
714
	FILE *fp;
715
	char tmp[256];
716
	char fname[MAX_PATH+1];
717
	char* from=NULL;
718
719
	uint i;
	static uint startmsg;
rswindell's avatar
rswindell committed
720
	fidoaddr_t	faddr;
721
	fmsghdr_t hdr;
722
	time_t t;
723
	struct tm *tm;
724
	when_t when_written;
rswindell's avatar
rswindell committed
725
726
	nodecfg_t* nodecfg;
	bool	direct=false;
727

728
	if(msg==NULL) {
rswindell's avatar
rswindell committed
729
		when_written.time = time32(NULL);
730
731
732
733
734
		when_written.zone = sys_timezone(&scfg);
	} else {
		from = msg->from;
		when_written = msg->hdr.when_written;
	}
735
736
737
738
	if(from==NULL)
		from="SBBSecho";
	if(to==NULL)
		to="Sysop";
739
	if(!startmsg) startmsg=1;
rswindell's avatar
rswindell committed
740
741
742
	if((nodecfg=findnodecfg(&cfg, dest, 0)) != NULL) {
		if(nodecfg->status == MAIL_STATUS_NORMAL && !nodecfg->direct)
			nodecfg=findnodecfg(&cfg, dest, /* skip exact match: */2);
743
	}
744

745
	MKDIR(scfg.netmail_dir);
746
747
748
749
750
751
752
753
754
755
	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;
756
	if((fp=fnopen(NULL,fname,O_RDWR|O_CREAT))==NULL) {
757
758
759
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,fname);
		return(-1); 
	}
760

761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
	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
776
777
778
779
	if(nodecfg != NULL) {
		switch(nodecfg->status) {
			case MAIL_STATUS_HOLD:	hdr.attr|=FIDO_HOLD;	break;
			case MAIL_STATUS_CRASH:	hdr.attr|=FIDO_CRASH;	break;
780
			case MAIL_STATUS_NORMAL:						break;
rswindell's avatar
rswindell committed
781
782
783
		}
		direct = nodecfg->direct;
	}
784

785
	t = when_written.time;
rswindell's avatar
rswindell committed
786
	tm = localtime(&t);
787
788
789
790
791
792
793
794
795
796
797
798
799
	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);

800
801
802
803
804
805
806
807
808
	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);
809
	}
810
811
	/* Add FSC-53 FLAGS kludge */
	fprintf(fp,"\1FLAGS");
rswindell's avatar
rswindell committed
812
	if(direct)
813
814
		fprintf(fp," DIR");
	if(file_attached) {
rswindell's avatar
rswindell committed
815
		if(cfg.trunc_bundles)
816
			fprintf(fp," TFS");
817
		else
818
819
820
			fprintf(fp," KFS");
	}
	fprintf(fp,"\r");
821

822
823
824
825
	if(hdr.destpoint)
		fprintf(fp,"\1TOPT %hu\r",hdr.destpoint);
	if(hdr.origpoint)
		fprintf(fp,"\1FMPT %hu\r",hdr.origpoint);
826
827
828
829
830
831
832
	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
833
	if(!file_attached || (!direct && file_attached))
834
835
836
		fwrite_crlf(body,strlen(body)+1,fp);	/* Write additional NULL */
	else
		fwrite("\0",1,1,fp);               /* Write NULL */
rswindell's avatar
rswindell committed
837
838
839
	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);
840
	return fclose(fp);
841
842
843
844
845
846
}

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

852
853
854
855
	l=len=ftell(infile);
	if(len>8192L)
		len=8192L;
	rewind(infile);
deuce's avatar
deuce committed
856
	if((buf=(char *)malloc(len+1))==NULL) {
857
		lprintf(LOG_ERR,"ERROR line %d allocating %lu for file to netmail buf",__LINE__,len);
858
859
		return; 
	}
860
861
862
	while((m=fread(buf,1,(len>8064L) ? 8064L:len,infile))>0) {
		buf[m]=0;
		if(l>8064L && (p=strrchr(buf,'\n'))!=NULL) {
863
			p++;
864
865
			if(*p) {
				*p=0;
866
				p++;
867
868
869
				fseek(infile,-1L,SEEK_CUR);
				while(*p) { 			/* Seek back to end of last line */
					p++;
870
871
872
873
					fseek(infile,-1L,SEEK_CUR); 
				} 
			} 
		}
874
875
		if(ftell(infile)<l)
			strcat(buf,"\r\nContinued in next message...\r\n");
rswindell's avatar
rswindell committed
876
		create_netmail(to, /* msg: */NULL, title, buf, dest, /* attachment: */false); 
877
	}
deuce's avatar
deuce committed
878
	free(buf);
879
}
880

rswindell's avatar
rswindell committed
881
882
/* Returns true if area is linked with specified node address */
bool area_is_linked(unsigned area_num, const fidoaddr_t* addr)
883
{
884
	unsigned i;
rswindell's avatar
rswindell committed
885
886
887
888
	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;
889
890
}

891
892
/* Returns area index */
uint find_area(const char* echotag)
rswindell's avatar
rswindell committed
893
{
894
895
896
897
898
899
900
901
902
903
904
905
906
907
	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;
}

908
909
/* Returns subnum (INVALID_SUB if pass-through) or SUB_NOT_FOUND */
#define SUB_NOT_FOUND ((uint)-2)
910
911
912
913
914
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))
915
		return cfg.area[area].sub;
rswindell's avatar
rswindell committed
916

917
	return SUB_NOT_FOUND;
rswindell's avatar
rswindell committed
918
919
}

920
921
922
923
924
/******************************************************************************
 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
925
void gen_notify_list(void)
926
927
928
{
	FILE *	tmpf;
	char	str[256];
deuce's avatar
deuce committed
929
	uint	i,k;
930

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

rswindell's avatar
rswindell committed
933
		if(!cfg.nodecfg[k].send_notify)
934
935
936
			continue;

		if((tmpf=tmpfile())==NULL) {
937
			lprintf(LOG_ERR,"ERROR line %d couldn't open tmpfile",__LINE__);
938
939
			return; 
		}
940
941
942
943

		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
944
		fprintf(tmpf,"Packet Type       %s\r\n", pktTypeStringList[cfg.nodecfg[k].pkt_type]);
945
		fprintf(tmpf,"Archive Type      %s\r\n"
rswindell's avatar
rswindell committed
946
947
948
949
			,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");
950
951
952
953
954
955
956
957
		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
958
			if(area_is_linked(i,&cfg.nodecfg[k].addr))
959
960
				fprintf(tmpf,"%s",str); 
		}
961
962

		if(ftell(tmpf))
rswindell's avatar
rswindell committed
963
			file_to_netmail(tmpf,"SBBSecho Notify List",cfg.nodecfg[k].addr, /* To: */NULL);
964
965
		fclose(tmpf); 
	}
966
}
967

968
969
970
971
/******************************************************************************
 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).
******************************************************************************/
972
973
974
975
976
enum arealist_type {
	 AREALIST_ALL			// %LIST
	,AREALIST_CONNECTED		// %QUERY
	,AREALIST_UNLINKED		// %UNLINKED
};
rswindell's avatar
rswindell committed
977
void netmail_arealist(enum arealist_type type, fidoaddr_t addr, const char* to)
978
{
979
	char str[256],title[128],match,*p,*tp;
980
	unsigned k,x;
981
	unsigned u;
982
	str_list_t	area_list;
983

984
	if(type == AREALIST_ALL)
985
		strcpy(title,"List of Available Areas");
986
	else if(type == AREALIST_CONNECTED)
987
988
989
990
		strcpy(title,"List of Connected Areas");
	else
		strcpy(title,"List of Unlinked Areas");

991
992
	if((area_list=strListInit()) == NULL) {
		lprintf(LOG_ERR,"ERROR line %d couldn't allocate string list",__LINE__);
993
994
		return; 
	}
995

996
	/* Include relevant areas from the area file (e.g. areas.bbs): */
997
998
	for(u=0;u<cfg.areas;u++) {
		if((type == AREALIST_CONNECTED || cfg.add_from_echolists_only) && !area_is_linked(u,&addr))
999
			continue;
1000
		if(type == AREALIST_UNLINKED && area_is_linked(u,&addr))
1001
			continue;
1002
		strListPush(&area_list, cfg.area[u].name); 
1003
	} 
1004

1005
	if(type != AREALIST_CONNECTED) {
rswindell's avatar
rswindell committed
1006
1007
		nodecfg_t* nodecfg=findnodecfg(&cfg, addr,0);
		if(nodecfg != NULL) {
1008
			for(u=0;u<cfg.listcfgs;u++) {
1009
				match=0;
1010
				for(k=0; cfg.listcfg[u].keys[k]; k++) {
1011
					if(match) break;
rswindell's avatar
rswindell committed
1012
					for(x=0; nodecfg->keys[x]; x++) {
1013
						if(!stricmp(cfg.listcfg[u].keys[k]
rswindell's avatar
rswindell committed
1014
							,nodecfg->keys[x])) {
1015
							FILE* fp;
1016
							if((fp=fopen(cfg.listcfg[u].listpath,"r"))==NULL) {
1017
								lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
1018
									,errno,strerror(errno),__LINE__,cfg.listcfg[u].listpath);
1019
								match=1;
1020
1021
								break; 
							}
1022
							while(!feof(fp)) {
1023
								memset(str,0,sizeof(str));
1024
								if(!fgets(str,sizeof(str),fp))
1025
									break;
1026
								truncsp(str);
1027
								p=str;
1028
								SKIP_WHITESPACE(p);
1029
								if(*p==0 || *p==';')     /* Ignore Blank and Comment Lines */
1030
									continue;
1031
1032
1033
								tp=p;
								FIND_WHITESPACE(tp);
								*tp=0;
1034
								if(find_linked_area(p, addr) == SUB_NOT_FOUND) {
1035
1036
1037
									if(strListFind(area_list, p, /* case_sensitive */false) < 0)
										strListPush(&area_list, p);
								}
1038
							}
1039
							fclose(fp);
1040
							match=1;
1041
							break; 
1042
1043
						}
					}
1044
1045
1046
1047
				} 
			} 
		} 
	}
1048
1049
	strListSortAlpha(area_list);
	if(!strListCount(area_list))
rswindell's avatar
rswindell committed
1050
		create_netmail(to,/* msg: */NULL,title,"None.",addr,/* attachment: */false);
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
	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);
1063
}
1064

rswindell's avatar
rswindell committed
1065
int check_elists(const char *areatag, fidoaddr_t addr)
1066
1067
{
	FILE *stream;
1068
	char str[1025],quit=0,*p,*tp;
1069
1070
	unsigned k,x,match=0;
	unsigned u;
1071

rswindell's avatar
rswindell committed
1072
1073
	nodecfg_t* nodecfg=findnodecfg(&cfg, addr,0);
	if(nodecfg!=NULL) {
1074
		for(u=0;u<cfg.listcfgs;u++) {
rswindell's avatar
rswindell committed
1075
			quit=0;
1076
			for(k=0; cfg.listcfg[u].keys[k]; k++) {
rswindell's avatar
rswindell committed
1077
				if(quit) break;
rswindell's avatar
rswindell committed
1078
				for(x=0; nodecfg->keys[x] ;x++)
1079
					if(!stricmp(cfg.listcfg[u].keys[k]
rswindell's avatar
rswindell committed
1080
						,nodecfg->keys[x])) {
1081
						if((stream=fopen(cfg.listcfg[u].listpath,"r"))==NULL) {
1082
							lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
1083
								,errno,strerror(errno),__LINE__,cfg.listcfg[u].listpath);
rswindell's avatar
rswindell committed
1084
							quit=1;
1085
1086
							break; 
						}
rswindell's avatar
rswindell committed
1087
						while(!feof(stream)) {
1088
							if(!fgets(str,sizeof(str),stream))
rswindell's avatar
rswindell committed
1089
1090
								break;
							p=str;
1091
							SKIP_WHITESPACE(p);
rswindell's avatar
rswindell committed
1092
1093
							if(*p==';')     /* Ignore Comment Lines */
								continue;
1094
1095
1096
1097
							tp=p;
							FIND_WHITESPACE(tp);
							*tp=0;
							if(!stricmp(areatag,p)) {
rswindell's avatar
rswindell committed
1098
								match=1;
1099
1100
1101
								break; 
							} 
						}
rswindell's avatar
rswindell committed
1102
						fclose(stream);
1103
						quit=1;
rswindell's avatar
rswindell committed
1104
1105
						if(match)
							return(match);
1106
1107
1108
1109
1110
						break; 
					} 
			} 
		} 
	}
rswindell's avatar
rswindell committed
1111
	return(match);
1112
1113
1114
1115
}
/******************************************************************************
 Used by AREAFIX to add/remove/change areas in the areas file
******************************************************************************/
rswindell's avatar
rswindell committed
1116
void alter_areas(str_list_t add_area, str_list_t del_area, fidoaddr_t addr, const char* to)
1117
1118
{
	FILE *nmfile,*afilein,*afileout,*fwdfile;
1119
1120
	char str[1024],fields[1024],field1[256],field2[256],field3[256]
		,outpath[MAX_PATH+1]
1121
		,*outname,*p,*tp,nomatch=0,match=0;
1122
1123
	unsigned j,k,x,y;
	unsigned u;
rswindell's avatar
rswindell committed
1124
1125
	size_t add_count;
	size_t del_count;
1126

rswindell's avatar
rswindell committed
1127
1128
1129
1130
1131
1132
1133
1134
	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);

1135
1136
	SAFECOPY(outpath,cfg.areafile);
	*getfname(outpath)=0;
1137
	if((outname=tempnam(outpath,"AREAS"))==NULL) {
1138
		lprintf(LOG_ERR,"ERROR tempnam(%s,AREAS)",outpath);
1139
1140
		return; 
	}
1141
	if((nmfile=tmpfile())==NULL) {
1142
		lprintf(LOG_ERR,"ERROR in tmpfile()");
1143
		free(outname);
1144
1145
		return; 
	}
1146
	if((afileout=fopen(outname,"w+"))==NULL) {
1147
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,outname);
1148
1149
		fclose(nmfile);
		free(outname);
1150
1151
		return; 
	}
1152
	if((afilein=fopen(cfg.areafile,"r"))==NULL) {
1153
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,cfg.areafile);
1154
1155
1156
		fclose(afileout);
		fclose(nmfile);
		free(outname);
1157
1158
		return; 
	}
1159
	while(!feof(afilein)) {
1160
		if(!fgets(fields,sizeof(fields),afilein))
1161
1162
1163
			break;
		truncsp(fields);
		p=fields;
1164
		SKIP_WHITESPACE(p);
1165
		if(*p==';') {    /* Skip Comment Lines */
1166
			fprintf(afileout,"%s\n",fields);
1167
1168
			continue; 
		}
1169
		SAFECOPY(field1,p);         /* Internal Code Field */
1170
		truncstr(field1," \t\r\n");
1171
1172
1173
		FIND_WHITESPACE(p);
		SKIP_WHITESPACE(p);
		SAFECOPY(field2,p);         /* Areatag Field */
1174
		truncstr(field2," \t\r\n");
1175
1176
		FIND_WHITESPACE(p);
		SKIP_WHITESPACE(p);
1177
		if((tp=strchr(p,';'))!=NULL) {
1178
1179
			SAFECOPY(field3,p);     /* Comment Field (if any) */
			FIND_WHITESPACE(tp);
1180
1181
			*tp=0; 
		}
1182
1183
		else
			field3[0]=0;
rswindell's avatar
rswindell committed
1184
		if(del_count) { 				/* Check for areas to remove */
1185
1186
			for(u=0;del_area[u]!=NULL;u++) {
				if(!stricmp(del_area[u],field2) ||
1187
1188
1189
					!stricmp(del_area[0],"-ALL"))     /* Match Found */
					break; 
			}
1190
1191
1192
			if(del_area[u]!=NULL) {
				for(u=0;u<cfg.areas;u++) {
					if(!stricmp(field2,cfg.area[u].name)) {
1193
						lprintf(LOG_DEBUG,"Unlinking area (%s) for %s in %s", field2, smb_faddrtoa(&addr,NULL), cfg.areafile);
1194
						if(!area_is_linked(u,&addr)) {
1195
1196
							fprintf(afileout,"%s\n",fields);
							/* bugfix here Mar-25-2004 (wasn't breaking for "-ALL") */
1197
							if(stricmp(del_area[0],"-ALL"))
1198
1199
1200
								fprintf(nmfile,"%s not connected.\r\n",field2);
							break; 
						}
1201

rswindell's avatar
rswindell committed
1202
						/* Added 12/4/95 to remove link from connected link */
1203

1204
1205
						for(k=u;k<cfg.area[u].links-1;k++)
							memcpy(&cfg.area[u].link[k],&cfg.area[u].link[k+1]
rswindell's avatar
rswindell committed
1206
								,sizeof(fidoaddr_t));
1207
1208
1209
						--cfg.area[u].links;
						if(cfg.area[u].links==0) {
							FREE_AND_NULL(cfg.area[u].link);
1210
						} else {
1211
1212
1213
							if((cfg.area[u].link=(fidoaddr_t *)
								realloc(cfg.area[u].link,sizeof(fidoaddr_t)
								*(cfg.area[u].links)))==NULL) {
1214
								lprintf(LOG_ERR,"ERROR line %d allocating memory for area "
1215
									"#%u links.",__LINE__,u+1);
1216
								bail(1); 
1217
								return;
1218
							}
1219
						}
1220

1221
1222
1223
						fprintf(afileout,"%-*s %-*s "
							,LEN_EXTCODE, field1
							,FIDO_AREATAG_LEN, field2);
1224
						for(j=0;j<cfg.area[u].links;j++) {
rswindell's avatar
rswindell committed
1225
							if(!memcmp(&cfg.area[u].link[j],&addr
rswindell's avatar
rswindell committed
1226
								,sizeof(fidoaddr_t)))
1227
1228
								continue;
							fprintf(afileout,"%s "
rswindell's avatar
rswindell committed
1229
								,smb_faddrtoa(&cfg.area[u].link[j],NULL)); 
1230
						}
1231
1232
						if(field3[0])
							fprintf(afileout,"%s",field3);
1233
						fprintf(afileout,"\n");
1234
						fprintf(nmfile,"%s removed.\r\n",field2);
1235
1236
1237
						break; 
					} 
				}
1238
				if(u==cfg.areas)			/* Something screwy going on */
1239
					fprintf(afileout,"%s\n",fields);
1240
1241
1242
				continue; 
			} 				/* Area match so continue on */
		}
rswindell's avatar
rswindell committed
1243
		if(add_count) { 				/* Check for areas to add */
1244
1245
			for(u=0;add_area[u]!=NULL;u++)
				if(!stricmp(add_area[u],field2) ||
1246
					!stricmp(add_area[0],"+ALL"))      /* Match Found */
1247
					break;
1248
1249
1250
1251
1252
			if(add_area[u]!=NULL) {
				if(stricmp(add_area[u],"+ALL"))
					add_area[u][0]=0;  /* So we can check other lists */
				for(u=0;u<cfg.areas;u++) {
					if(!stricmp(field2,cfg.area[u].name)) {
1253
						lprintf(LOG_DEBUG,"Linking area (%s) for %s in %s", field2, smb_faddrtoa(&addr,NULL), cfg.areafile);
1254
						if(area_is_linked(u,&addr)) {
1255
							fprintf(afileout,"%s\n",fields);
1256
							fprintf(nmfile,"%s already connected.\r\n",field2);
1257
1258
							break; 
						}
rswindell's avatar
rswindell committed
1259
						if(cfg.add_from_echolists_only && !check_elists(field2,addr)) {
1260
							fprintf(afileout,"%s\n",fields);
1261
1262
							break; 
						}
1263