sbbsecho.c 192 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
#include "sbbs.h"			/* load_cfg() */
#include "sbbsdefs.h"
#include "smblib.h"
#include "scfglib.h"
#include "sbbsecho.h"
deuce's avatar
deuce committed
58
#include "genwrap.h"		/* PLATFORM_DESC */
rswindell's avatar
rswindell committed
59
60
61
#include "xpendian.h"

#define MAX_OPEN_SMBS	10
62
63

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

rswindell's avatar
rswindell committed
79
80
81
82
83
84
/* statistics */
ulong netmail=0;	/* imported */
ulong echomail=0;	/* imported */
ulong exported_netmail=0;
ulong exported_echomail=0;
ulong packed_netmail=0;
85
86
87
88
ulong packets_sent=0;
ulong packets_imported=0;
ulong bundles_sent=0;
ulong bundles_unpacked=0;
rswindell's avatar
rswindell committed
89

rswindell's avatar
rswindell committed
90
int cur_smb=0;
91
FILE *fidologfile=NULL;
92
str_list_t twit_list;
rswindell's avatar
rswindell committed
93
str_list_t bad_areas;
94

rswindell's avatar
rswindell committed
95
96
97
98
99
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];
100

rswindell's avatar
rswindell committed
101
102
103
104
bool pause_on_exit=false;
bool pause_on_abend=false;
bool mtxfile_locked=false;
bool terminated=false;
105

106
107
str_list_t	locked_bso_nodes;

108
109
110
111
112
int lprintf(int level, char *fmt, ...)
#if defined(__GNUC__)   // Catch printf-format errors
    __attribute__ ((format (printf, 2, 3)));
#endif
;
rswindell's avatar
rswindell committed
113
int mv(const char *insrc, const char *indest, bool copy);
114
time32_t fmsgtime(const char *str);
rswindell's avatar
rswindell committed
115
void export_echomail(const char *sub_code, const nodecfg_t*, bool rescan);
rswindell's avatar
rswindell committed
116
const char* area_desc(const char* areatag);
rswindell's avatar
rswindell committed
117

118
119
120
121
/* FTN-compliant "Program Identifier"/PID (also used as a "Tosser Identifier"/TID) */
const char* sbbsecho_pid(void)
{
	static char str[256];
rswindell's avatar
rswindell committed
122

123
124
125
126
127
128
129
130
131
132
	sprintf(str, "SBBSecho %u.%02u-%s r%s %s %s"
		,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,revision,__DATE__,compiler);

	return str;
}

/* for *.bsy file contents: */
const char* program_id(void)
{
	static char str[256];
rswindell's avatar
rswindell committed
133

134
135
136
137
138
	SAFEPRINTF2(str, "%u %s", getpid(), sbbsecho_pid());

	return str;
}

rswindell's avatar
rswindell committed
139
140
141
142
143
144
145
146
147
148
const char* tear_line(void)
{
	static char str[256];

	sprintf(str,"--- SBBSecho %u.%02u-%s\r"
		,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC);

	return str;
}

149
150
151
152
const char default_domain[] = "fidonet";

const char* zone_domain(uint16_t zone)
{
153
154
155
156
	for(unsigned i = 0; i < cfg.domain_count; i++)
		for(unsigned j = 0; j < cfg.domain_list[i].zone_count; j++)
			if(cfg.domain_list[i].zone_list[j] == zone)
				return cfg.domain_list[i].name;
157
158
159
160
161
162

	return default_domain;
}

const char* zone_root_outbound(uint16_t zone)
{
163
164
165
166
	for(unsigned i = 0; i < cfg.domain_count; i++)
		for(unsigned j = 0; j < cfg.domain_list[i].zone_count; j++)
			if(cfg.domain_list[i].zone_list[j] == zone)
				return cfg.domain_list[i].root;
167
168
169
170

	return cfg.outbound;
}

171
172
173
174
175
176
177
178
/* Allocates the buffer and returns the data (or NULL) */
char* parse_control_line(const char* fmsgbuf, const char* kludge)
{
	char*	p;
	char	str[128];

	if(fmsgbuf == NULL)
		return NULL;
179
	SAFEPRINTF(str, "\1%s", kludge);
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
	p = strstr(fmsgbuf, str);
	if(p == NULL)
		return NULL;
	if(p != fmsgbuf && *(p-1) != '\r' && *(p-1) != '\n')	/* Kludge must start new-line */
		return NULL;
	SAFECOPY(str, p);

	/* Terminate at the CR */
	p = strchr(str, '\r');
	if(p == NULL)
		return NULL;
	*p = 0;

	/* Only return the data portion */
	p = str + strlen(kludge) + 1;
	SKIP_WHITESPACE(p);
	return strdup(p);
}

rswindell's avatar
rswindell committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/* Write an FTS-4009 compliant "Via" control line to the (netmail) file */
int fwrite_via_control_line(FILE* fp, fidoaddr_t* addr)
{
	time_t t = time(NULL);
	struct tm* tm = gmtime(&t);
	return fprintf(fp,"\1Via %s @%04u%02u%02u.%02u%02u%02u.UTC "
		"SBBSecho %u.%02u-%s r%s\r"
		,smb_faddrtoa(addr, NULL)
		,tm->tm_year+1900
		,tm->tm_mon+1
		,tm->tm_mday
		,tm->tm_hour
		,tm->tm_min
		,tm->tm_sec
		,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,revision);
}
215
216
217
218
219
220
221
222

int fwrite_intl_control_line(FILE* fp, fmsghdr_t* hdr)
{
	return fprintf(fp,"\1INTL %hu:%hu/%hu %hu:%hu/%hu\r"
		,hdr->destzone,hdr->destnet,hdr->destnode
		,hdr->origzone,hdr->orignet,hdr->orignode);
}

223
224
225
226
227
228
229
230
typedef struct echostat_msg {
	char msg_id[128];
	char reply_id[128];
	char pid[128];
	char tid[128];
	char to[FIDO_NAME_LEN];
	char from[FIDO_NAME_LEN];
	char subj[FIDO_SUBJ_LEN];
231
232
	char msg_tz[128];
	time_t msg_time;
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
	time_t localtime;
	size_t length;
	fidoaddr_t origaddr;
	fidoaddr_t pkt_orig;
} echostat_msg_t;

enum echostat_msg_type {
	ECHOSTAT_MSG_RECEIVED,
	ECHOSTAT_MSG_IMPORTED,
	ECHOSTAT_MSG_EXPORTED,
	ECHOSTAT_MSG_DUPLICATE,
	ECHOSTAT_MSG_CIRCULAR,
	ECHOSTAT_MSG_TYPES
};

const char* echostat_msg_type[ECHOSTAT_MSG_TYPES] = {
	"Received",
	"Imported",
	"Exported",
	"Duplicate",
	"Circular",
};

typedef struct echostat {
	char tag[FIDO_AREATAG_LEN + 1];
258
	bool known;	// listed in Area File
259
260
261
262
263
264
265
266
	echostat_msg_t last[ECHOSTAT_MSG_TYPES];
	echostat_msg_t first[ECHOSTAT_MSG_TYPES];
	ulong total[ECHOSTAT_MSG_TYPES];
} echostat_t;

echostat_t* echostat;
size_t echostat_count;

267
268
int echostat_compare(const void* c1, const void* c2)
{
269
270
	echostat_t* stat1 = (echostat_t*)c1;
	echostat_t* stat2 = (echostat_t*)c2;
271
272
273
274

	return stricmp(stat1->tag, stat2->tag);
}

275
276
277
278
echostat_msg_t parse_echostat_msg(str_list_t ini, const char* section, const char* prefix)
{
	char str[128];
	char key[128];
rswindell's avatar
rswindell committed
279
	echostat_msg_t msg = {{0}};
280
281
282
283
284
285
286
287

	sprintf(key, "%s.to", prefix),			iniGetString(ini, section, key, NULL, msg.to);
	sprintf(key, "%s.from", prefix),		iniGetString(ini, section, key, NULL, msg.from);
	sprintf(key, "%s.subj", prefix),		iniGetString(ini, section, key, NULL, msg.subj);
	sprintf(key, "%s.msg_id", prefix),		iniGetString(ini, section, key, NULL, msg.msg_id);
	sprintf(key, "%s.reply_id", prefix),	iniGetString(ini, section, key, NULL, msg.reply_id);
	sprintf(key, "%s.pid", prefix),			iniGetString(ini, section, key, NULL, msg.pid);
	sprintf(key, "%s.tid", prefix),			iniGetString(ini, section, key, NULL, msg.tid);
288
289
	sprintf(key, "%s.msg_tz", prefix),		iniGetString(ini, section, key, NULL, msg.msg_tz);
	sprintf(key, "%s.msg_time", prefix),	msg.msg_time = iniGetDateTime(ini, section, key, 0);
290
291
292
293
294
295
296
297
298
299
	sprintf(key, "%s.localtime", prefix),	msg.localtime = iniGetDateTime(ini, section, key, 0);
	sprintf(key, "%s.length", prefix),		msg.length = (size_t)iniGetBytes(ini, section, key, 1, 0);
	sprintf(key, "%s.origaddr", prefix),	iniGetString(ini, section, key, NULL, str);
	if(str[0])	 msg.origaddr = atofaddr(str);
	sprintf(key, "%s.pkt_orig", prefix),	iniGetString(ini, section, key, NULL, str);
	if(str[0])	 msg.pkt_orig = atofaddr(str);

	return msg;
}

300
echostat_msg_t fidomsg_to_echostat_msg(fmsghdr_t* hdr, fidoaddr_t* pkt_orig, const char* fmsgbuf)
301
302
{
	char* p;
rswindell's avatar
rswindell committed
303
	echostat_msg_t msg = {{0}};
304

305
306
307
308
	SAFECOPY(msg.to		, hdr->to);
	SAFECOPY(msg.from	, hdr->from);
	SAFECOPY(msg.subj	, hdr->subj);
	msg.msg_time		= fmsgtime(hdr->time);
309
	msg.localtime		= time(NULL);
310
311
312
313
	msg.origaddr.zone	= hdr->origzone;
	msg.origaddr.net	= hdr->orignet;
	msg.origaddr.node	= hdr->orignode;
	msg.origaddr.point	= hdr->origpoint;
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
	if(pkt_orig != NULL)
		msg.pkt_orig	= *pkt_orig;
	if((p = parse_control_line(fmsgbuf, "MSGID:")) != NULL) {
		SAFECOPY(msg.msg_id, p);
		free(p);
	}
	if((p = parse_control_line(fmsgbuf, "REPLY:")) != NULL) {
		SAFECOPY(msg.reply_id, p);
		free(p);
	}
	if((p = parse_control_line(fmsgbuf, "PID:")) != NULL) {
		SAFECOPY(msg.pid, p);
		free(p);
	}
	if((p = parse_control_line(fmsgbuf, "TID:")) != NULL) {
		SAFECOPY(msg.tid, p);
		free(p);
	}
332
333
	if((p = parse_control_line(fmsgbuf, "TZUTC:")) != NULL
		|| (p = parse_control_line(fmsgbuf, "TZUTCINFO:")) != NULL) {
334
335
336
		SAFECOPY(msg.msg_tz, p);
		free(p);
	}
337
338
339
340
341
342
	if(fmsgbuf != NULL)
		msg.length = strlen(fmsgbuf);

	return msg;
}

343
echostat_msg_t smsg_to_echostat_msg(smbmsg_t smsg, size_t msglen, fidoaddr_t addr)
344
345
{
	char* p;
rswindell's avatar
rswindell committed
346
	echostat_msg_t emsg = {{0}};
347
348
349
350

	SAFECOPY(emsg.to	, smsg.to);
	SAFECOPY(emsg.from	, smsg.from);
	SAFECOPY(emsg.subj	, smsg.subj);
351
	emsg.msg_time		= smsg.hdr.when_written.time;
352
	emsg.localtime		= time(NULL);
353
	SAFECOPY(emsg.msg_tz, smb_zonestr(smsg.hdr.when_written.zone, NULL));
354
	if(smsg.from_net.type == NET_FIDO && smsg.from_net.addr != NULL)
355
		emsg.origaddr	= *(fidoaddr_t*)smsg.from_net.addr;
356
357
358
359
360
361
362
363
	if((p = smsg.ftn_msgid) != NULL)
		SAFECOPY(emsg.msg_id, p);
	if((p = smsg.ftn_reply) != NULL)
		SAFECOPY(emsg.reply_id, p);
	if((p = smsg.ftn_pid) != NULL)
		SAFECOPY(emsg.pid, p);
	if((p = smsg.ftn_tid) != NULL)
		SAFECOPY(emsg.tid, p);
364
365
	else
		SAFECOPY(emsg.tid, sbbsecho_pid());
366
	emsg.length = msglen;
367
	emsg.pkt_orig = addr;
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

	return emsg;
}

void new_echostat_msg(echostat_t* stat, enum echostat_msg_type type, echostat_msg_t msg)
{
	stat->last[type] = msg;
	if(stat->first[type].localtime == 0)
		stat->first[type] = msg;
	stat->total[type]++;
}

size_t read_echostats(const char* fname, echostat_t **echostat)
{
	str_list_t ini;
	FILE* fp = iniOpenFile(fname, FALSE);
	if(fp == NULL)
		return 0;

	ini = iniReadFile(fp);
	iniCloseFile(fp);

	str_list_t echoes = iniGetSectionList(ini, NULL);
	if(echoes == NULL) {
		iniFreeStringList(ini);
		return 0;
	}

	size_t echo_count = strListCount(echoes);
	*echostat = malloc(sizeof(echostat_t) * echo_count);
	if(*echostat == NULL) {
		iniFreeStringList(echoes);
		iniFreeStringList(ini);
		return 0;
	}
	for(unsigned i = 0; i < echo_count; i++) {
		echostat_t* stat = &(*echostat)[i];
		SAFECOPY(stat->tag, echoes[i]);
406
407
408
		str_list_t keys = iniGetSection(ini, stat->tag);
		if(keys == NULL)
			continue;
409
410
411
		for(int type = 0; type < ECHOSTAT_MSG_TYPES; type++) {
			char prefix[32];
			sprintf(prefix, "First%s", echostat_msg_type[type])
412
				,stat->first[type]	= parse_echostat_msg(keys, NULL, prefix);
413
			sprintf(prefix, "Last%s", echostat_msg_type[type])
414
				,stat->last[type]	= parse_echostat_msg(keys, NULL, prefix);
415
			sprintf(prefix, "Total%s", echostat_msg_type[type])
416
				,stat->total[type] = iniGetLongInt(keys, NULL, prefix, 0);
417
		}
418
419
		stat->known = iniGetBool(keys, NULL, "Known", false);
		iniFreeStringList(keys);
420
421
422
423
	}
	iniFreeStringList(echoes);
	iniFreeStringList(ini);

424
	lprintf(LOG_DEBUG, "Read %lu echo statistics from %s", (ulong)echo_count, fname);
425
426
427
428
429
430
431
432
433
434
435
436
437
	return echo_count;
}

/* Mimic iniSetDateTime() */
const char* iniTimeStr(time_t t)
{
	static char	tstr[32];
	char	tmp[32];
	char*	p;

	if(t == 0)
		return "Never";
	if((p = ctime_r(&t, tmp)) == NULL)
438
		sprintf(tstr, "0x%lx", (ulong)t);
439
440
	else
		sprintf(tstr, "%.3s %.2s %.4s %.8s", p+4, p+8, p+20, p+11);
rswindell's avatar
rswindell committed
441

442
443
444
	return tstr;
}

rswindell's avatar
rswindell committed
445
void fwrite_echostat_msg(FILE* fp, echostat_msg_t msg, const char* prefix)
446
{
rswindell's avatar
rswindell committed
447
	echostat_msg_t zero = {{0}};
448
449
450
451
452
453
454
455
456
	if(memcmp(&msg, &zero, sizeof(msg)) == 0)
		return;
	if(msg.to[0])		fprintf(fp, "%s.to = %s\n"			, prefix, msg.to);
	if(msg.from[0])		fprintf(fp, "%s.from = %s\n"		, prefix, msg.from);
	if(msg.subj[0])		fprintf(fp, "%s.subj = %s\n"		, prefix, msg.subj);
	if(msg.msg_id[0])	fprintf(fp, "%s.msg_id = %s\n"		, prefix, msg.msg_id);
	if(msg.reply_id[0])	fprintf(fp, "%s.reply_id = %s\n"	, prefix, msg.reply_id);
	if(msg.pid[0])		fprintf(fp, "%s.pid = %s\n"			, prefix, msg.pid);
	if(msg.tid[0])		fprintf(fp, "%s.tid = %s\n"			, prefix, msg.tid);
457
	fprintf(fp, "%s.length = %lu\n"							, prefix, (ulong)msg.length);
458
459
	fprintf(fp, "%s.msg_time = %s\n"						, prefix, iniTimeStr(msg.msg_time));
	if(msg.msg_tz[0])	fprintf(fp, "%s.msg_tz = %s\n"		, prefix, msg.msg_tz);
460
	fprintf(fp, "%s.localtime = %s\n"						, prefix, iniTimeStr(msg.localtime));
461
462
	if(msg.origaddr.zone)
		fprintf(fp, "%s.origaddr = %s\n"					, prefix, faddrtoa(&msg.origaddr));
463
464
465
	fprintf(fp, "%s.pkt_orig = %s\n"						, prefix, faddrtoa(&msg.pkt_orig));
}

rswindell's avatar
rswindell committed
466
467
468
469
470
471
472
473
474
475
476
477
478
void fwrite_echostat(FILE* fp, echostat_t* stat)
{
	fprintf(fp, "[%s]\n"						, stat->tag);
	fprintf(fp,	"Known = %s\n"					, stat->known ? "true" : "false");
	for(int type = 0; type < ECHOSTAT_MSG_TYPES; type++) {
		char prefix[32];
		sprintf(prefix, "First%s", echostat_msg_type[type])	, fwrite_echostat_msg(fp, stat->first[type], prefix);
		sprintf(prefix, "Last%s", echostat_msg_type[type])	, fwrite_echostat_msg(fp, stat->last[type], prefix);
		if(stat->total[type] != 0)
			fprintf(fp,	"Total%s = %lu\n"					, echostat_msg_type[type], stat->total[type]);
	}
}

479
480
481
482
bool write_echostats(const char* fname, echostat_t* echostat, size_t echo_count)
{
	FILE* fp;

483
484
	qsort(echostat, echo_count, sizeof(echostat_t), echostat_compare);

485
486
487
488
	if((fp=fopen(fname, "w"))==NULL)
		return false;

	for(unsigned i = 0; i < echo_count; i++) {
rswindell's avatar
rswindell committed
489
		fwrite_echostat(fp, &echostat[i]);
490
491
492
493
494
495
		fprintf(fp, "\n");
	}
	fclose(fp);
	return true;
}

496
echostat_t* get_echostat(const char* tag, bool create)
497
498
499
500
501
{
	for(unsigned int i = 0; i < echostat_count; i++) {
		if(stricmp(echostat[i].tag, tag) == 0)
			return &echostat[i];
	}
502
503
	if(!create)
		return NULL;
504
505
506
507
508
509
510
511
512
513
	echostat_t* new_echostat = realloc(echostat, sizeof(echostat_t) * (echostat_count + 1));
	if(new_echostat == NULL)
		return NULL;
	echostat = new_echostat;
	memset(&echostat[echostat_count], 0, sizeof(echostat_t));
	SAFECOPY(echostat[echostat_count].tag, tag);

	return &echostat[echostat_count++];
}

rswindell's avatar
rswindell committed
514
515
516
517
/**********************/
/* Log print function */
/**********************/
int lprintf(int level, char *fmt, ...)
rswindell's avatar
rswindell committed
518
519
{
	va_list argptr;
rswindell's avatar
rswindell committed
520
	char sbuf[1024];
rswindell's avatar
rswindell committed
521
522
	int chcount;

rswindell's avatar
rswindell committed
523
524
	va_start(argptr,fmt);
	chcount=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
525
	sbuf[sizeof(sbuf)-1]=0;
rswindell's avatar
rswindell committed
526
	va_end(argptr);
527
528
	truncsp(sbuf);
	printf("%s\n",sbuf);
529

rswindell's avatar
rswindell committed
530
531
532
533
	if(fidologfile!=NULL && level<=cfg.log_level) {
	    time_t now = time(NULL);
		struct tm *tm;
		struct tm tmbuf = {0};
534
		char timestamp[128];
rswindell's avatar
rswindell committed
535
536
537
		strip_ctrl(sbuf, sbuf);
		if((tm = localtime(&now)) == NULL)
			tm = &tmbuf;
538
539
540
541
542
		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
543
544
		fflush(fidologfile);
	}
rswindell's avatar
rswindell committed
545
546
547
	return(chcount);
}

rswindell's avatar
rswindell committed
548
bool delfile(const char *filename, int line)
549
{
rswindell's avatar
rswindell committed
550
	lprintf(LOG_DEBUG, "Deleting %s (from line %u)", filename, line);
551
	if(remove(filename) != 0) {
552
553
		lprintf(LOG_ERR, "ERROR %u (%s) line %u removing file %s"
			,errno, strerror(errno), line, filename);
rswindell's avatar
rswindell committed
554
		return false;
555
	}
rswindell's avatar
rswindell committed
556
	return true;
557
558
}

559
560
561
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments             */
/*****************************************************************************/
rswindell's avatar
rswindell committed
562
char *mycmdstr(scfg_t* cfg, const char *instr, const char *fpath, const char *fspec)
563
{
564
    static char cmd[MAX_PATH+1];
565
566
567
    char str[256],str2[128];
    int i,j,len;

568
569
570
571
572
573
574
	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 */
575
					SAFECAT(cmd,fpath);
576
577
					break;
				case 'G':   /* Temp directory */
rswindell's avatar
rswindell committed
578
579
					if(cfg->temp_dir[0]!='\\'
						&& cfg->temp_dir[0]!='/'
580
						&& cfg->temp_dir[1]!=':') {
581
582
						SAFECOPY(str,cfg->node_dir);
						SAFECAT(str,cfg->temp_dir);
583
584
585
						if(FULLPATH(str2,str,40))
							strcpy(str,str2);
						backslash(str);
586
						SAFECAT(cmd,str);}
587
					else
588
						SAFECAT(cmd,cfg->temp_dir);
589
590
					break;
				case 'J':
rswindell's avatar
rswindell committed
591
592
					if(cfg->data_dir[0]!='\\'
						&& cfg->data_dir[0]!='/'
593
						&& cfg->data_dir[1]!=':') {
594
595
						SAFECOPY(str,cfg->node_dir);
						SAFECAT(str,cfg->data_dir);
596
						if(FULLPATH(str2,str,40))
597
							SAFECOPY(str,str2);
598
						backslash(str);
rswindell's avatar
rswindell committed
599
						SAFECAT(cmd,str);
600
					}
601
					else
602
						SAFECAT(cmd,cfg->data_dir);
603
604
					break;
				case 'K':
rswindell's avatar
rswindell committed
605
606
					if(cfg->ctrl_dir[0]!='\\'
						&& cfg->ctrl_dir[0]!='/'
607
						&& cfg->ctrl_dir[1]!=':') {
608
609
						SAFECOPY(str,cfg->node_dir);
						SAFECAT(str,cfg->ctrl_dir);
610
						if(FULLPATH(str2,str,40))
611
							SAFECOPY(str,str2);
612
						backslash(str);
rswindell's avatar
rswindell committed
613
						SAFECAT(cmd,str);
614
					}
615
					else
616
						SAFECAT(cmd,cfg->ctrl_dir);
617
618
					break;
				case 'N':   /* Node Directory (same as SBBSNODE environment var) */
619
					SAFECAT(cmd,cfg->node_dir);
620
621
					break;
				case 'O':   /* SysOp */
622
					SAFECAT(cmd,cfg->sys_op);
623
624
					break;
				case 'Q':   /* QWK ID */
625
					SAFECAT(cmd,cfg->sys_id);
626
627
					break;
				case 'S':   /* File Spec */
628
					SAFECAT(cmd,fspec);
629
630
					break;
				case '!':   /* EXEC Directory */
631
					SAFECAT(cmd,cfg->exec_dir);
632
					break;
633
634
                case '@':   /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
635
                    SAFECAT(cmd,cfg->exec_dir);
636
637
#endif
                    break;
638
639
				case '#':   /* Node number (same as SBBSNNUM environment var) */
					sprintf(str,"%d",cfg->node_num);
640
					SAFECAT(cmd,str);
641
642
643
					break;
				case '*':
					sprintf(str,"%03d",cfg->node_num);
644
					SAFECAT(cmd,str);
645
646
					break;
				case '%':   /* %% for percent sign */
647
					SAFECAT(cmd,"%");
648
					break;
649
650
				case '.':	/* .exe for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
651
					SAFECAT(cmd,".exe");
652
653
654
#endif
					break;
				case '?':	/* Platform */
655
					SAFECOPY(str,PLATFORM_DESC);
656
					strlwr(str);
657
					SAFECAT(cmd,str);
658
					break;
659
				default:    /* unknown specification */
660
					lprintf(LOG_ERR,"ERROR Checking Command Line '%s'",instr);
661
					bail(1);
rswindell's avatar
rswindell committed
662
					break;
663
			}
rswindell's avatar
rswindell committed
664
			j=strlen(cmd);
665
		}
666
		else
rswindell's avatar
rswindell committed
667
			cmd[j++]=instr[i];
668
}
669
	cmd[j]=0;
670

671
	return(cmd);
672
673
674
}

/****************************************************************************/
rswindell's avatar
rswindell committed
675
/* Runs an external program directly using system()							*/
676
677
678
/****************************************************************************/
int execute(char *cmdline)
{
rswindell's avatar
rswindell committed
679
	int retval;
rswindell's avatar
rswindell committed
680

rswindell's avatar
rswindell committed
681
682
683
684
	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));
685

rswindell's avatar
rswindell committed
686
	return retval;
687
}
688

689
/******************************************************************************
rswindell's avatar
rswindell committed
690
 Returns the local system address that best correlates with the passed address
691
******************************************************************************/
rswindell's avatar
rswindell committed
692
fidoaddr_t getsysfaddr(fidoaddr_t addr)
693
{
rswindell's avatar
rswindell committed
694
	nodecfg_t*	nodecfg;
695

rswindell's avatar
rswindell committed
696
697
698
699
700
701
702
	nodecfg = findnodecfg(&cfg, addr, /* exact */FALSE);
	if(nodecfg != NULL && nodecfg->local_addr.zone)
		return nodecfg->local_addr;

	int i;
	for(i=0; i<scfg.total_faddrs && addr.net != 0; i++)
		if(scfg.faddr[i].zone == addr.zone && scfg.faddr[i].net == addr.net)
rswindell's avatar
rswindell committed
703
			return scfg.faddr[i];
rswindell's avatar
rswindell committed
704
705
	for(i=0; i<scfg.total_faddrs; i++)
		if(scfg.faddr[i].zone == addr.zone)
rswindell's avatar
rswindell committed
706
707
			return scfg.faddr[i];
	return sys_faddr;
708
}
709

rswindell's avatar
rswindell committed
710
int get_outbound(fidoaddr_t dest, char* outbound, size_t maxlen, bool fileboxes)
711
{
rswindell's avatar
rswindell committed
712
713
	nodecfg_t*	nodecfg;

714
	strncpy(outbound,zone_root_outbound(dest.zone),maxlen);
rswindell's avatar
rswindell committed
715
716
717
718
719
720
	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/" */
721
722
723
			char* p = lastchar(outbound);
			if(IS_PATH_DELIM(*p))
				*p = 0;
rswindell's avatar
rswindell committed
724
725
726
727
728
729
			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);
		}
730
731
	}
	backslash(outbound);
732
733
734
	if(isdir(outbound))
		return 0;
	lprintf(LOG_DEBUG, "Creating outbound directory for %s: %s", smb_faddrtoa(&dest, NULL), outbound);
735
736
737
	return mkpath(outbound);
}

rswindell's avatar
rswindell committed
738
739
740
const char* get_current_outbound(fidoaddr_t dest, bool fileboxes)
{
	static char outbound[MAX_PATH+1];
741
742
743
744
	if(get_outbound(dest, outbound, sizeof(outbound)-1, fileboxes) != 0) {
		lprintf(LOG_ERR, "Error creating outbound directory");
		return NULL;
	}
rswindell's avatar
rswindell committed
745
746
747
748
	return outbound;
}

bool bso_lock_node(fidoaddr_t dest)
749
{
rswindell's avatar
rswindell committed
750
	const char* outbound;
751
752
	char fname[MAX_PATH+1];

rswindell's avatar
rswindell committed
753
754
	if(!cfg.flo_mailer)
		return true;
755

rswindell's avatar
rswindell committed
756
	outbound = get_current_outbound(dest, /* fileboxes: */false);
757
758
	if(outbound == NULL)
		return false;
759
760
761
762
763
764

	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
765
766
767
768
769
	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;
770
		lprintf(LOG_NOTICE, "Node (%s) externally locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
771
772
773
774
775
776
777
		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);
778
779
780
	}
	strListPush(&locked_bso_nodes, fname);
	lprintf(LOG_DEBUG, "Node (%s) successfully locked via: %s", smb_faddrtoa(&dest, NULL), fname);
rswindell's avatar
rswindell committed
781
782
783
	return true;
}

784
const char* bso_flo_filename(fidoaddr_t dest, uint16_t attr)
rswindell's avatar
rswindell committed
785
786
787
788
789
790
{
	nodecfg_t* nodecfg;
	char ch='f';
	const char* outbound;
	static char filename[MAX_PATH+1];

791
792
793
794
795
	if(attr&FIDO_CRASH)
		ch='c';
	else if(attr&FIDO_HOLD)
		ch='h';
	else if((nodecfg=findnodecfg(&cfg, dest, /* exact: */false)) != NULL) {
rswindell's avatar
rswindell committed
796
797
798
799
800
801
802
803
804
805
		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);
806
807
	if(outbound == NULL)
		return NULL;
rswindell's avatar
rswindell committed
808
809
810
811
812
813
814
815

	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;
}

816
817
818
819
820
/******************************************************************************
 This function creates or appends on existing Binkley compatible .?LO file
 attach file.
 Returns 0 on success.
******************************************************************************/
821
int write_flofile(const char *infile, fidoaddr_t dest, bool bundle, bool use_outbox, uint16_t attr)
822
{
rswindell's avatar
rswindell committed
823
824
	const char* flo_filename;
	char attachment[MAX_PATH+1];
825
	char searchstr[MAX_PATH+1];
826
	char* p;
rswindell's avatar
rswindell committed
827
828
	FILE *fp;
	nodecfg_t* nodecfg;
829

rswindell's avatar
rswindell committed
830
831
832
833
	if(use_outbox && (nodecfg=findnodecfg(&cfg, dest, /* exact: */false)) != NULL) {
		if(nodecfg->outbox[0])
			return 0;
	}
834
835
836
837

	if(!bso_lock_node(dest))
		return 1;

838
	flo_filename = bso_flo_filename(dest, attr);
839
840
841
	if(flo_filename == NULL)
		return -2;

842
843
844
	if(*infile == '^')  /* work-around for BRE/FE inter-BBS attachment bug */
		infile++;

845
846
847
848
#ifdef __unix__
	if(isalpha(infile[0]) && infile[1] == ':')	// Ignore "C:" prefix
		infile += 2;
#endif
rswindell's avatar
rswindell committed
849
	SAFECOPY(attachment, infile);
850
	REPLACE_CHARS(attachment, '\\', '/', p);
rswindell's avatar
rswindell committed
851
852
853
	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;
854
	}
rswindell's avatar
rswindell committed
855
856
857
858
859
	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);
rswindell's avatar
rswindell committed
860
		return -1;
rswindell's avatar
rswindell committed
861
862
863
864
865
866
	}
	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;
867
868
}

869
/* Writes text buffer to file, expanding sole LFs to CRLFs or stripping LFs */
rswindell's avatar
rswindell committed
870
size_t fwrite_crlf(const char* buf, size_t len, FILE* fp)
871
872
873
874
875
876
877
{
	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++;
878
879
880
881
882
883
884
885
		if(ch=='\n') {
			if(last_ch!='\r') {
				if(fputc('\r', fp) == EOF)
					break;
				wr++;
			}
			if(cfg.strip_lf)
				continue;
886
887
		}
		if(fputc(ch,fp)==EOF)
rswindell's avatar
rswindell committed
888
			break;
889
890
891
892
893
894
895
		wr++;
		last_ch=ch;
	}

	return(wr);
}

rswindell's avatar
rswindell committed
896
bool fidoctrl_line_exists(const smbmsg_t* msg, const char* prefix)
897
{
898
	if(msg==NULL || prefix==NULL)
rswindell's avatar
rswindell committed
899
		return false;
900
901
902
	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
903
			return true;
904
	}
rswindell's avatar
rswindell committed
905
	return false;
906
907
}

rswindell's avatar
rswindell committed
908
fidoaddr_t fmsghdr_srcaddr(const fmsghdr_t* hdr)
909
910
911
912
913
914
915
916
917
918
919
{
	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
920
const char* fmsghdr_srcaddr_str(const fmsghdr_t* hdr)
921
922
923
924
925
926
927
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_srcaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

rswindell's avatar
rswindell committed
928
fidoaddr_t fmsghdr_destaddr(const fmsghdr_t* hdr)
929
930
931
932
933
934
935
936
937
938
939
{
	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
940
const char* fmsghdr_destaddr_str(const fmsghdr_t* hdr)
941
942
943
944
945
946
947
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_destaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

948
949
950
951
bool parse_origin(const char* fmsgbuf, fmsghdr_t* hdr)
{
	char* p;
	fidoaddr_t origaddr;
rswindell's avatar
rswindell committed
952

953
954
955
956
957
958
959
960
961
962
963
	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);
rswindell's avatar
rswindell committed
964
	if(origaddr.zone == 0 || faddr_contains_wildcard(&origaddr))
965
966
967
968
969
970
971
972
		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
973
974
975
976
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
977
	enum pkt_type type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
978
979
980
981
982
983
984
985
986
987
988
989
990
991

	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
992
993
994
995
	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
996
		dest.point = hdr->type2plus.destpoint;
rswindell's avatar
rswindell committed
997
998
999
1000
1001
1002
		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
1003
1004
1005
1006
		}
	} 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;
rswindell's avatar
rswindell committed
1007
		dest.point = hdr->type2_2.destpoint;
rswindell's avatar
rswindell committed
1008
1009
1010
1011
1012
1013
1014
1015
	}

	if(pkt_type != NULL)
		*pkt_type = type;
	if(dest_addr != NULL)
		*dest_addr = dest;
	if(orig_addr != NULL)
		*orig_addr = orig;
rswindell's avatar
rswindell committed
1016

rswindell's avatar
rswindell committed
1017
1018
1019
1020
1021
	return true;
}

bool new_pkthdr(fpkthdr_t* hdr, fidoaddr_t orig, fidoaddr_t dest, const nodecfg_t* nodecfg)
{
rswindell's avatar
rswindell committed
1022
	enum pkt_type pkt_type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
	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;
rswindell's avatar
rswindell committed
1046

rswindell's avatar
rswindell committed
1047
1048
1049
1050
1051
	hdr->type2.pkttype	= 2;
	hdr->type2.prodcode	= SBBSECHO_PRODUCT_CODE&0xff;
	hdr->type2.sernum	= SBBSECHO_VERSION_MAJOR;

	if(nodecfg != NULL && nodecfg->pktpwd[0] != 0)
1052
		strncpy((char*)hdr->type2.password, nodecfg->pktpwd, sizeof(hdr->type2.password));
rswindell's avatar
rswindell committed
1053

rswindell's avatar
rswindell committed
1054
	if(pkt_type == PKT_TYPE_2)
rswindell's avatar
rswindell committed
1055
1056
1057
1058
		return true;

	if(pkt_type == PKT_TYPE_2_2) {
		hdr->type2_2.subversion = 2;	/* 2.2 */
1059
1060
		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
1061
1062
		return true;
	}
rswindell's avatar
rswindell committed
1063

rswindell's avatar
rswindell committed
1064
1065
	/* 2e and 2+ */
	if(pkt_type != PKT_TYPE_2_EXT && pkt_type != PKT_TYPE_2_PLUS) {
rswindell's avatar
rswindell committed
1066
1067
1068
1069
		lprintf(LOG_ERR, "UNSUPPORTED PACKET TYPE: %u", pkt_type);
		return false;
	}

rswindell's avatar
rswindell committed
1070
1071
1072
1073
	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
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
	}
	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;
}

1087
/******************************************************************************
rswindell's avatar
rswindell committed
1088
 This function will create a netmail message (FTS-1 "stored message" format).
1089
1090
1091
 If file is non-zero, will set file attachment bit (for bundles).
 Returns 0 on success.
******************************************************************************/
rswindell's avatar
rswindell committed
1092
int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, const char *body, fidoaddr_t dest)
1093
{
1094
	FILE *fp;
1095
	char tmp[256];
1096
	char fname[MAX_PATH+1];
1097
	char* from=NULL;
1098
1099
	uint i;
	static uint startmsg;
rswindell's avatar
rswindell committed
1100
	fidoaddr_t	faddr;
1101
	fmsghdr_t hdr;
1102
	time_t t;
1103
	struct tm *tm;
1104
	when_t when_written;
rswindell's avatar
rswindell committed
1105
1106
	nodecfg_t* nodecfg;
	bool	direct=false;
1107

1108
	if(msg==NULL) {
rswindell's avatar
rswindell committed
1109
		when_written.time = time32(NULL);
1110
1111
1112
1113
1114
		when_written.zone = sys_timezone(&scfg);
	} else {
		from = msg->from;
		when_written = msg->hdr.when_written;
	}
rswindell's avatar
rswindell committed
1115
	if(from==NULL || *from==0) {
1116
		from="SBBSecho";
rswindell's avatar
rswindell committed
1117
1118
1119
1120
1121
1122
1123
		if(to != NULL && stricmp(to, "SBBSecho") == 0) {
			lprintf(LOG_NOTICE, "Refusing to create netmail-loop msg from %s to %s", from, to);
			return -42;	// Refuse to create mail-loop between 2 SBBSecho-bots
		}
	}

	if(to==NULL || *to==0)
1124
		to="Sysop";
1125
	if(!startmsg) startmsg=1;
rswindell's avatar
rswindell committed
1126
1127
1128
	if((nodecfg=findnodecfg(&cfg, dest, 0)) != NULL) {
		if(nodecfg->status == MAIL_STATUS_NORMAL && !nodecfg->direct)
			nodecfg=findnodecfg(&cfg, dest, /* skip exact match: */2);