/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: Fidonet Technology Network functions
 * Remark ................: From ifmail with patches from P.Saratxaga
 *
 *****************************************************************************
 * Copyright (C) 1997-2005
 *   
 * Michiel Broek		FIDO:	2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS 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.
 *
 * MBSE BBS 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 MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/


#include "../config.h"
#include "mbselib.h"
#include "users.h"
#include "mbsedb.h"


#ifndef MAXUSHORT
#define MAXUSHORT 65535
#endif
#ifndef BLANK_SUBS
#define BLANK_SUBS '.'
#endif


int	addrerror = 0;

void tidy_faddr(faddr *addr)
{
	if (addr == NULL) 
		return;
	if (addr->name) 
		free(addr->name);
	if (addr->domain) 
		free(addr->domain);
	free(addr);
}



faddr *parsefnode(char *s)
{
	faddr	addr, *tmp;
	char	*buf, *str, *p, *q, *n;
	int	good = 1;

	if (s == NULL) 
		return NULL;

	str = buf = xstrcpy(s);

	while (isspace(*str)) 
		str++;
	if (*(p=str+strlen(str)-1) == '\n') 
		*(p--) ='\0';
	while (isspace(*p)) 
		*(p--) = '\0';

	p=str + strlen(str) - 1;
	if (((*str == '(') && (*p == ')')) || ((*str == '\"') && (*p == '\"')) ||
	    ((*str == '\'') && (*p == '\'')) || ((*str == '<') && (*p == '>')) ||
	    ((*str == '[') && (*p == ']')) || ((*str == '{') && (*p == '}'))) {
		str++;
		*p = '\0';
	}

	memset(&addr, 0, sizeof(faddr));
	if ((p = strrchr(str,' '))) {
		n = str;
		str = p + 1;
		while (isspace(*p)) 
			*(p--) = '\0';
		if (!strcasecmp(p - 2," of")) 
			*(p-2) = '\0';
		else if (!strcasecmp(p - 1," @")) 
			*(p-1) = '\0';
		p -= 3;
		while (isspace(*p) || (*p == ',')) 
			*(p--) = '\0';
		if (strlen(n) > MAXNAME) 
			n[MAXNAME] = '\0';
		if (*n != '\0') 
			addr.name = xstrcpy(n);
	}

	if ((p = strrchr(str, '@'))) 
		*(p++) = '\0';
	else if ((p = strrchr(str,'%'))) 
		*(p++) = '\0';
	else if ((q = strrchr(str,'#'))) { 
		*(q++) = '\0'; 
		p = str; 
		str = q; 
	} else if (addr.name && (strrchr(addr.name,'@'))) {
		str = xstrcpy(addr.name);
		if ((p=strrchr(str,'@'))) 
			*(p++) = '\0';
		else if ((p=strrchr(str,'%'))) 
			*(p++) = '\0';
        } else 
		p = NULL;

	if (p && ((q = strchr(p,'.')))) 
		*q = '\0';

	addr.point = 0;
	addr.node = 0;
	addr.net = 0;
	addr.zone = 0;
	addr.domain = xstrcpy(p);

	if ((p = strchr(str, ':'))) {
		*(p++) = '\0';
		if (strspn(str, "0123456789") == strlen(str))
			addr.zone = atoi(str);
		else 
			if (strcmp(str,"*") == 0)
				addr.zone = -1;
			else 
				good = 0;
		str = p;
	}

	if ((p = strchr(str, '/'))) {
		*(p++) = '\0';
		if (strspn(str, "0123456789") == strlen(str))
			addr.net = atoi(str);
		else 
			if (strcmp(str, "*") == 0)
				addr.net = -1;
			else 
				good = 0;
		str = p;
	}

	if ((p=strchr(str, '.'))) {
		*(p++) = '\0';
		if (strspn(str, "0123456789") == strlen(str))
			addr.node = atoi(str);
		else 
			if (strcmp(str, "*") == 0)
				addr.node = -1;
			else 
				good = 0;
		str = p;
	} else {
		if (strspn(str, "0123456789") == strlen(str))
			addr.node = atoi(str);
		else 
			if (strcmp(str, "*") == 0)
				addr.node = -1;
			else 
				good = 0;
		str = NULL;
	}

	if (str) {
		if (strspn(str, "0123456789") == strlen(str))
			addr.point = atoi(str);
		else 
			if (strcmp(str, "*") == 0)
				addr.point = -1;
			else 
				good = 0;
	}

	if (buf) 
		free(buf);

	if (good) {
		tmp = (faddr *)malloc(sizeof(addr));
		tmp->name   = NULL;
		tmp->domain = addr.domain;
		tmp->point  = addr.point;
		tmp->node   = addr.node;
		tmp->net    = addr.net;
		tmp->zone   = addr.zone;
		return tmp;
	} else {
		if (addr.name) 
			free(addr.name);
		if (addr.domain) 
			free(addr.domain);
		return NULL;
	}
}



faddr *parsefaddr(char *s)
{
	faddr		*tmpaddr = NULL;
	parsedaddr	rfcaddr;
	int		gotzone = 0, gotnet = 0, gotnode = 0, gotpoint = 0;
	int		zone = 0, net = 0, noden = 0, point = 0;
	char		*domain = NULL, *freename = NULL;
	int		num;
	char		*p = NULL,*q = NULL,*t = NULL;
	int		l, quoted;
	FILE		*fp;

	rfcaddr.target    = NULL;
	rfcaddr.remainder = NULL;
	rfcaddr.comment   = NULL;

	t = xstrcpy(s);
	if (*(q=t+strlen(t)-1) == '\n') 
		*q='\0';
	if (((*(p=t) == '(') && (*(q=p+strlen(p)-1) == ')')) || ((*p == '\"') && (*q == '\"'))) {
		p++;
		*q='\0';
	}

	if (strchr(s,'@') || strchr(s,'%') || strchr(s,'!'))
		rfcaddr = parserfcaddr(p);
	else {
		addrerror = 0;
		rfcaddr.target = xstrcpy(p);
	}
	free(t);
	if ((addrerror) || (rfcaddr.target == NULL))
		goto leave;

	p = calloc(PATH_MAX, sizeof(char));
	snprintf(p, PATH_MAX -1, "%s/etc/domain.data", getenv("MBSE_ROOT"));
	if ((fp = fopen(p, "r")) == NULL) {
		WriteError("$Can't open %s", p);
		free(p);
	} else {
		free(p);
		fread(&domainhdr, sizeof(domainhdr), 1, fp);
		p = rfcaddr.target;

		while (fread(&domtrans, domainhdr.recsize, 1, fp) == 1) {
			q = p + strlen(p) - strlen(domtrans.intdom);
			if ((q >= p) && (strcasecmp(domtrans.intdom, q) == 0)) {
				*q = '\0';
				q = malloc(strlen(p) + strlen(domtrans.ftndom) +1);
				strcpy(q, p);
				strcat(q, domtrans.ftndom);
				p = q;
				free(rfcaddr.target);
				rfcaddr.target = p;
				break;
			}
		}
		fclose(fp);
	}

	if (((l = strlen(rfcaddr.target)) > 4) && (strcasecmp(rfcaddr.target + l - 4,".ftn") == 0)) {
		rfcaddr.target[l-4] = '\0';
	}

	for (p = strtok(rfcaddr.target, "."); p; p = strtok(NULL,".")) {
		if (((l = strlen(p + 1)) > 0) && (l <= 5) &&
		    (strspn(p + 1, "0123456789") == l)) {
			num = atol(p + 1);
			switch (*p) {
			case 'z':
			case 'Z':
				gotzone++;
				if (num > MAXUSHORT) 
					addrerror |= ADDR_BADTOKEN;
				zone = num;
				break;
			case 'n':
			case 'N':
				gotnet++;
				if (num > MAXUSHORT) 
					addrerror |= ADDR_BADTOKEN;
				net = num;
				break;
			case 'f':
			case 'F':
				gotnode++;
				if (num > MAXUSHORT) 
					addrerror |= ADDR_BADTOKEN;
				noden = num;
				break;
			case 'p':
			case 'P':
				gotpoint++;
				if (num > MAXUSHORT) 
					addrerror |= ADDR_BADTOKEN;
				point = num;
				break;
			default:
				if (gotnet && gotnode) {
					if (domain == NULL)
						domain = xstrcpy(p);
				} else 
					addrerror |= ADDR_BADTOKEN;
				break;
			}
		} else { /* not "cNNNN" token */
			if (gotnet && gotnode) {
				if (domain == NULL) 
					domain = xstrcpy(p);
			} else 
				addrerror |= ADDR_BADTOKEN;
		}
	}

	if ((gotzone > 1) || (gotnet != 1) || (gotnode != 1) || (gotpoint > 1)) {
		addrerror |= ADDR_BADSTRUCT;
	}

	if (addrerror) 
		goto leave;

	if (rfcaddr.remainder) {
		quoted = 0;
		if ((*(p = rfcaddr.remainder) == '\"') && (*(q = p + strlen(p) -1) == '\"')) {
			p++;
			*q='\0';
			quoted = 1;
		}
		if (strchr(p,'@') || strchr(p,'%') || strchr(p,'!')) {
			if (((q=strrchr(p,'%'))) && !strchr(p,'@'))
				*q = '@';
		} else if ((!quoted) && (!strchr(p, ' '))) {
			for (q = p; *q; q++) {
				if (*q == BLANK_SUBS)
					*q = ' ';
				else if (*q == '.') 
					*q = ' ';
				else if (*q == '_')
					*q = ' ';
			}
		}
		for (q = p; *q; q++) {
			if ((*q == '\\') && ((*(q+1) == '"') || ((*(q+1) == '\\') && (!quoted)))) {
				*q='\0';
				strcat(p, q+1);
			}
		}
		if (strspn(p," ") != strlen(p)) 
			freename = xstrcpy(p);
	}

	tmpaddr=(faddr*)malloc(sizeof(faddr));

	tmpaddr->zone=zone;
	tmpaddr->net=net;
	tmpaddr->node=noden;
	tmpaddr->point=point;
	tmpaddr->domain=domain;
	domain=NULL;
	tmpaddr->name=freename;
	freename=NULL;

leave:
	if (domain) 
		free(domain);
	if (freename) 
		free(freename);

	tidyrfcaddr(rfcaddr);
	return tmpaddr;
}



char *ascinode(faddr *a, int fl)
{
	static char	buf[128], *f, *t, *p;
	static char	*q;
	int		skip, found = FALSE;
	FILE		*fp;

	if (a == NULL) {
		strcpy(buf,"<none>");
		return buf;
	}

	buf[0]='\0';
	if ((fl & 0x80) && (a->name)) {
		if ((strchr(a->name,'.')) || (strchr(a->name,'@')) ||
		    (strchr(a->name,'\'')) || (strchr(a->name,',')) ||
		    (strchr(a->name,'<')) || (strchr(a->name,'>')))
			snprintf(buf+strlen(buf), 128, "\"%s\" <", a->name);
		else
			snprintf(buf+strlen(buf), 128, "%s <", a->name);
	}

	if ((fl & 0x40) && (a->name)) {
		f = a->name;
		t = buf + strlen(buf);
		skip = 0;
		if ((!strchr(f,'@')) && ((strchr(f,BLANK_SUBS)) || (strchr(f,'.')) || (strchr(f,'_')))) { 
			skip = 1; 
			*t++='"'; 
		}
		while (*f) {
			switch (*f) {
			case '_':
			case '.':	*t++=*f; 
					break;
			case ' ':	if (!skip) 
						*t++=BLANK_SUBS; 
					else {
						*t++='='; *t++='2'; *t++='0';
					}
					break;
			case ',':	{ /* "," is a problem on mail addr */
					if (!skip) 
						*t++='\\';
					*t++='=';
					*t++='2';
					*t++='c';
					}
			case '@':	if (skip) { 
						*t++='"'; 
						skip=0; 
					}
					*t++='%'; 
					break;
			case '"':	*t++='\\'; 
					*t++=*f; 
					break;
			case '>':
			case '<':
			case '\'':	if (!skip) 
						*t++='\\'; 
					*t++=*f; 
					break;
			default:	if ((((*f & 0xff) > 0x29) && ((*f & 0xff) < 0x3a)) ||
                                            (((*f & 0xff) > 0x40) && ((*f & 0xff) < 0x5b)) ||
                                            (((*f & 0xff) > 0x60) && ((*f & 0xff) < 0x7b)))
						*t++=*f;
					else {
						if (!skip) 
							*t++='\\';
						*t++=*f;
					}
					break;
			}
			f++;
		}
		if (skip) 
			*t++='"'; 
		skip = 0;
		*t++ = '@';
		*t++ = '\0';
	}

	if ((fl & 0x01) && (a->point))
		snprintf(buf+strlen(buf), 128, "p%u.", a->point);
	if (fl & 0x02)
		snprintf(buf+strlen(buf), 128, "f%u.", a->node);
	if (fl & 0x04)
		snprintf(buf+strlen(buf), 128, "n%u.", a->net);
	if ((fl & 0x08) && (a->zone))
		snprintf(buf+strlen(buf), 128, "z%u.", a->zone);
	buf[strlen(buf)-1]='\0';

	if (fl & 0x10) {
		if (a->domain)
			snprintf(buf+strlen(buf), 128, ".%s", a->domain);
	}

	if (fl & 0x20) {
		if (a->domain) {
			if ((fl & 0x10) == 0)
				snprintf(buf+strlen(buf), 128, ".%s", a->domain);
		} else {
			if (SearchFidonet(a->zone))
				snprintf(buf+strlen(buf), 128, ".%s", fidonet.domain);
			else
				snprintf(buf+strlen(buf), 128, ".fidonet");
		}

		p = calloc(128, sizeof(char));
		snprintf(p, 128, "%s/etc/domain.data", getenv("MBSE_ROOT"));
		if ((fp = fopen(p, "r")) == NULL) {
			WriteError("$Can't open %s", p);
		} else {
			fread(&domainhdr, sizeof(domainhdr), 1, fp);
			while (fread(&domtrans, domainhdr.recsize, 1, fp) == 1) {
				q = buf + strlen(buf) - strlen(domtrans.ftndom);
				if ((q >= buf) && (strcasecmp(domtrans.ftndom, q) == 0)) {
					strcpy(q, domtrans.intdom);
					found = TRUE;
					break;
				}
			}
			fclose(fp);
		}
		free(p);
		if (!found) 
			snprintf(buf + strlen(buf), 128, ".ftn");
	}

	if ((fl & 0x80) && (a->name))
		snprintf(buf+strlen(buf), 128, ">");

	return buf;
}



/*
 * Return ASCII string for node, the bits in 'fl' set the
 * output format.
 */
char *ascfnode(faddr *a, int fl)
{
	static char buf[128];

	if (a == NULL) {
		strcpy(buf, "<none>");
		return buf;
	}

	buf[0] = '\0';
	if ((fl & 0x40) && (a->name))
		snprintf(buf+strlen(buf),128,"%s of ",a->name);
	if ((fl & 0x08) && (a->zone))
		snprintf(buf+strlen(buf),128,"%u:",a->zone);
	if (fl & 0x04)
		snprintf(buf+strlen(buf),128,"%u/",a->net);
	if (fl & 0x02)
		snprintf(buf+strlen(buf),128,"%u",a->node);
	if ((fl & 0x01) && (a->point))
		snprintf(buf+strlen(buf),128,".%u",a->point);
	if ((fl & 0x10) && (a->domain))
		snprintf(buf+strlen(buf),128,"@%s",a->domain);
	return buf;
}



int metric(faddr *a1, faddr *a2)
{
	if ((a1->domain != NULL) && (a2->domain != NULL) &&
	    (strcasecmp(a1->domain,a2->domain) != 0))
		return METRIC_DOMAIN;
	if ((a1->zone != 0) && (a2->zone != 0) &&
	    (a1->zone != a2->zone)) return METRIC_ZONE;
	if (a1->net   != a2->net)   return METRIC_NET;
	if (a1->node  != a2->node)  return METRIC_NODE;
	if (a1->point != a2->point) return METRIC_POINT;
	return METRIC_EQUAL;
}



/*
 * Convert mbse style to ifcico style.
 */
faddr *fido2faddr(fidoaddr aka)
{
	faddr	*fa;

	fa = (faddr *)malloc(sizeof(faddr));
	fa->name   = NULL;
	fa->domain = xstrcpy(aka.domain);;
	fa->zone   = aka.zone;
	fa->net    = aka.net;
	fa->node   = aka.node;
	fa->point  = aka.point;

	return fa;
}



/*
 * Convert ifcico style to mbse style
 */
fidoaddr *faddr2fido(faddr *aka)
{
	fidoaddr *Sys;

	Sys = (fidoaddr *)malloc(sizeof(fidoaddr));
	memset(Sys, 0, sizeof(Sys));
	Sys->zone  = aka->zone;
	Sys->net   = aka->net;
	Sys->node  = aka->node;
	Sys->point = aka->point;
	if (aka->domain != NULL)
		snprintf(Sys->domain, 13, "%s", aka->domain);

	return Sys;
}



faddr *bestaka_s(faddr *addr)
{
	faddr	*best = NULL, *tmp;
	int	i, minmetric, wt;

	for (i = 0; i < 40; i++) {
		if (CFG.akavalid[i]) {
			best = fido2faddr(CFG.aka[i]);
			break;
		}
	}
	if (addr == NULL) 
		return best;
	minmetric = metric(addr, best);

	for (i = 0; i < 40; i++) {
		if (CFG.akavalid[i]) {
			tmp = fido2faddr(CFG.aka[i]);
			wt = metric(addr, tmp);
			tidy_faddr(tmp);

			if ((wt < minmetric) && ((best->point != 0) || (minmetric > METRIC_NODE))) {
				/*
				 * In the same network, use node address even when 
				 * routing to the node where we have a point address
				 */
				minmetric = wt;
				tidy_faddr(best);
				best = fido2faddr(CFG.aka[i]);
			}
		}
	}
	return best;
}



int is_local(faddr *addr)
{
    int	    i;
    faddr   *tmp;

    for (i = 0; i < 40; i++) {
	tmp = fido2faddr(CFG.aka[i]);
	if ((CFG.akavalid[i]) && (metric(tmp, addr) == METRIC_EQUAL)) {
	    tidy_faddr(tmp);
	    return TRUE;
	}
	tidy_faddr(tmp);
    }
    return FALSE;
}



int chkftnmsgid(char *msgid)
{
	faddr *p;

	if (msgid == NULL)
		return 0;

	while (isspace(*msgid)) 
		msgid++;
	if ((p=parsefaddr(msgid))) {
		if (p->name && (strspn(p->name,"0123456789") == strlen(p->name)))
			return 1;
	} else if ((!strncmp(msgid,"<MSGID_",7)) || (!strncmp(msgid,"<NOMSGID_",9)) || (!strncmp(msgid,"<ftn_",5))) {
		return 1;
	}
	return 0;
}