sbbsecho.c 153 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
50
51
52
53

#ifdef __WATCOMC__
	#include <mem.h>
#endif

54
#ifndef __unix__
55
56
57
	#include <malloc.h>
#endif

rswindell's avatar
rswindell committed
58
#include "conwrap.h"		/* getch() */
59
60
61
62
63
64
#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
65
#include "genwrap.h"		/* PLATFORM_DESC */
66
67
68
69

smb_t *smb,*email;
long misc=(IMPORT_PACKETS|IMPORT_NETMAIL|IMPORT_ECHOMAIL|EXPORT_ECHOMAIL
			|DELETE_NETMAIL|DELETE_PACKETS);
70
BOOL export_netmail_option=TRUE;
rswindell's avatar
rswindell committed
71
72
73
74
75
76
77
/* statistics */
ulong netmail=0;	/* imported */
ulong echomail=0;	/* imported */
ulong exported_netmail=0;
ulong exported_echomail=0;
ulong packed_netmail=0;

78
79
80
char tmp[256],pkt_type=0;
int secure,cur_smb=0;
FILE *fidologfile=NULL;
81
BOOL twit_list;
82

83
faddr_t		sys_faddr = {1,1,1,0};		/* Default system address: 1:1/1.0 */
84
85
config_t	cfg;
scfg_t		scfg;
86
char		revision[16];
87
char		compiler[32];
88

89
BOOL pause_on_exit=FALSE;
90
BOOL pause_on_abend=FALSE;
91

92
93
str_list_t	locked_bso_nodes;

94
95
96
97
98
99
100
101
102
103
104
/* 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;
}

105
#if defined(__unix__)	/* borrowed from MSVC */
rswindell's avatar
rswindell committed
106
unsigned _rotr (
107
108
109
110
        unsigned val,
        int shift
        )
{
111
112
113
114
115
116
117
118
119
120
121
    register unsigned lobit;        /* non-zero means lo bit set */
    register unsigned num = val;    /* number to rotate */

    shift &= 0x1f;                  /* modulo 32 -- this will also make
                                       negative shifts work */
    while (shift--) {
	    lobit = num & 1;        /* get high bit */
        num >>= 1;              /* shift right one bit */
        if (lobit)
			num |= 0x80000000;  /* set hi bit if lo bit was set */
	}
122

123
    return num;
124
125
126
}
#endif

127
128
void logprintf(char *str, ...);

rswindell's avatar
rswindell committed
129
/****************************************************************************/
130
/* This is needed by load_cfg.c												*/
rswindell's avatar
rswindell committed
131
/****************************************************************************/
132
int lprintf(int level, char *fmat, ...)
rswindell's avatar
rswindell committed
133
134
135
136
137
138
{
	va_list argptr;
	char sbuf[256];
	int chcount;

	va_start(argptr,fmat);
139
	chcount=vsnprintf(sbuf,sizeof(sbuf),fmat,argptr);
140
	sbuf[sizeof(sbuf)-1]=0;
rswindell's avatar
rswindell committed
141
	va_end(argptr);
142
143
	truncsp(sbuf);
	printf("%s\n",sbuf);
144

145
	if(level<=cfg.log_level)
146
		logprintf("%s",sbuf);
rswindell's avatar
rswindell committed
147
148
149
	return(chcount);
}

150
151
152
153
154
155
156
157
158
159
/**********************/
/* Log print function */
/**********************/
void logprintf(char *str, ...)
{
    va_list argptr;
    char buf[256];
    time_t now;
    struct tm *gm;

160
161
162
163
164
165
	if(!(misc&LOGFILE) || fidologfile==NULL)
		return;
	va_start(argptr,str);
	vsnprintf(buf,sizeof(buf),str,argptr);
	buf[sizeof(buf)-1]=0;
	va_end(argptr);
rswindell's avatar
rswindell committed
166
	strip_ctrl(buf, buf);
167
168
	now=time(NULL);
	gm=localtime(&now);
rswindell's avatar
rswindell committed
169
170
171
	fprintf(fidologfile,"%u-%02u-%02u %02u:%02u:%02u %s\n"
		,1900+gm->tm_year, gm->tm_mon+1, gm->tm_mday
		,gm->tm_hour, gm->tm_min, gm->tm_sec
172
		,buf);
173
174
}

175
BOOL delfile(const char *filename, int line)
176
{
177
178
	lprintf(LOG_DEBUG, "Deleting %s", filename);
	if(remove(filename) != 0) {
179
180
		lprintf(LOG_ERR, "ERROR %u (%s) line %u removing file %s"
			,errno, strerror(errno), line, filename);
181
182
183
		return FALSE;
	}
	return TRUE;
184
185
}

186
187
188
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments             */
/*****************************************************************************/
189
char *mycmdstr(scfg_t* cfg, char *instr, char *fpath, char *fspec)
190
{
191
    static char cmd[MAX_PATH+1];
192
193
194
    char str[256],str2[128];
    int i,j,len;

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
	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);
226
227
						strcat(cmd,str); 
					}
228
229
230
231
232
233
234
235
236
237
238
239
					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);
240
241
						strcat(cmd,str); 
					}
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
					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 */
258
					strcat(cmd,cfg->exec_dir);
259
					break;
260
261
262
263
264
                case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
                    strcat(cmd,cfg->exec_dir);
#endif
                    break;
265
266
267
268
269
270
271
272
273
274
275
				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;
276
277
278
279
280
281
282
283
284
285
286
287
288
289
				case '.':	/* .exe for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
					strcat(cmd,".exe");
#endif
					break;
				case '?':	/* Platform */
#ifdef __OS2__
					strcpy(str,"OS2");
#else
					strcpy(str,PLATFORM_DESC);
#endif
					strlwr(str);
					strcat(cmd,str);
					break;
290
				default:    /* unknown specification */
291
					lprintf(LOG_ERR,"ERROR Checking Command Line '%s'",instr);
292
					bail(1);
293
294
295
296
					break; 
			}
			j=strlen(cmd); 
		}
297
		else
298
299
			cmd[j++]=instr[i]; 
}
300
	cmd[j]=0;
301

302
	return(cmd);
303
304
305
306
307
308
309
}

/****************************************************************************/
/* Runs an external program directly using spawnvp							*/
/****************************************************************************/
int execute(char *cmdline)
{
rswindell's avatar
rswindell committed
310
#if 1
311
	return system(cmdline);
rswindell's avatar
rswindell committed
312
#else
313
314
315
316
317
318
319
	char c,d,e,cmdlen,*arg[30],str[256];
	int i;

	strcpy(str,cmdline);
	arg[0]=str;	/* point to the beginning of the string */
	cmdlen=strlen(str);
	for(c=0,d=1,e=0;c<cmdlen;c++,e++)	/* Break up command line */
320
		if(str[c]==' ') {
321
322
323
324
325
326
			str[c]=0;			/* insert nulls */
			arg[d++]=str+c+1;	/* point to the beginning of the next arg */
			e=0; }
	arg[d]=0;
	i=spawnvp(P_WAIT,arg[0],arg);
	return(i);
rswindell's avatar
rswindell committed
327
#endif
328
}
329

330
331
332
333
334
335
/******************************************************************************
 Returns the system address with the same zone as the address passed
******************************************************************************/
faddr_t getsysfaddr(short zone)
{
	int i;
336

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

343
344
345
346
347
int get_flo_outbound(faddr_t dest, char* outbound, size_t maxlen)
{
	if(dest.zone==sys_faddr.zone)		/* Default zone, use default outbound */
		strncpy(outbound,cfg.outbound,maxlen);
	else {								/* Inter-zone outbound is OUTBOUND.XXX */
rswindell's avatar
rswindell committed
348
		safe_snprintf(outbound,maxlen,"%.*s.%03x"
349
350
351
352
353
354
355
356
357
358
359
360
			,(int)strlen(cfg.outbound)-1,cfg.outbound,dest.zone);
	}
	if(dest.point) {					/* Point destination is OUTBOUND\*.PNT */
		char point[128];
		SAFEPRINTF2(point,"%04x%04x.pnt"
			,dest.net,dest.node);
		strncat(outbound,point,maxlen); 
	}
	backslash(outbound);
	return mkpath(outbound);
}

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
BOOL bso_lock_node(faddr_t dest)
{
	char outbound[MAX_PATH+1];
	char fname[MAX_PATH+1];

	if(!(misc&FLO_MAILER))
		return TRUE;

	get_flo_outbound(dest, outbound, sizeof(outbound)-2);

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

	if(strListFind(locked_bso_nodes, fname, /* case_sensitive: */TRUE) >= 0)
		return TRUE;
	if(!fmutex(fname, sbbsecho_pid(), 12*60*60)) {
		lprintf(LOG_NOTICE, "Node (%s) externally locked via: %s", smb_faddrtoa(&dest, NULL), fname);
		return FALSE;
	}
	strListPush(&locked_bso_nodes, fname);
	lprintf(LOG_DEBUG, "Node (%s) successfully locked via: %s", smb_faddrtoa(&dest, NULL), fname);
	return TRUE;
}

387
388
389
390
391
/******************************************************************************
 This function creates or appends on existing Binkley compatible .?LO file
 attach file.
 Returns 0 on success.
******************************************************************************/
392
int write_flofile(char *attachment, faddr_t dest, BOOL bundle)
393
{
394
395
396
397
	char fname[MAX_PATH+1];
	char outbound[MAX_PATH+1];
	char ch;
	char searchstr[MAX_PATH+1];
398
	ushort attr=0;
399
	int i;
400
401
402
403
404
405
	FILE *stream;

	i=matchnode(dest,0);
	if(i<(int)cfg.nodecfgs)
		attr=cfg.nodecfg[i].attr;

rswindell's avatar
rswindell committed
406
407
408
409
	if(attr&ATTR_CRASH) ch='c';
	else if(attr&ATTR_HOLD) ch='h';
	else if(attr&ATTR_DIRECT) ch='d';
	else ch='f';
410
411

	get_flo_outbound(dest, outbound, sizeof(outbound)-2);
412
413
414
415

	if(!bso_lock_node(dest))
		return 1;

416
	if(dest.point)
417
		SAFEPRINTF3(fname,"%s%08x.%clo",outbound,dest.point,ch);
418
	else
419
		SAFEPRINTF4(fname,"%s%04x%04x.%clo",outbound,dest.net,dest.node,ch);
rswindell's avatar
rswindell committed
420
	if(bundle && (misc&TRUNC_BUNDLES))
421
422
423
		ch='#';
	else
		ch='^';
424
425
426
	if(*attachment == '^')	/* work-around for BRE/FE inter-BBS attachment bug */
		attachment++;
	fexistcase(attachment);	/* just in-case it's the wrong case for a Unix file system */
427
	SAFEPRINTF2(searchstr,"%c%s",ch,attachment);
428
429
	if(findstr(searchstr,fname))	/* file already in FLO file */
		return(0);
430
	if((stream=fopen(fname,"a"))==NULL) {
431
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,fname);
432
433
		return(-1); 
	}
434
	fprintf(stream,"%s\n",searchstr);
435
	fclose(stream);
rswindell's avatar
rswindell committed
436
437
438
	if(cfg.log&LOG_BSO_FLO)
		lprintf(LOG_INFO, "File (%s) for %s added to BSO/FLO file: %s"
			,attachment, smb_faddrtoa(&dest,NULL), fname);
439
440
441
	return(0);
}

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/* Writes text buffer to file, expanding sole LFs to CRLFs */
size_t fwrite_crlf(char* buf, size_t len, FILE* fp)
{
	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);
}

465
466
BOOL fidoctrl_line_exists(smbmsg_t* msg, const char* prefix)
{
467
468
	if(msg==NULL || prefix==NULL)
		return FALSE;
469
470
471
472
473
474
475
476
	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)
			return TRUE;
	}
	return FALSE;
}

477
478
479
480
481
/******************************************************************************
 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.
******************************************************************************/
482
int create_netmail(char *to, smbmsg_t* msg, char *subject, char *body, faddr_t dest, BOOL file_attached)
483
{
484
485
	FILE *fp;
	char fname[MAX_PATH+1];
486
	char* from=NULL;
487
488
489
490
491
	ushort attr=0;
	uint i;
	static uint startmsg;
	faddr_t	faddr;
	fmsghdr_t hdr;
492
	time_t t;
493
	struct tm *tm;
494
	when_t when_written;
495

496
497
498
499
500
501
502
	if(msg==NULL) {
		when_written.time = time(NULL);
		when_written.zone = sys_timezone(&scfg);
	} else {
		from = msg->from;
		when_written = msg->hdr.when_written;
	}
503
504
505
506
	if(from==NULL)
		from="SBBSecho";
	if(to==NULL)
		to="Sysop";
507
508
509
510
511
512
513
	if(!startmsg) startmsg=1;
	i=matchnode(dest,0);
	if(i<cfg.nodecfgs) {
		attr=cfg.nodecfg[i].attr;
		if(!attr) {
			i=matchnode(dest,2);
			if(i<cfg.nodecfgs)
514
515
516
				attr=cfg.nodecfg[i].attr; 
		} 
	}
517

518
	MKDIR(scfg.netmail_dir);
519
520
521
522
523
524
525
526
527
528
	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;
529
	if((fp=fnopen(NULL,fname,O_RDWR|O_CREAT))==NULL) {
530
531
532
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,fname);
		return(-1); 
	}
533

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
	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;

	if(attr&ATTR_HOLD)
		hdr.attr|=FIDO_HOLD;
	if(attr&ATTR_CRASH)
		hdr.attr|=FIDO_CRASH;

554
	t = when_written.time;
555
556
557
558
559
560
561
562
563
564
565
566
567
568
	tm=localtime(&t);
	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);

569
570
571
572
573
574
575
576
577
	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);
578
	}
579
580
581
582
583
584
585
	/* Add FSC-53 FLAGS kludge */
	fprintf(fp,"\1FLAGS");
	if(attr&ATTR_DIRECT)
		fprintf(fp," DIR");
	if(file_attached) {
		if(misc&TRUNC_BUNDLES)
			fprintf(fp," TFS");
586
		else
587
588
589
			fprintf(fp," KFS");
	}
	fprintf(fp,"\r");
590

591
592
593
594
	if(hdr.destpoint)
		fprintf(fp,"\1TOPT %hu\r",hdr.destpoint);
	if(hdr.origpoint)
		fprintf(fp,"\1FMPT %hu\r",hdr.origpoint);
595
596
597
598
599
600
601
	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]);
	}
602
603
604
605
	if(!file_attached || (!(attr&ATTR_DIRECT) && file_attached))
		fwrite_crlf(body,strlen(body)+1,fp);	/* Write additional NULL */
	else
		fwrite("\0",1,1,fp);               /* Write NULL */
rswindell's avatar
rswindell committed
606
	if(cfg.log&LOG_NETMAIL)
607
608
609
		lprintf(LOG_INFO, "Created NetMail (%s)%s from %s (%s) to %s (%s), attr: %04hX, subject: %s"
			,fname, file_attached ? " with attachment" : ""
			,from, smb_faddrtoa(&faddr, tmp), to, smb_faddrtoa(&dest, NULL), hdr.attr, subject);
610
	return fclose(fp);
611
612
613
614
615
616
617
618
619
620
621
}

/******************************************************************************
 This function takes the contents of 'infile' and puts it into a netmail
 message bound for addr.
******************************************************************************/
void file_to_netmail(FILE *infile,char *title,faddr_t addr,char *to)
{
	char *buf,*p;
	long l,m,len;

622
623
624
625
	l=len=ftell(infile);
	if(len>8192L)
		len=8192L;
	rewind(infile);
deuce's avatar
deuce committed
626
	if((buf=(char *)malloc(len+1))==NULL) {
627
		lprintf(LOG_ERR,"ERROR line %d allocating %lu for file to netmail buf",__LINE__,len);
628
629
		return; 
	}
630
631
632
	while((m=fread(buf,1,(len>8064L) ? 8064L:len,infile))>0) {
		buf[m]=0;
		if(l>8064L && (p=strrchr(buf,'\n'))!=NULL) {
633
			p++;
634
635
			if(*p) {
				*p=0;
636
				p++;
637
638
639
				fseek(infile,-1L,SEEK_CUR);
				while(*p) { 			/* Seek back to end of last line */
					p++;
640
641
642
643
					fseek(infile,-1L,SEEK_CUR); 
				} 
			} 
		}
644
645
		if(ftell(infile)<l)
			strcat(buf,"\r\nContinued in next message...\r\n");
646
		create_netmail(to,/* msg: */NULL, title,buf,addr,/* attachment: */FALSE); 
647
	}
deuce's avatar
deuce committed
648
	free(buf);
649
}
650
651
652
653

/* Returns TRUE if area is linked with specified node address */
BOOL area_is_linked(unsigned area_num, faddr_t* addr)
{
654
655
	unsigned i;
	for(i=0;i<cfg.area[area_num].uplinks;i++)
656
657
658
659
660
		if(!memcmp(addr,&cfg.area[area_num].uplink[i],sizeof(faddr_t)))
			return TRUE;
	return FALSE;
}

661
662
663
664
665
666
667
668
669
/******************************************************************************
 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.
******************************************************************************/
void notify_list(void)
{
	FILE *	tmpf;
	char	str[256];
deuce's avatar
deuce committed
670
	uint	i,k;
671
672
673
674
675
676
677

	for(k=0;k<cfg.nodecfgs;k++) {

		if(!(cfg.nodecfg[k].attr&SEND_NOTIFY))
			continue;

		if((tmpf=tmpfile())==NULL) {
678
			lprintf(LOG_ERR,"ERROR line %d couldn't open tmpfile",__LINE__);
679
680
			return; 
		}
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705

		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");
		fprintf(tmpf,"Packet Type       %s\r\n"
			,cfg.nodecfg[k].pkt_type==PKT_TWO ? "2"
			:cfg.nodecfg[k].pkt_type==PKT_TWO_TWO ? "2.2":"2+");
		fprintf(tmpf,"Archive Type      %s\r\n"
			,(cfg.nodecfg[k].arctype>cfg.arcdefs) ?
			 "None":cfg.arcdef[cfg.nodecfg[k].arctype].name);
		fprintf(tmpf,"Mail Status       %s\r\n"
			,cfg.nodecfg[k].attr&ATTR_CRASH ? "Crash"
			:cfg.nodecfg[k].attr&ATTR_HOLD ? "Hold" : "None");
		fprintf(tmpf,"Direct            %s\r\n"
			,cfg.nodecfg[k].attr&ATTR_DIRECT ? "Yes":"No");
		fprintf(tmpf,"Passive           %s\r\n"
			,cfg.nodecfg[k].attr&ATTR_PASSIVE ? "Yes":"No");
		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;
706
707
708
			if(area_is_linked(i,&cfg.nodecfg[k].faddr))
				fprintf(tmpf,"%s",str); 
		}
709
710

		if(ftell(tmpf))
711
			file_to_netmail(tmpf,"SBBSecho Notify List",cfg.nodecfg[k].faddr, /* To: */NULL);
712
713
		fclose(tmpf); 
	}
714
}
715

716
717
718
719
/******************************************************************************
 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).
******************************************************************************/
720
721
722
723
724
725
enum arealist_type {
	 AREALIST_ALL			// %LIST
	,AREALIST_CONNECTED		// %QUERY
	,AREALIST_UNLINKED		// %UNLINKED
};
void netmail_arealist(enum arealist_type type, faddr_t addr, char* to)
726
{
727
728
	char str[256],title[128],match,*p,*tp;
	int i,j,k,x,y;
729
	str_list_t	area_list;
730

731
	if(type == AREALIST_ALL)
732
		strcpy(title,"List of Available Areas");
733
	else if(type == AREALIST_CONNECTED)
734
735
736
737
		strcpy(title,"List of Connected Areas");
	else
		strcpy(title,"List of Unlinked Areas");

738
739
	if((area_list=strListInit()) == NULL) {
		lprintf(LOG_ERR,"ERROR line %d couldn't allocate string list",__LINE__);
740
741
		return; 
	}
742

743
744
745
746
747
748
	/* Include relevant areas from the area file (e.g. areas.bbs): */
	for(i=0;i<cfg.areas;i++) {
		if((type == AREALIST_CONNECTED || (misc&ELIST_ONLY)) && !area_is_linked(i,&addr))
			continue;
		if(type == AREALIST_UNLINKED && area_is_linked(i,&addr))
			continue;
749
		strListPush(&area_list, cfg.area[i].name); 
750
	} 
751

752
	if(type != AREALIST_CONNECTED) {
753
754
755
756
757
758
		i=matchnode(addr,0);
		if(i<cfg.nodecfgs) {
			for(j=0;j<cfg.listcfgs;j++) {
				match=0;
				for(k=0;k<cfg.listcfg[j].numflags;k++) {
					if(match) break;
759
					for(x=0;x<cfg.nodecfg[i].numflags;x++) {
760
761
						if(!stricmp(cfg.listcfg[j].flag[k].flag
							,cfg.nodecfg[i].flag[x].flag)) {
762
763
							FILE* fp;
							if((fp=fopen(cfg.listcfg[j].listpath,"r"))==NULL) {
764
765
								lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
									,errno,strerror(errno),__LINE__,cfg.listcfg[j].listpath);
766
								match=1;
767
768
								break; 
							}
769
							while(!feof(fp)) {
770
								memset(str,0,sizeof(str));
771
								if(!fgets(str,sizeof(str),fp))
772
									break;
773
								truncsp(str);
774
								p=str;
775
								SKIP_WHITESPACE(p);
776
								if(*p==0 || *p==';')     /* Ignore Blank and Comment Lines */
777
									continue;
778
779
780
								tp=p;
								FIND_WHITESPACE(tp);
								*tp=0;
781
782
783
								for(y=0;y<cfg.areas;y++)
									if(!stricmp(cfg.area[y].name,p))
										break;
784
785
								if(y>=cfg.areas || !area_is_linked(y,&addr))
									strListPush(&area_list, p); 
786
							}
787
							fclose(fp);
788
							match=1;
789
							break; 
790
791
						}
					}
792
793
794
795
				} 
			} 
		} 
	}
796
797
	strListSortAlpha(area_list);
	if(!strListCount(area_list))
798
		create_netmail(to,/* msg: */NULL,title,"None.",addr,/* attachment: */FALSE);
799
800
801
802
803
804
805
806
807
808
809
810
	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);
811
}
812

rswindell's avatar
rswindell committed
813
int check_elists(char *areatag,faddr_t addr)
814
815
{
	FILE *stream;
816
	char str[1025],quit=0,*p,*tp;
817
	int i,j,k,x,match=0;
818

rswindell's avatar
rswindell committed
819
820
821
822
823
824
825
826
827
	i=matchnode(addr,0);
	if(i<cfg.nodecfgs) {
		for(j=0;j<cfg.listcfgs;j++) {
			quit=0;
			for(k=0;k<cfg.listcfg[j].numflags;k++) {
				if(quit) break;
				for(x=0;x<cfg.nodecfg[i].numflags;x++)
					if(!stricmp(cfg.listcfg[j].flag[k].flag
						,cfg.nodecfg[i].flag[x].flag)) {
828
						if((stream=fopen(cfg.listcfg[j].listpath,"r"))==NULL) {
829
830
							lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
								,errno,strerror(errno),__LINE__,cfg.listcfg[j].listpath);
rswindell's avatar
rswindell committed
831
							quit=1;
832
833
							break; 
						}
rswindell's avatar
rswindell committed
834
						while(!feof(stream)) {
835
							if(!fgets(str,sizeof(str),stream))
rswindell's avatar
rswindell committed
836
837
								break;
							p=str;
838
							SKIP_WHITESPACE(p);
rswindell's avatar
rswindell committed
839
840
							if(*p==';')     /* Ignore Comment Lines */
								continue;
841
842
843
844
							tp=p;
							FIND_WHITESPACE(tp);
							*tp=0;
							if(!stricmp(areatag,p)) {
rswindell's avatar
rswindell committed
845
								match=1;
846
847
848
								break; 
							} 
						}
rswindell's avatar
rswindell committed
849
						fclose(stream);
850
						quit=1;
rswindell's avatar
rswindell committed
851
852
						if(match)
							return(match);
853
854
855
856
857
						break; 
					} 
			} 
		} 
	}
rswindell's avatar
rswindell committed
858
	return(match);
859
860
861
862
}
/******************************************************************************
 Used by AREAFIX to add/remove/change areas in the areas file
******************************************************************************/
863
void alter_areas(str_list_t add_area, str_list_t del_area, faddr_t addr, char* to)
864
865
{
	FILE *nmfile,*afilein,*afileout,*fwdfile;
866
867
	char str[1024],fields[1024],field1[256],field2[256],field3[256]
		,outpath[MAX_PATH+1]
868
		,*outname,*p,*tp,nomatch=0,match=0;
869
	int i,j,k,x,y;
870
871
	ulong tagcrc;

872
873
	SAFECOPY(outpath,cfg.areafile);
	*getfname(outpath)=0;
874
	if((outname=tempnam(outpath,"AREAS"))==NULL) {
875
		lprintf(LOG_ERR,"ERROR tempnam(%s,AREAS)",outpath);
876
877
		return; 
	}
878
	if((nmfile=tmpfile())==NULL) {
879
		lprintf(LOG_ERR,"ERROR in tmpfile()");
880
		free(outname);
881
882
		return; 
	}
883
	if((afileout=fopen(outname,"w+"))==NULL) {
884
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,outname);
885
886
		fclose(nmfile);
		free(outname);
887
888
		return; 
	}
889
	if((afilein=fopen(cfg.areafile,"r"))==NULL) {
890
		lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s",errno,strerror(errno),__LINE__,cfg.areafile);
891
892
893
		fclose(afileout);
		fclose(nmfile);
		free(outname);
894
895
		return; 
	}
896
	while(!feof(afilein)) {
897
		if(!fgets(fields,sizeof(fields),afilein))
898
899
900
			break;
		truncsp(fields);
		p=fields;
901
		SKIP_WHITESPACE(p);
902
		if(*p==';') {    /* Skip Comment Lines */
903
			fprintf(afileout,"%s\n",fields);
904
905
			continue; 
		}
906
		SAFECOPY(field1,p);         /* Internal Code Field */
907
		truncstr(field1," \t\r\n");
908
909
910
		FIND_WHITESPACE(p);
		SKIP_WHITESPACE(p);
		SAFECOPY(field2,p);         /* Areatag Field */
911
		truncstr(field2," \t\r\n");
912
913
		FIND_WHITESPACE(p);
		SKIP_WHITESPACE(p);
914
		if((tp=strchr(p,';'))!=NULL) {
915
916
			SAFECOPY(field3,p);     /* Comment Field (if any) */
			FIND_WHITESPACE(tp);
917
918
			*tp=0; 
		}
919
920
		else
			field3[0]=0;
921
		if(strListCount(del_area)) { 				/* Check for areas to remove */
922
			lprintf(LOG_DEBUG,"Removing areas for %s from %s", smb_faddrtoa(&addr,NULL), cfg.areafile);
923
924
925
926
927
928
			for(i=0;del_area[i]!=NULL;i++) {
				if(!stricmp(del_area[i],field2) ||
					!stricmp(del_area[0],"-ALL"))     /* Match Found */
					break; 
			}
			if(del_area[i]!=NULL) {
929
930
				for(i=0;i<cfg.areas;i++) {
					if(!stricmp(field2,cfg.area[i].name)) {
931
						lprintf(LOG_DEBUG,"Unlinking area (%s) for %s in %s", field2, smb_faddrtoa(&addr,NULL), cfg.areafile);
932
						if(!area_is_linked(i,&addr)) {
933
934
							fprintf(afileout,"%s\n",fields);
							/* bugfix here Mar-25-2004 (wasn't breaking for "-ALL") */
935
							if(stricmp(del_area[0],"-ALL"))
936
937
938
								fprintf(nmfile,"%s not connected.\r\n",field2);
							break; 
						}
939

940
941
942
943
944
945
						/* Added 12/4/95 to remove uplink from connected uplinks */

						for(k=j;k<cfg.area[i].uplinks-1;k++)
							memcpy(&cfg.area[i].uplink[k],&cfg.area[i].uplink[k+1]
								,sizeof(faddr_t));
						--cfg.area[i].uplinks;
946
947
						if(cfg.area[i].uplinks==0) {
							FREE_AND_NULL(cfg.area[i].uplink);
948
						} else {
949
							if((cfg.area[i].uplink=(faddr_t *)
deuce's avatar
deuce committed
950
								realloc(cfg.area[i].uplink,sizeof(faddr_t)
951
								*(cfg.area[i].uplinks)))==NULL) {
952
953
								lprintf(LOG_ERR,"ERROR line %d allocating memory for area "
									"#%u uplinks.",__LINE__,i+1);
954
								bail(1); 
955
								return;
956
							}
957
						}
958
959
960
961
962
963
964

						fprintf(afileout,"%-16s%-23s ",field1,field2);
						for(j=0;j<cfg.area[i].uplinks;j++) {
							if(!memcmp(&cfg.area[i].uplink[j],&addr
								,sizeof(faddr_t)))
								continue;
							fprintf(afileout,"%s "
965
966
								,smb_faddrtoa(&cfg.area[i].uplink[j],NULL)); 
						}
967
968
						if(field3[0])
							fprintf(afileout,"%s",field3);
969
						fprintf(afileout,"\n");
970
						fprintf(nmfile,"%s removed.\r\n",field2);
971
972
973
						break; 
					} 
				}
974
				if(i==cfg.areas)			/* Something screwy going on */
975
					fprintf(afileout,"%s\n",fields);
976
977
978
979
				continue; 
			} 				/* Area match so continue on */
		}
		if(strListCount(add_area)) { 				/* Check for areas to add */
980
			lprintf(LOG_DEBUG,"Adding areas for %s to %s", smb_faddrtoa(&addr,NULL), cfg.areafile);
981
982
983
			for(i=0;add_area[i]!=NULL;i++)
				if(!stricmp(add_area[i],field2) ||
					!stricmp(add_area[0],"+ALL"))      /* Match Found */
984
					break;
985
986
987
			if(add_area[i]!=NULL) {
				if(stricmp(add_area[i],"+ALL"))
					add_area[i][0]=0;  /* So we can check other lists */
988
989
				for(i=0;i<cfg.areas;i++) {
					if(!stricmp(field2,cfg.area[i].name)) {
990
						lprintf(LOG_DEBUG,"Linking area (%s) for %s in %s", field2, smb_faddrtoa(&addr,NULL), cfg.areafile);
991
						if(area_is_linked(i,&addr)) {
992
							fprintf(afileout,"%s\n",fields);
993
							fprintf(nmfile,"%s already connected.\r\n",field2);
994
995
							break; 
						}
rswindell's avatar
rswindell committed
996
						if((misc&ELIST_ONLY) && !check_elists(field2,addr)) {
997
							fprintf(afileout,"%s\n",fields);
998
999
							break; 
						}
1000
1001
1002
1003
1004

						/* Added 12/4/95 to add uplink to connected uplinks */

						++cfg.area[i].uplinks;
						if((cfg.area[i].uplink=(faddr_t *)
deuce's avatar
deuce committed
1005
							realloc(cfg.area[i].uplink,sizeof(faddr_t)
1006
							*(cfg.area[i].uplinks)))==NULL) {
1007
1008
							lprintf(LOG_ERR,"ERROR line %d allocating memory for area "
								"#%u uplinks.",__LINE__,i+1);
1009
1010
1011
							bail(1); 
							return;
						}
1012
						memcpy(&cfg.area[i].uplink[cfg.area[i].uplinks-1],&addr,sizeof(faddr_t));
1013
1014
1015
1016

						fprintf(afileout,"%-16s%-23s ",field1,field2);
						for(j=0;j<cfg.area[i].uplinks;j++)
							fprintf(afileout,"%s "
1017
								,smb_faddrtoa(&cfg.area[i].uplink[j],NULL));
1018
1019
						if(field3[0])
							fprintf(afileout,"%s",field3);
1020
						fprintf(afileout,"\n");
1021
						fprintf(nmfile,"%s added.\r\n",field2);
1022
1023
1024
						break; 
					} 
				}
1025
				if(i==cfg.areas)			/* Something screwy going on */
1026
					fprintf(afileout,"%s\n",fields);
1027
1028
1029
1030
1031
1032
				continue;  					/* Area match so continue on */
			}
			nomatch=1; 						/* This area wasn't in there */
		}
		fprintf(afileout,"%s\n",fields);	/* No match so write back line */
	}
1033
	fclose(afilein);
1034
	if(nomatch || (strListCount(add_area) && !stricmp(add_area[0],"+ALL"))) {
1035
1036
1037
1038
1039
1040
		i=matchnode(addr,0);
		if(i<cfg.nodecfgs) {
			for(j=0;j<cfg.listcfgs;j++) {
				match=0;
				for(k=0;k<cfg.listcfg[j].numflags;k++) {
					if(match) break;
1041
					for(x=0;x<cfg.nodecfg[i].numflags;x++) {
1042
1043
1044
						if(!stricmp(cfg.listcfg[j].flag[k].flag
							,cfg.nodecfg[i].flag[x].flag)) {
							if((fwdfile=tmpfile())==NULL) {
1045
								lprintf(LOG_ERR,"ERROR line %d opening forward temp "
1046
1047
									"file",__LINE__);
								match=1;
1048
1049
								break; 
							}
1050
							if((afilein=fopen(cfg.listcfg[j].listpath,"r"))==NULL) {
1051
1052
								lprintf(LOG_ERR,"ERROR %u (%s) line %d opening %s"
									,errno,strerror(errno),__LINE__,cfg.listcfg[j].listpath);
1053
1054
								fclose(fwdfile);
								match=1;
1055
1056
								break; 
							}
1057
							while(!feof(afilein)) {
1058
								if(!fgets(str,sizeof(str),afilein))
1059
									break;
1060
								p=str;
1061
								SKIP_WHITESPACE(p);
1062
1063
								if(*p==';')     /* Ignore Comment Lines */
									continue;
1064
1065
1066
								tp=p;
								FIND_WHITESPACE(tp);
								*tp=0;
1067
								if(add_area[0]!=NULL && stricmp(add_area[0],"+ALL")==0) {
1068
1069
									SAFECOPY(tmp,p);
									tagcrc=crc32(strupr(tmp),0);
1070
1071
1072
1073
									for(y=0;y<cfg.areas;y++)
										if(tagcrc==cfg.area[y].tag)
											break;
									if(y<cfg.areas)
1074
1075
1076
1077
1078
1079
										continue; 
								}
								for(y=0;add_area[y]!=NULL;y++)
									if((!stricmp(add_area[y],str) &&
										add_area[y][0]) ||
										!stricmp(add_area[0],"+ALL"))