Skip to content
Snippets Groups Projects
rechocfg.c 20.5 KiB
Newer Older
/* rechocfg.C */

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

/* $Id$ */

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

/* Portions written by Allen Christiansen 1994-1996 						*/

#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
rswindell's avatar
rswindell committed
#include <sys/stat.h>
rswindell's avatar
rswindell committed
#include "sbbs.h"
#include "sbbsecho.h"
#include "filewrap.h"	/* O_DENYNONE */

/****************************************************************************/
/* Returns the FidoNet address kept in str as ASCII.                        */
rswindell's avatar
rswindell committed
/* Supports wildcard of "ALL" in fields										*/
/****************************************************************************/
rswindell's avatar
rswindell committed
faddr_t atofaddr(const char *instr)
{
	char *p,str[51];
    faddr_t addr;

rswindell's avatar
rswindell committed
	SAFECOPY(str, instr);
	p=str;
	FIND_WHITESPACE(p);
	*p=0;
	if(!stricmp(str,"ALL")) {
		addr.zone=addr.net=addr.node=addr.point=0xffff;
rswindell's avatar
rswindell committed
		return(addr); 
	}
	addr.zone=addr.net=addr.node=addr.point=0;
	if((p=strchr(str,':'))!=NULL) {
		if(!strnicmp(str,"ALL:",4))
			addr.zone=0xffff;
		else
			addr.zone=atoi(str);
		p++;
		if(!strnicmp(p,"ALL",3))
			addr.net=0xffff;
		else
rswindell's avatar
rswindell committed
			addr.net=atoi(p);
	}
rswindell's avatar
rswindell committed
		addr.zone=1;
rswindell's avatar
rswindell committed
		addr.net=atoi(str);
	}
	if(!addr.zone)              /* no such thing as zone 0 */
		addr.zone=1;
	if((p=strchr(str,'/'))!=NULL) {
		p++;
		if(!strnicmp(p,"ALL",3))
			addr.node=0xffff;
		else
rswindell's avatar
rswindell committed
			addr.node=atoi(p);
	}
rswindell's avatar
rswindell committed
		if(!addr.net)
			addr.net=1;
rswindell's avatar
rswindell committed
		addr.node=atoi(str);
	}
	if((p=strchr(str,'.'))!=NULL) {
		p++;
		if(!strnicmp(p,"ALL",3))
			addr.point=0xffff;
		else
rswindell's avatar
rswindell committed
			addr.point=atoi(p);
	}
	return(addr);
rswindell's avatar
rswindell committed
/****************************************************************************/
/* Returns an ASCII string for FidoNet address 'addr'                       */
/* Supports wildcard of "ALL" in fields										*/
/****************************************************************************/
const char *faddrtoa(const faddr_t* addr)
{
    static char str[25];
	char tmp[25];

	str[0]=0;
	if(addr->zone==0xffff)
		strcpy(str,"ALL");
	else if(addr->zone) {
		sprintf(str,"%u:",addr->zone);
		if(addr->net==0xffff)
			strcat(str,"ALL");
		else {
			sprintf(tmp,"%u/",addr->net);
			strcat(str,tmp);
			if(addr->node==0xffff)
				strcat(str,"ALL");
			else {
				sprintf(tmp,"%u",addr->node);
				strcat(str,tmp);
				if(addr->point==0xffff)
					strcat(str,".ALL");
				else if(addr->point) {
					sprintf(tmp,".%u",addr->point);
					strcat(str,tmp); 
				} 
			} 
		} 
	}
	return(str);
}

/******************************************************************************
 This function returns the number of the node in the SBBSECHO.CFG file which
 matches the address passed to it (or cfg.nodecfgs if no match).
 ******************************************************************************/
rswindell's avatar
rswindell committed
int matchnode(sbbsecho_cfg_t* cfg, faddr_t addr, int exact)
	if(exact!=2) {
rswindell's avatar
rswindell committed
		for(i=0;i<cfg->nodecfgs;i++) 				/* Look for exact match */
			if(!memcmp(&cfg->nodecfg[i].addr,&addr,sizeof(faddr_t)))
rswindell's avatar
rswindell committed
		if(exact || i<cfg->nodecfgs)
rswindell's avatar
rswindell committed
			return(i);
	}
rswindell's avatar
rswindell committed
	for(i=0;i<cfg->nodecfgs;i++) 					/* Look for point match */
		if(cfg->nodecfg[i].addr.point==0xffff
			&& addr.zone==cfg->nodecfg[i].addr.zone
			&& addr.net==cfg->nodecfg[i].addr.net
			&& addr.node==cfg->nodecfg[i].addr.node)
rswindell's avatar
rswindell committed
	if(i<cfg->nodecfgs)
rswindell's avatar
rswindell committed
	for(i=0;i<cfg->nodecfgs;i++) 					/* Look for node match */
		if(cfg->nodecfg[i].addr.node==0xffff
			&& addr.zone==cfg->nodecfg[i].addr.zone
			&& addr.net==cfg->nodecfg[i].addr.net)
rswindell's avatar
rswindell committed
	if(i<cfg->nodecfgs)
rswindell's avatar
rswindell committed
	for(i=0;i<cfg->nodecfgs;i++) 					/* Look for net match */
		if(cfg->nodecfg[i].addr.net==0xffff
			&& addr.zone==cfg->nodecfg[i].addr.zone)
rswindell's avatar
rswindell committed
	if(i<cfg->nodecfgs)
		return(i);
rswindell's avatar
rswindell committed
	for(i=0;i<cfg->nodecfgs;i++) 					/* Look for total wild */
		if(cfg->nodecfg[i].addr.zone==0xffff)
			break;
	return(i);
rswindell's avatar
rswindell committed
nodecfg_t* findnodecfg(sbbsecho_cfg_t* cfg, faddr_t addr, int exact)
rswindell's avatar
rswindell committed
	uint node = matchnode(cfg, addr, exact);
rswindell's avatar
rswindell committed
	if(node < cfg->nodecfgs)
		return &cfg->nodecfg[node];
rswindell's avatar
rswindell committed
	return NULL;
}
rswindell's avatar
rswindell committed
void get_default_echocfg(sbbsecho_cfg_t* cfg)
{
	cfg->maxpktsize				= DFLT_PKT_SIZE;
	cfg->maxbdlsize				= DFLT_BDL_SIZE;
	cfg->badecho				= -1;
	cfg->log_level				= LOG_INFO;
	cfg->check_path				= true;
	cfg->zone_blind				= false;
	cfg->zone_blind_threshold	= 0xffff;
	cfg->sysop_alias_list		= strListSplitCopy(NULL, "SYSOP", ",");
	cfg->max_echomail_age		= 60*24*60*60;
	cfg->bsy_timeout			= 12*60*60;
	cfg->bso_lock_attempts		= 60;
	cfg->bso_lock_delay			= 10;
	cfg->delete_packets			= true;
	cfg->delete_netmail			= true;
	cfg->echomail_notify		= true;
	cfg->kill_empty_netmail		= true;
	cfg->use_ftn_domains		= false;
rswindell's avatar
rswindell committed
}
rswindell's avatar
rswindell committed
char* pktTypeStringList[] = {"2+", "2.2", "2", NULL};
char* mailStatusStringList[] = {"Normal", "Hold", "Crash", NULL};
rswindell's avatar
rswindell committed
bool sbbsecho_read_ini(sbbsecho_cfg_t* cfg)
{
	FILE*		fp;
	str_list_t	ini;
	char		value[INI_MAX_VALUE_LEN];

	get_default_echocfg(cfg);

	if((fp=iniOpenFile(cfg->cfgfile, /* create: */false))==NULL)
		return false;
	ini = iniReadFile(fp);
	iniCloseFile(fp);

	/************************/
	/* Global/root section: */
	/************************/
	iniFreeStringList(cfg->sysop_alias_list);
	SAFECOPY(cfg->inbound		, iniGetString(ini, ROOT_SECTION, "Inbound",		"../fido/nonsecure", value));
	SAFECOPY(cfg->secure_inbound, iniGetString(ini, ROOT_SECTION, "SecureInbound", "../fido/inbound", value));
	SAFECOPY(cfg->outbound		, iniGetString(ini, ROOT_SECTION, "Outbound",		"../fido/outbound", value));
	SAFECOPY(cfg->areafile		, iniGetString(ini, ROOT_SECTION, "AreaFile",		"../data/areas.bbs", value));
	SAFECOPY(cfg->logfile		, iniGetString(ini, ROOT_SECTION, "LogFile",		"../data/sbbsecho.log", value));
	SAFECOPY(cfg->temp_dir		, iniGetString(ini, ROOT_SECTION, "TempDirectory",	"../temp/sbbsecho", value));
	SAFECOPY(cfg->outgoing_sem	, iniGetString(ini, ROOT_SECTION, "OutgoingSemaphore",	"", value));
rswindell's avatar
rswindell committed
	cfg->log_level				= iniGetLogLevel(ini, ROOT_SECTION, "LogLevel", cfg->log_level);
	cfg->strip_lf				= iniGetBool(ini, ROOT_SECTION, "StripLineFeeds", cfg->strip_lf);
	cfg->flo_mailer				= iniGetBool(ini, ROOT_SECTION, "BinkleyStyleOutbound", cfg->flo_mailer);
	cfg->bsy_timeout			= (ulong)iniGetDuration(ini, ROOT_SECTION, "BsyTimeout", cfg->bsy_timeout);
	cfg->bso_lock_attempts		= iniGetLongInt(ini, ROOT_SECTION, "BsoLockAttempts", cfg->bso_lock_attempts);
	cfg->bso_lock_delay			= (ulong)iniGetDuration(ini, ROOT_SECTION, "BsoLockDelay", cfg->bso_lock_delay);
	cfg->use_ftn_domains		= iniGetBool(ini, ROOT_SECTION, "UseFTNDomains", cfg->use_ftn_domains);
rswindell's avatar
rswindell committed

	/* EchoMail options: */
	cfg->maxbdlsize				= (ulong)iniGetBytes(ini, ROOT_SECTION, "BundleSize", 1, cfg->maxbdlsize);
	cfg->maxpktsize				= (ulong)iniGetBytes(ini, ROOT_SECTION, "PacketSize", 1, cfg->maxpktsize);
	cfg->check_path				= iniGetBool(ini, ROOT_SECTION, "CheckPathsForDupes", cfg->check_path);
	cfg->secure_echomail		= iniGetBool(ini, ROOT_SECTION, "SecureEchomail", cfg->secure_echomail);
	cfg->echomail_notify		= iniGetBool(ini, ROOT_SECTION, "EchomailNotify", cfg->echomail_notify);
	cfg->convert_tear			= iniGetBool(ini, ROOT_SECTION, "ConvertTearLines", cfg->convert_tear);
	cfg->trunc_bundles			= iniGetBool(ini, ROOT_SECTION, "TruncateBundles", cfg->trunc_bundles);
	cfg->zone_blind				= iniGetBool(ini, ROOT_SECTION, "ZoneBlind", cfg->zone_blind);
	cfg->zone_blind_threshold	= iniGetShortInt(ini, ROOT_SECTION, "ZoneBlindThreshold", cfg->zone_blind_threshold);
	cfg->add_from_echolists_only= iniGetBool(ini, ROOT_SECTION, "AreaAddFromEcholistsOnly", cfg->add_from_echolists_only);
	cfg->max_echomail_age		= (ulong)iniGetDuration(ini, ROOT_SECTION, "MaxEchomailAge", cfg->max_echomail_age);
	SAFECOPY(cfg->areamgr,		  iniGetString(ini, ROOT_SECTION, "AreaManager", "SYSOP", value));

	/* NetMail options: */
	SAFECOPY(cfg->default_recipient, iniGetString(ini, ROOT_SECTION, "DefaultRecipient", "SYSOP", value));
	cfg->sysop_alias_list			= iniGetStringList(ini, ROOT_SECTION, "SysopAliasList", ",", "SYSOP");
	cfg->fuzzy_zone					= iniGetBool(ini, ROOT_SECTION, "FuzzyNetmailZones", cfg->fuzzy_zone);
	cfg->ignore_netmail_dest_addr	= iniGetBool(ini, ROOT_SECTION, "IgnoreNetmailDestAddr", cfg->ignore_netmail_dest_addr);
	cfg->ignore_netmail_recv_attr	= iniGetBool(ini, ROOT_SECTION, "IgnoreNetmailRecvAttr", cfg->ignore_netmail_recv_attr);
	cfg->ignore_netmail_local_attr	= iniGetBool(ini, ROOT_SECTION, "IgnoreNetmailLocalAttr", cfg->ignore_netmail_local_attr);
	cfg->kill_empty_netmail			= iniGetBool(ini, ROOT_SECTION, "KillEmptyNetmail", cfg->kill_empty_netmail);
	cfg->delete_netmail				= iniGetBool(ini, ROOT_SECTION, "DeleteNetmail", cfg->delete_netmail);
	cfg->delete_packets				= iniGetBool(ini, ROOT_SECTION, "DeletePackets", cfg->delete_packets);
	cfg->max_netmail_age			= (ulong)iniGetDuration(ini, ROOT_SECTION, "MaxNetmailAge", cfg->max_netmail_age);

	/******************/
	/* Archive Types: */
	/******************/
	str_list_t archivelist = iniGetSectionList(ini, "archive:");
	cfg->arcdefs = strListCount(archivelist);
	if((cfg->arcdef = realloc(cfg->arcdef, sizeof(arcdef_t)*cfg->arcdefs)) == NULL)
		return false;
	cfg->arcdefs = 0;
	char* archive;
	while((archive=strListPop(&archivelist)) != NULL) {
		arcdef_t* arc = &cfg->arcdef[cfg->arcdefs++];
		memset(arc, 0, sizeof(arcdef_t));
		SAFECOPY(arc->name, truncsp(archive+8));
		arc->byteloc = iniGetInteger(ini, archive, "SigOffset", 0);
		SAFECOPY(arc->hexid, truncsp(iniGetString(ini, archive, "Sig", "", value)));
		SAFECOPY(arc->pack, truncsp(iniGetString(ini, archive, "Pack", "", value)));
		SAFECOPY(arc->unpack, truncsp(iniGetString(ini, archive, "Unpack", "", value)));
	}
	strListFree(&archivelist);

	/****************/
	/* Links/Nodes: */
	/****************/
	str_list_t nodelist = iniGetSectionList(ini, "node:");
	cfg->nodecfgs = strListCount(nodelist);
	if((cfg->nodecfg = realloc(cfg->nodecfg, sizeof(nodecfg_t)*cfg->nodecfgs)) == NULL)
		return false;
	cfg->nodecfgs = 0;
	char* node;
	while((node=strListPop(&nodelist)) != NULL) {
		nodecfg_t* ncfg = &cfg->nodecfg[cfg->nodecfgs++];
		memset(ncfg, 0, sizeof(nodecfg_t));
		ncfg->addr = atofaddr(node+5);
		if(iniGetString(ini, node, "route", NULL, value) != NULL)
			ncfg->route = atofaddr(value);
		SAFECOPY(ncfg->password	, iniGetString(ini, node, "AreafixPwd", "", value));
		SAFECOPY(ncfg->pktpwd	, iniGetString(ini, node, "PacketPwd", "", value));
		SAFECOPY(ncfg->comment	, iniGetString(ini, node, "Comment", "", value));
		SAFECOPY(ncfg->inbox	, iniGetString(ini, node, "inbox", "", value));
		SAFECOPY(ncfg->outbox	, iniGetString(ini, node, "outbox", "", value));
		ncfg->keys				= iniGetStringList(ini, node, "keys", ",", "");
		ncfg->pkt_type			= iniGetEnum(ini, node, "PacketType", pktTypeStringList, ncfg->pkt_type);
		ncfg->send_notify		= iniGetBool(ini, node, "notify", ncfg->send_notify);
		ncfg->passive			= iniGetBool(ini, node, "passive", ncfg->passive);
		ncfg->direct			= iniGetBool(ini, node, "direct", ncfg->direct);
		ncfg->status			= iniGetEnum(ini, node, "status", mailStatusStringList, ncfg->status);
		char* archive = iniGetString(ini, node, "archive", "", value);
		for(uint i=0; i<cfg->arcdefs; i++) {
			if(stricmp(cfg->arcdef[i].name, archive) == 0) {
				ncfg->archive = &cfg->arcdef[i];
				break;
rswindell's avatar
rswindell committed
	}
	strListFree(&nodelist);

	/**************/
	/* EchoLists: */
	/**************/
	str_list_t echolists = iniGetSectionList(ini, "echolist:");
	cfg->listcfgs = strListCount(echolists);
	if((cfg->listcfg = realloc(cfg->listcfg, sizeof(echolist_t)*cfg->listcfgs)) == NULL)
		return false;
	cfg->listcfgs = 0;
	char* echolist;
	while((echolist=strListPop(&echolists)) != NULL) {
		echolist_t* elist = &cfg->listcfg[cfg->listcfgs++];
		memset(elist, 0, sizeof(echolist_t));
		SAFECOPY(elist->listpath, echolist + 9);
		elist->keys = iniGetStringList(ini, echolist, "keys", ",", "");
		elist->hub = atofaddr(iniGetString(ini,echolist,"hub","",value));
		elist->forward = iniGetBool(ini, echolist, "fwd", false);
		SAFECOPY(elist->password, iniGetString(ini,echolist, "pwd", "", value));
	}
	strListFree(&echolists);
rswindell's avatar
rswindell committed
	/* make sure we have some sane "maximum" size values here: */
	if(cfg->maxpktsize<1024)
		cfg->maxpktsize=DFLT_PKT_SIZE;
	if(cfg->maxbdlsize<1024)
		cfg->maxbdlsize=DFLT_BDL_SIZE;
rswindell's avatar
rswindell committed
	strListFree(&ini);
	return true;
}

bool sbbsecho_read_ftn_domains(sbbsecho_cfg_t* cfg, const char * ctrl_dir)
{
	FILE*		fp;
	str_list_t	ini;
	char		path[MAX_PATH+1];
	str_list_t	domains;
	const char *	domain;
	str_list_t	zones;
	const char *	zone;
	struct zone_mapping * mapping;
	struct zone_mapping * old_mapping;

	// First, free any old mappings...
	for (mapping = cfg->zone_map; mapping;) {
		FREE_AND_NULL(mapping->domain);
		FREE_AND_NULL(mapping->root);
		old_mapping = mapping;
		mapping = old_mapping->next;
		FREE_AND_NULL(old_mapping);
	}
	cfg->zone_map = NULL;
	if(cfg->use_ftn_domains) {
		SAFEPRINTF(path, "%sftn_domains.ini", ctrl_dir);
		if((fp=iniOpenFile(path, /* create: */false))==NULL)
			return false;
		ini = iniReadFile(fp);
		iniCloseFile(fp);
		domains = iniGetSectionList(ini, NULL);
		while((domain = strListPop(&domains)) != NULL) {
			zones = iniGetStringList(ini, domain, "Zones", ",", NULL);
			while((zone = strListPop(&zones)) != NULL) {
				mapping = (struct zone_mapping *)malloc(sizeof(struct zone_mapping));

				if (mapping == NULL) {
					strListFree(&zones);
					strListFree(&domains);
					return false;
				}
				mapping->zone = (uint16_t)strtol(zone, NULL, 10);
				mapping->domain = strdup(domain);
				mapping->root = strdup(iniGetString(ini, domain, "OutboundRoot", cfg->outbound, path));
				mapping->next = cfg->zone_map;
				cfg->zone_map = mapping;
			}
rswindell's avatar
rswindell committed
	return true;
}
rswindell's avatar
rswindell committed
bool sbbsecho_write_ini(sbbsecho_cfg_t* cfg)
{
	char section[128];
	FILE* fp;
	str_list_t	ini;

	if((fp=iniOpenFile(cfg->cfgfile, /* create: */true))==NULL)
		return false;
	ini = iniReadFile(fp);

	ini_style_t style = {  .value_separator = " = " };
	iniSetDefaultStyle(style);

	/************************/
	/* Global/root section: */
	/************************/
	iniSetString(&ini,		ROOT_SECTION, "Inbound"					,cfg->inbound					,NULL);
	iniSetString(&ini,		ROOT_SECTION, "SecureInbound"			,cfg->secure_inbound			,NULL);
	iniSetString(&ini,		ROOT_SECTION, "Outbound"				,cfg->outbound					,NULL);
	iniSetString(&ini,		ROOT_SECTION, "AreaFile"				,cfg->areafile					,NULL);
	if(cfg->logfile[0])
	iniSetString(&ini,		ROOT_SECTION, "LogFile"					,cfg->logfile					,NULL);
	if(cfg->temp_dir[0])
	iniSetString(&ini,		ROOT_SECTION, "TempDirectory"			,cfg->temp_dir					,NULL);
	iniSetBytes(&ini,		ROOT_SECTION, "BundleSize"				,1,cfg->maxbdlsize				,NULL);
	iniSetBytes(&ini,		ROOT_SECTION, "PacketSize"				,1,cfg->maxpktsize				,NULL);
	iniSetStringList(&ini,	ROOT_SECTION, "SysopAliasList", ","		,cfg->sysop_alias_list			,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "ZoneBlind"				,cfg->zone_blind				,NULL);
	iniSetShortInt(&ini,	ROOT_SECTION, "ZoneBlindThreshold"		,cfg->zone_blind_threshold		,NULL);
	iniSetLogLevel(&ini,	ROOT_SECTION, "LogLevel"				,cfg->log_level					,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "CheckPathsForDupes"		,cfg->check_path				,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "SecureEchomail"			,cfg->secure_echomail			,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "EchomailNotify"			,cfg->echomail_notify			,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "StripLineFeeds"			,cfg->strip_lf					,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "ConvertTearLines"		,cfg->convert_tear				,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "FuzzyNetmailZones"		,cfg->fuzzy_zone				,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "BinkleyStyleOutbound"	,cfg->flo_mailer				,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "TruncateBundles"			,cfg->trunc_bundles				,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "AreaAddFromEcholistsOnly",cfg->add_from_echolists_only	,NULL);
	iniSetDuration(&ini,	ROOT_SECTION, "BsyTimeout"				,cfg->bsy_timeout				,NULL);
	iniSetDuration(&ini,	ROOT_SECTION, "BsoLockDelay"			,cfg->bso_lock_delay			,NULL);
	iniSetLongInt(&ini,		ROOT_SECTION, "BsoLockAttempts"			,cfg->bso_lock_attempts			,NULL);
	iniSetDuration(&ini,	ROOT_SECTION, "MaxEchomailAge"			,cfg->max_echomail_age			,NULL);
	iniSetDuration(&ini,	ROOT_SECTION, "MaxNetmailAge"			,cfg->max_netmail_age			,NULL);

	iniSetBool(&ini,		ROOT_SECTION, "IgnoreNetmailDestAddr"	,cfg->ignore_netmail_dest_addr	,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "IgnoreNetmailRecvAttr"	,cfg->ignore_netmail_recv_attr	,NULL);
	iniSetBool(&ini,		ROOT_SECTION, "IgnoreNetmailLocalAttr"	,cfg->ignore_netmail_local_attr	,NULL);
	iniSetString(&ini,		ROOT_SECTION, "DefaultRecipient"		,cfg->default_recipient			,NULL);

	iniSetBool(&ini,		ROOT_SECTION, "UseFTNDomains"			,cfg->use_ftn_domains			,NULL);
rswindell's avatar
rswindell committed

	style.key_prefix = "\t";

	/******************/
	/* Archive Types: */
	/******************/
	iniRemoveSections(&ini, "archive:");
	for(uint i=0; i<cfg->arcdefs; i++) {
		arcdef_t* arc = &cfg->arcdef[i];
		SAFEPRINTF(section,"archive:%s", arc->name);
		iniSetString(&ini,	section,	"Sig"			,arc->hexid		,&style);
		iniSetInteger(&ini,	section,	"SigOffset"		,arc->byteloc	,&style);
		iniSetString(&ini,	section,	"Pack"			,arc->pack		,&style);
		iniSetString(&ini,	section,	"Unpack"		,arc->unpack	,&style);
	}
rswindell's avatar
rswindell committed
	/****************/
	/* Links/Nodes: */
	/****************/
	iniRemoveSections(&ini, "node:");
	for(uint i=0; i<cfg->nodecfgs; i++) {
		nodecfg_t*	node = &cfg->nodecfg[i];
		SAFEPRINTF(section,"node:%s", faddrtoa(&node->addr));
		iniSetString(&ini	,section,	"Comment"		,node->comment		,&style);
		iniSetString(&ini	,section,	"AreafixPwd"	,node->password		,&style);
		iniSetString(&ini	,section,	"PacketPwd"		,node->pktpwd		,&style);
		iniSetEnum(&ini		,section,	"PacketType"	,pktTypeStringList, node->pkt_type, &style);
		iniSetString(&ini	,section,	"Archive"		,node->archive == SBBSECHO_ARCHIVE_NONE ? "None" : node->archive->name, &style);
		iniSetString(&ini	,section,	"Inbox"			,node->inbox		,&style);
		iniSetString(&ini	,section,	"Outbox"		,node->outbox		,&style);
		iniSetBool(&ini		,section,	"Passive"		,node->passive		,&style);
		iniSetBool(&ini		,section,	"Direct"		,node->direct		,&style);
		iniSetBool(&ini		,section,	"Notify"		,node->send_notify	,&style);
		iniSetStringList(&ini,section,	"Keys", ","		,node->keys			,&style);
		iniSetEnum(&ini		,section,	"Status"		,mailStatusStringList, node->status, &style);
		if(node->route.zone)
			iniSetString(&ini,section,	"Route"			,faddrtoa(&node->route), &style);
		else
			iniRemoveKey(&ini,section,	"Route");
rswindell's avatar
rswindell committed
	}
rswindell's avatar
rswindell committed
	/**************/
	/* EchoLists: */
	/**************/
	iniRemoveSections(&ini, "echolist:");
	for(uint i=0; i<cfg->listcfgs; i++) {
		echolist_t* elist = &cfg->listcfg[i];
		SAFEPRINTF(section,"echolist:%s", elist->listpath);
		iniSetString(&ini	,section,	"Hub"		,faddrtoa(&elist->hub)				,&style);
		iniSetString(&ini	,section,	"Pwd"		,elist->password					,&style);
		iniSetBool(&ini		,section,	"Fwd"		,elist->forward						,&style);
		iniSetStringList(&ini,section,	"Keys", ","	,elist->keys						,&style);
rswindell's avatar
rswindell committed
	iniWriteFile(fp, ini);
	iniCloseFile(fp);
rswindell's avatar
rswindell committed
	iniFreeStringList(ini);
	return true;
}