/*:ts=8*/
/*****************************************************************************
 * FIDOGATE --- Gateway UNIX Mail/News <-> FIDO NetMail/EchoMail
 *
 * $Id: config.c,v 3.9.2.0 1995/06/12 17:14:03 mj Exp $
 *
 * Configuration data and functions
 *
 *****************************************************************************
 * Copyright (C) 1990-1995
 *  _____ _____
 * |     |___  |   Martin Junius             FIDO:      2:2452/110.1
 * | | | |   | |   Republikplatz 3           Internet:  mj@sungate.fido.de
 * |_|_|_|@home|   D-52072 Aachen, Germany   Phone:     ++49-241-86931 (voice)
 *
 * This file is part of FIDOGATE.
 *
 * FIDOGATE 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, or (at your option) any
 * later version.
 *
 * FIDOGATE is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with FIDOGATE; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "fidogate.h"



/*
 * Current line in config file
 */
static int scf_line;



/*
 * Directories
 */
static char scf_libdir[MAXPATH];
static char scf_spooldir[MAXPATH];
static char scf_logdir[MAXPATH];
static char scf_outbound[MAXPATH];
static char scf_inbound[MAXPATH];

/*
 * Hostname / domainname
 */
static char scf_hostname[MAXPATH];
static char scf_domainname[MAXPATH];
static char scf_hostsdomain[MAXPATH];
static char scf_fqdn[MAXPATH];

/*
 * EchoMail origin line
 */
static char scf_origin[MAXPATH];

/*
 * News organization
 */
static char scf_organization[MAXPATH];



/*
 * Configured zones and addresses
 */
struct st_addr
{
    int zone;				/* Zone for this address set */
    Node addr;				/* Our own main address */
    Node uplink;			/* Uplink address */
};

static struct st_addr scf_addr[MAXADDRESS];
static int scf_naddr = 0;		/* # of addresses */
static int scf_zone  = 0;		/* Current zone */
static int scf_index = 0;		/* Index to current zone scf_addr[] */

static Node scf_c_addr   = { -1, -1, -1, -1, "" };	/* Current address */
static Node scf_c_uplink = { -1, -1, -1, -1, "" };	/* Current uplink  */


/*
 * Zones, domains, and outbound directories
 */
struct st_zones
{
    int zone;				/* Zone */
    char *inet_domain;			/* Internet domain */
    char *ftn_domain;			/* FTN domain */
    char *out;				/* Outbound subdir */
};

static struct st_zones scf_zones[MAXADDRESS];
static int scf_nzones = 0;		/* # of zones */
static int scf_izones = 0;		/* Index for cf_zones_trav() */


/*
 * Configured DOS drive -> UNIX path translation
 */
struct st_dos
{
    char *drive;			/* MSDOS drive */
    char *path;				/* UNIX path */
};

static struct st_dos scf_dos[MAXDOSDRIVE];
static int scf_ndos = 0;		/* # of DOS drives */


/*
 * FTN-Internet gateway
 */
static Node scf_gateway;


/*
 * All other, unknown config lines stored in linked list
 */
struct st_cflist
{
    char *key;
    char *string;
    struct st_cflist *next;
};

static struct st_cflist *scf_list_first = NULL;
static struct st_cflist *scf_list_last  = NULL;

    

/*
 * Debug output of configuration
 */
void cf_debug()
{
    int i;

    debug(8, "config: libdir=%s  spooldir=%s  logdir=%s",
	  scf_libdir, scf_spooldir, scf_logdir);
    debug(8, "config: inbound=%s outbound=%s", scf_inbound, scf_outbound);
    debug(8, "config: fqdn=%s", scf_fqdn);
    debug(8, "config: origin=%s", scf_origin);
    debug(8, "config: organization=%s", scf_organization);
    
    for(i=0; i<scf_naddr; i++)
	debug(8, "config: address Z%-4d: %s  Uplink: %s",
	      scf_addr[i].zone,
	      node_to_asc(&scf_addr[i].addr, TRUE),
	      node_to_asc(&scf_addr[i].uplink, TRUE)          );

    for(i=0; i<scf_nzones; i++)
	debug(8, "config: zone %-4d: %s  %s  %s", scf_zones[i].zone,
	      scf_zones[i].inet_domain, scf_zones[i].ftn_domain,
	      scf_zones[i].out                                 );

    debug(8, "config: gateway=%s", node_to_asc(&scf_gateway, TRUE));
}



/*
 * Return main AKA (independent of current zone setting)
 */
Node *cf_main_addr()
{
    return &scf_addr[0].addr;
}

Node cf_n_main_addr()
{
    return scf_addr[0].addr;
}



/*
 * Return current main/fakenet/uplink FTN address
 */
Node *cf_addr()
{
    return &scf_c_addr;
}

Node *cf_uplink()
{
    return &scf_c_uplink;
}

Node cf_n_addr()
{
    return scf_c_addr;
}

Node cf_n_uplink()
{
    return scf_c_uplink;
}



/*
 * Set current zone
 */
void cf_set_zone(zone)
    int zone;
{
    int i;
    
    if(scf_naddr == 0) 
    {
	fprintf(stderr, "No FTN addresses configured.\n");
	exit(1);
    }

    scf_zone = zone;
    for(i=0; i<scf_naddr; i++)
	if(zone == scf_addr[i].zone)
	{
	    scf_index    = i;
	    scf_c_addr   = scf_addr[i].addr;
	    scf_c_uplink = scf_addr[i].uplink;
	    debug(9, "Select Z%d AKA: %s  Uplink: %s",
		  scf_addr[i].zone,
		  node_to_asc(&scf_addr[i].addr, TRUE),
		  node_to_asc(&scf_addr[i].uplink, TRUE)          );
	    return;
	}
    
    scf_index    = i = 0;
    scf_c_addr   = scf_addr[i].addr;
    scf_c_uplink = scf_addr[i].uplink;
    debug(9, "Select default AKA: %s  Uplink: %s",
	  node_to_asc(&scf_addr[i].addr, TRUE),
	  node_to_asc(&scf_addr[i].uplink, TRUE)          );
}


/*
 * Set current address
 */
void cf_set_curr(node)
    Node *node;
{
    cf_set_zone(node->zone);
    scf_c_addr = *node;
}



/*
 * Return current/default zone
 */
int cf_zone()
{
    return scf_zone;
}

int cf_defzone()
{
    return scf_addr[0].zone;
}



/*
 * Read line from config file. Strip `\n', leading spaces,
 * comments (starting with `#'), and empty lines. cf_getline() returns
 * a pointer to the first non-whitespace in buffer.
 */
char *cf_getline(buffer, len, fp)
    char *buffer;
    int len;
    FILE *fp;
{
    char *p;

    scf_line = 0;
    while (fgets(buffer, len, fp)) {
	scf_line++;
	strip_crlf(buffer);
	if((p = strchr(buffer, '#')))		/* Strip comments */
	    *p = 0;
	for(p=buffer; *p && is_space(*p); p++) ;	/* Skip white spaces */
	if (*p)
	    return p;
    }
    return NULL;
}



/*
 * Read config file
 */
void cf_read_config_file(name)
    char *name;
{
    FILE *cf;
    char *line, *p, *keyword;
    int ia=0, ir=0;

    if(name && !*name)			/* Empty string -> no config file */
	return;

    if(name)
    {
	if(!strncmp(name, "%L/", 3))	/* File in LIBDIR */
	{
	    name += 3;
	    cf = libfopen(name, R_MODE);
	}
	else				/* Full path name */
	    cf = xfopen(name, R_MODE);
    }
    else				/* Default */
	cf = libfopen(CONFIG, R_MODE);
    
    while((line = cf_getline(buffer, BUFFERSIZE, cf)))
    {
	keyword = xstrtok(line, " \t");
	if(!keyword)
	    continue;

	/***** hostname *****************************************************/
	if	(!stricmp(keyword, "hostname"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing hostname");
		continue;
	    }
	    strncpy0(scf_hostname, p, MAXPATH);
	}
	/***** domain *******************************************************/
	else if (!stricmp(keyword, "domain"  ))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing domainname");
		continue;
	    }
	    if(p[0] != '.')
	    {
		strncpy0(scf_domainname, ".", sizeof(scf_domainname));
		strncat0(scf_domainname, p  , sizeof(scf_domainname));
	    }
	    else
		strncpy0(scf_domainname, p  , sizeof(scf_domainname));
	    /* This is also the default for "HostsDomain" */
	    strncpy0(scf_hostsdomain, scf_domainname, sizeof(scf_hostsdomain));
	}
	/***** hostsdomain **************************************************/
	else if (!stricmp(keyword, "hostsdomain"  ))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing domainname");
		continue;
	    }
	    if(p[0] != '.')
	    {
		strncpy0(scf_hostsdomain, ".", sizeof(scf_hostsdomain));
		strncat0(scf_hostsdomain, p  , sizeof(scf_hostsdomain));
	    }
	    else
		strncpy0(scf_hostsdomain, p  , sizeof(scf_hostsdomain));
	}
	/***** origin *******************************************************/
	else if (!stricmp(keyword, "origin"  ))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing origin");
		continue;
	    }
	    strncpy0(scf_origin, p, MAXPATH);
	}
	/***** organization *************************************************/
	else if (!stricmp(keyword, "organization"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing organization");
		continue;
	    }
	    strncpy0(scf_organization, p, MAXPATH);
	}
	/***** libdir *******************************************************/
	else if (!stricmp(keyword, "libdir"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing libdir");
		continue;
	    }
	    strncpy0(scf_libdir, p, MAXPATH);
	}
	/***** spooldir *****************************************************/
	else if (!stricmp(keyword, "spooldir"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing spooldir");
		continue;
	    }
	    strncpy0(scf_spooldir, p, MAXPATH);
	}
	/***** logdir *******************************************************/
	else if (!stricmp(keyword, "logdir"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing logdir");
		continue;
	    }
	    strncpy0(scf_logdir, p, MAXPATH);
	}
	/***** outbound *****************************************************/
	else if (!stricmp(keyword, "outbound"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing outbound");
		continue;
	    }
	    strncpy0(scf_outbound, p, MAXPATH);
	}
	/***** inbound ******************************************************/
	else if (!stricmp(keyword, "inbound"))
	{
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing inbound");
		continue;
	    }
	    strncpy0(scf_inbound, p, MAXPATH);
	}
	/***** address ******************************************************/
	else if (!stricmp(keyword, "address" ))
	{
	    Node a;
	    
	    /* address */
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing address");
		continue;
	    }
	    if( asc_to_node(p, &a, FALSE) == ERROR )
	    {
		log("config: illegal address %s", p);
		continue;
	    }

	    if(ia < MAXADDRESS)
	    {
		scf_addr[ia].zone   = a.zone;
		scf_addr[ia].addr   = a;
		ia++;
	    }
	    else
		log("config: too many addresses");
	}
	/***** uplink *******************************************************/
	else if (!stricmp(keyword, "uplink" ))
	{
	    Node a;
	    
	    /* Main address */
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing address");
		continue;
	    }
	    if( asc_to_node(p, &a, FALSE) == ERROR )
	    {
		log("config: illegal address %s", p);
		continue;
	    }

	    if(ir < MAXADDRESS)
	    {
		scf_addr[ir].uplink = a;
		ir++;
	    }
	    else
		log("config: too many addresses");
	}
	/***** zone *********************************************************/
	else if (!stricmp(keyword, "zone" ))
	{
	    int zone;
	    char *inet, *ftn, *out;

	    if(scf_nzones >= MAXADDRESS)
	    {
		log("config: too many zones");
		continue;
	    }
	    
	    if(! (p = xstrtok(NULL, " \t")) )
	    {
		log("config: missing zone");
		continue;
	    }
	    if(!stricmp(p, "default"))
		zone = 0;
	    else 
	    {
		zone = atoi(p);
		if(!zone)
		{
		    log("config: illegal zone value %s", p);
		    continue;
		}
	    }
	    
	    if(! (inet = xstrtok(NULL, " \t")) )
	    {
		log("config: missing Internet domain");
		continue;
	    }

	    if(! (ftn = xstrtok(NULL, " \t")) )
	    {
		log("config: missing FTN domain");
		continue;
	    }

	    if(! (out = xstrtok(NULL, " \t")) )
	    {
		log("config: missing outbound directory");
		continue;
	    }

	    scf_zones[scf_nzones].zone        = zone;
	    scf_zones[scf_nzones].inet_domain = strsave(inet);
	    scf_zones[scf_nzones].ftn_domain  = strsave(ftn);
	    scf_zones[scf_nzones].out         = strsave(out);
	    scf_nzones++;
	}
	/***** dosdrive *****************************************************/
	else if (!stricmp(keyword, "dosdrive" ))
	{
	    char *drive, *path;
	    
	    if(scf_ndos >= MAXDOSDRIVE)
	    {
		log("config: too many DOS drives");
		continue;
	    }
	    if(! (drive = xstrtok(NULL, " \t")) )
	    {
		log("config: missing DOS drive");
		continue;
	    }
	    if(! (path = xstrtok(NULL, " \t")) )
	    {
		log("config: missing UNIX path");
		continue;
	    }

	    scf_dos[scf_ndos].drive = strsave(drive);
	    scf_dos[scf_ndos].path  = strsave(path);
	    scf_ndos++;
	}
	/***** gateway ******************************************************/
	else if (!stricmp(keyword, "gateway" ))
	{
	    Node a;
	    
	    /* Main address */
	    p = xstrtok(NULL, " \t");
	    if(!p) 
	    {
		log("config: missing address");
		continue;
	    }
	    if( asc_to_node(p, &a, FALSE) == ERROR )
	    {
		log("config: illegal address %s", p);
		continue;
	    }
	    scf_gateway = a;
	}
	/***** U n k n o w n ************************************************/
	else 
	{
	    struct st_cflist *pl;
	    
	    p = xstrtok(NULL, "\n");

	    pl = (struct st_cflist *)xmalloc(sizeof(struct st_cflist));

	    pl->key    = strsave(keyword);
	    pl->string = p ? strsave(p) : "";
	    pl->next   = NULL;
	    
	    if(scf_list_first)
		scf_list_last->next = pl;
	    else
		scf_list_first = pl;
	    scf_list_last = pl;
	}
    }

    scf_naddr 	 = ia;
    scf_zone  	 = scf_addr[0].zone;
    scf_index 	 = 0;
    scf_c_addr   = scf_addr[0].addr;
    scf_c_uplink = scf_addr[0].uplink;
    
    strncpy0(scf_fqdn, scf_hostname,   MAXPATH);
    strncat0(scf_fqdn, scf_domainname, MAXPATH);
    
    fclose(cf);
}




/*
 * Init configuration
 */
void cf_initialize()
{
    strncpy0(scf_libdir,   LIBDIR,   MAXPATH);
    strncpy0(scf_spooldir, SPOOLDIR, MAXPATH);
    strncpy0(scf_logdir,   LOGDIR,   MAXPATH);
}



/*
 * Set FIDO address
 */
void cf_set_addr(addr)
    char *addr;
{
    Node node;
    
    if( asc_to_node(addr, &node, FALSE) == ERROR )
    {
	Node *n = inet_to_ftn(addr);
	if(!n)
	{
	    fprintf(stderr, "Illegal FIDO address %s\n", addr);
	    exit(EX_USAGE);
	}
	node = *n;
    }

    scf_naddr        = 1;
    scf_addr[0].zone = node.zone;
    scf_addr[0].addr = node;
    scf_zone         = node.zone;
    scf_index        = 0;
    scf_c_addr       = scf_addr[0].addr;
    scf_c_uplink     = scf_addr[0].uplink;
}



/*
 * Set uplink FIDO address
 */
void cf_set_uplink(addr)
    char *addr;
{
    Node node;
    
    if( asc_to_node(addr, &node, FALSE) == ERROR )
    {
	Node *n = inet_to_ftn(addr);
	if(!n)
	{
	    fprintf(stderr, "Illegal FIDO address %s\n", addr);
	    exit(EX_USAGE);
	}
	node = *n;
    }
    
    scf_naddr          = 1;
    scf_addr[0].uplink = node;
    scf_zone           = scf_addr[0].zone;
    scf_index          = 0;
    scf_c_addr         = scf_addr[0].addr;
    scf_c_uplink       = scf_addr[0].uplink;
}



/*
 * Set/get lib/spool/log directory
 */
void cf_set_libdir(dir)
    char *dir;
{
    strncpy0(scf_libdir, dir, MAXPATH);
}

void cf_set_spooldir(dir)
    char *dir;
{
    strncpy0(scf_spooldir, dir, MAXPATH);
}

void cf_set_logdir(dir)
    char *dir;
{
    strncpy0(scf_logdir, dir, MAXPATH);
}

char *cf_libdir()
{
    return scf_libdir;
}

char *cf_spooldir()
{
    return scf_spooldir;
}

char *cf_logdir()
{
    return scf_logdir;
}



/*
 * Return hostname / domain name / fully qualified domain name
 */
char *cf_hostname()
{
    return scf_hostname;
}

char *cf_domainname()
{
    return scf_domainname;
}

char *cf_hostsdomain()
{
    return scf_hostsdomain;
}

char *cf_fqdn()
{
    return scf_fqdn;
}


/*
 * Return origin
 */
char *cf_origin()
{
    return scf_origin;
}


/*
 * Return organization
 */
char *cf_organization()
{
    return scf_organization;
}


/*
 * Set/get outbound/inbound
 */
void cf_set_outbound(dir)
    char *dir;
{
    strncpy0(scf_outbound, dir, MAXPATH);
}

char *cf_outbound()
{
    return scf_outbound;
}

void cf_set_inbound(dir)
    char *dir;
{
    strncpy0(scf_inbound, dir, MAXPATH);
}

char *cf_inbound()
{
    return scf_inbound;
}



/***** Stuff for processing zone info ****************************************/

/*
 * Return Internet domain for a FIDO zone
 */
char *cf_zones_inet_domain(zone)
    int zone;
{
    int i;
    
    /*
     * Search zone
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == zone)
	    return scf_zones[i].inet_domain;

    /*
     * Search default domain
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == 0)
	    return scf_zones[i].inet_domain;

    return FIDODOMAIN;
}



/*
 * Check for valid zone
 */
int cf_zones_check(zone)
    int zone;
{
    int i;
    
    /*
     * Search zone
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == zone)
	    return TRUE;

    return FALSE;    
}



/*
 * Traverse Internet domains
 */
char *cf_zones_trav(first)
int first;
{
    if(first)
	scf_izones = 0;

    return scf_izones < scf_nzones
	   ? scf_zones[scf_izones++].inet_domain
	   : NULL;
}



/*
 * Return outbound directory for a FIDO zone
 */
char *cf_zones_out(zone)
int zone;
{
    int i;
    
    /*
     * Search zone
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == zone)
	    return scf_zones[i].out;

    return NULL;
}



/*
 * Return FTN domain name for a FTN zone
 */
char *cf_zones_ftn_domain(zone)
    int zone;
{
    int i;
    
    /*
     * Search zone
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == zone)
	    return scf_zones[i].ftn_domain;

    /*
     * Search default domain
     */
    for(i=0; i<scf_nzones; i++)
	if(scf_zones[i].zone == 0)
	    return scf_zones[i].ftn_domain;

    return "fidonet";
}



/*
 * Traverse FTN addresses
 */
Node *cf_addr_trav(first)
    int first;
{
    static int iaddr;
    
    if(first)
	iaddr = 0;

    if(iaddr >= scf_naddr)		/* End of addresses */
	return NULL;

    return &scf_addr[iaddr++].addr;
}



/*
 * UNIX to MSDOS file name translation enabled
 */
int cf_dos()
{
    return scf_ndos != 0;
}



/*
 * Convert UNIX path on server to MSDOS path on client
 */
char *cf_dos_xlate(name)
    char *name;
{
    static char buf[MAXPATH];
    int i;
    char *s;
    int len;
    
    for(i=0; i<scf_ndos; i++)
    {
	len = strlen(scf_dos[i].path);
	if(!strncmp(name, scf_dos[i].path, len))
	{
	    strncpy0(buf, scf_dos[i].drive, MAXPATH);
	    strncat0(buf, name+len        , MAXPATH);
	    for(s=buf; *s; s++)
		switch(*s)
		{
		case '/':	*s = '\\';  break;
		}
	    return buf;
	}
    }
    
    return NULL;
}



/*
 * Address of FTN-Internet gateway
 */
Node cf_gateway()
{
    return scf_gateway;
}



/*
 * Get config statement(s) string(s)
 */
char *cf_get_string(name, first)
    char *name;
    int first;
{
    static struct st_cflist *last_listp = NULL;
    char *string;
    
    if(first)
	last_listp = scf_list_first;
    
    while(last_listp)
    {
	if(!stricmp(last_listp->key, name))		/* Found */
	{
	    string     = last_listp->string;
	    last_listp = last_listp->next;
	    return string;
	}
	last_listp = last_listp->next;
    }

    return NULL;
}
