Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

sbbsecho.c 193 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 822
int write_flofile(const char *infile, fidoaddr_t dest, bool bundle, bool use_outbox
									, bool del_file, uint16_t attr)
823
{
rswindell's avatar
rswindell committed
824 825
	const char* flo_filename;
	char attachment[MAX_PATH+1];
826
	char searchstr[MAX_PATH+1];
827
	char* p;
rswindell's avatar
rswindell committed
828 829
	FILE *fp;
	nodecfg_t* nodecfg;
830

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

	if(!bso_lock_node(dest))
		return 1;

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

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

846 847 848 849
#ifdef __unix__
	if(isalpha(infile[0]) && infile[1] == ':')	// Ignore "C:" prefix
		infile += 2;
#endif
rswindell's avatar
rswindell committed
850
	SAFECOPY(attachment, infile);
851
	REPLACE_CHARS(attachment, '\\', '/', p);
rswindell's avatar
rswindell committed
852 853 854
	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;
855
	}
rswindell's avatar
rswindell committed
856 857 858 859
	char* prefix = "";
	if(bundle) {
		prefix = (cfg.trunc_bundles) ? "#" : "^";
	} else {
860
		if(del_file)
rswindell's avatar
rswindell committed
861 862 863
			prefix = "^";
	}
	SAFEPRINTF2(searchstr, "%s%s", prefix, attachment);
rswindell's avatar
rswindell committed
864 865 866 867
	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
868
		return -1;
rswindell's avatar
rswindell committed
869 870 871 872 873 874
	}
	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;
875 876
}

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

	return(wr);
}

rswindell's avatar
rswindell committed
904
bool fidoctrl_line_exists(const smbmsg_t* msg, const char* prefix)
905
{
906
	if(msg==NULL || prefix==NULL)
rswindell's avatar
rswindell committed
907
		return false;
908 909 910
	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
911
			return true;
912
	}
rswindell's avatar
rswindell committed
913
	return false;
914 915
}

rswindell's avatar
rswindell committed
916
fidoaddr_t fmsghdr_srcaddr(const fmsghdr_t* hdr)
917 918 919 920 921 922 923 924 925 926 927
{
	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
928
const char* fmsghdr_srcaddr_str(const fmsghdr_t* hdr)
929 930 931 932 933 934 935
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_srcaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

rswindell's avatar
rswindell committed
936
fidoaddr_t fmsghdr_destaddr(const fmsghdr_t* hdr)
937 938 939 940 941 942 943 944 945 946 947
{
	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
948
const char* fmsghdr_destaddr_str(const fmsghdr_t* hdr)
949 950 951 952 953 954 955
{
	static char buf[64];
	fidoaddr_t addr = fmsghdr_destaddr(hdr);

	return smb_faddrtoa(&addr, buf);
}

956 957 958 959
bool parse_origin(const char* fmsgbuf, fmsghdr_t* hdr)
{
	char* p;
	fidoaddr_t origaddr;
rswindell's avatar
rswindell committed
960

961 962 963 964 965 966 967 968 969 970 971
	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
972
	if(origaddr.zone == 0 || faddr_contains_wildcard(&origaddr))
973 974 975 976 977 978 979 980
		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
981 982 983 984
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
985
	enum pkt_type type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
986 987 988 989 990 991 992 993 994 995 996 997 998 999

	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
1000 1001 1002 1003
	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
1004
		dest.point = hdr->type2plus.destpoint;
rswindell's avatar
rswindell committed
1005 1006 1007 1008 1009 1010
		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
1011 1012 1013 1014
		}
	} 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
1015
		dest.point = hdr->type2_2.destpoint;
rswindell's avatar
rswindell committed
1016 1017 1018 1019 1020 1021 1022 1023
	}

	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
1024

rswindell's avatar
rswindell committed
1025 1026 1027 1028 1029
	return true;
}

bool new_pkthdr(fpkthdr_t* hdr, fidoaddr_t orig, fidoaddr_t dest, const nodecfg_t* nodecfg)
{
rswindell's avatar
rswindell committed
1030
	enum pkt_type pkt_type = PKT_TYPE_2;
rswindell's avatar
rswindell committed
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
	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
1054

rswindell's avatar
rswindell committed
1055 1056 1057 1058 1059
	hdr->type2.pkttype	= 2;
	hdr->type2.prodcode	= SBBSECHO_PRODUCT_CODE&0xff;
	hdr->type2.sernum	= SBBSECHO_VERSION_MAJOR;

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

rswindell's avatar
rswindell committed
1062
	if(pkt_type == PKT_TYPE_2)
rswindell's avatar
rswindell committed
1063 1064 1065 1066
		return true;

	if(pkt_type == PKT_TYPE_2_2) {
		hdr->type2_2.subversion = 2;	/* 2.2 */
1067 1068
		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
1069 1070
		return true;
	}
rswindell's avatar
rswindell committed
1071

rswindell's avatar
rswindell committed
1072 1073
	/* 2e and 2+ */
	if(pkt_type != PKT_TYPE_2_EXT && pkt_type != PKT_TYPE_2_PLUS) {
rswindell's avatar
rswindell committed
1074 1075 1076 1077
		lprintf(LOG_ERR, "UNSUPPORTED PACKET TYPE: %u", pkt_type);
		return false;
	}

rswindell's avatar
rswindell committed
1078 1079 1080 1081
	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
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
	}
	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;
}

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

1116
	if(msg==NULL) {
rswindell's avatar
rswindell committed
1117
		when_written.time = time32(NULL);
1118 1119 1120 1121 1122
		when_written.zone = sys_timezone(&scfg);
	} else {
		from = msg->from;
		when_written = msg->hdr.when_written;
	}
rswindell's avatar
rswindell committed
1123
	if(from==NULL || *from==0) {
1124
		from="SBBSecho";
rswindell's avatar
rswindell committed
1125 1126 1127 1128 1129 1130 1131
		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)
1132
		to="Sysop";
1133
	if(!startmsg) startmsg=1;
rswindell's avatar
rswindell committed
1134 1135 1136
	if((nodecfg=findnodecfg(&cfg, dest, 0)) != NULL) {
		if(nodecfg->status == MAIL_STATUS_NORMAL && !nodecfg->direct)
			nodecfg=findnodecfg(&cfg, dest, /* skip exact match: */2);
1137
	}
1138

1139