/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: Fidonet mailer 
 *
 *****************************************************************************
 * Copyright (C) 1997-2002
 *   
 * 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, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/libs.h"
#include "../lib/memwatch.h"
#include "../lib/structs.h"
#include "../lib/users.h"
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/nodelist.h"
#include "../lib/dbnode.h"
#include "emsi.h"
#include "session.h"
#include "lutil.h"
#include "config.h"
#include "emsidat.h"
#include "filetime.h"


extern int	Loaded;


char *emsiencode(char *s)
{
	char		Base16Code[]="0123456789ABCDEF";
	static char	*buf;
	char		*p, *q;

	if (buf)
		free(buf);
	if ((buf = malloc(2 * strlen(s) + 1 * sizeof(char))) == NULL) {
		Syslog('+', "emsiencode:out of memory:string too long:\"%s\"", s);
		return s;
	}
	for (p = s, q = buf; *p != '\0';) {
		switch (*p) {
			case '\\':      *q++ = '\\'; *q++ = *p++; break;
			case '[':
			case ']':
			case '{':
			case '}':       *q++ = '\\';
					*q++ = Base16Code[(*p >> 4) & 0x0f];
					*q++ = Base16Code[*p & 0x0f];
					p++; break;
			default:        *q++ = *p++; break;
		}
	}
	*q = '\0';

	return buf;
}



char *mkemsidat(int caller)
{
	time_t	tt;
	char	cbuf[16];
	char	*p;
	faddr	*primary;
	int	i;

	p = xstrcpy((char *)"EMSI_DAT0000{EMSI}{");

	primary = bestaka_s(remote->addr);
	p = xstrcat(p, ascfnode(primary, 0x1f));

	for (i = 0; i < 40; i++)
		if ((CFG.aka[i].node) && (CFG.akavalid[i]) &&
		    ((CFG.aka[i].zone != primary->zone) ||
		     (CFG.aka[i].net  != primary->net) ||
		     (CFG.aka[i].node != primary->node) ||
		     (CFG.aka[i].point!= primary->point))) {
			p = xstrcat(p, (char *)" ");
			p = xstrcat(p, aka2str(CFG.aka[i]));
		}

	p=xstrcat(p,(char *)"}{");
	tidy_faddr(primary);

	if (emsi_local_password) 
		p=xstrcat(p,emsi_local_password);
	else 
		if (strlen(nodes.Spasswd)) {
			p = xstrcat(p, nodes.Spasswd);
		}

	if (emsi_local_opts & OPT_EII) {
		p = xstrcat(p, (char *)"}{");

		if (emsi_local_lcodes & LCODE_FNC) 
			p = xstrcat(p, (char *)"FNC,");
		if (emsi_local_lcodes & LCODE_RMA) 
			p = xstrcat(p, (char *)"RMA,");
		if (emsi_local_lcodes & LCODE_RH1) 
			p = xstrcat(p, (char *)"RH1,");

		if (emsi_local_lcodes & LCODE_PUA) 
			p=xstrcat(p,(char *)"PUA,");
		else if (emsi_local_lcodes & LCODE_PUP) 
			p=xstrcat(p,(char *)"PUP,");
		else if (emsi_local_lcodes & LCODE_NPU) 
			p=xstrcat(p,(char *)"NPU,");
		if (emsi_local_lcodes & LCODE_HAT) 
			p=xstrcat(p,(char *)"HAT,");
		if (emsi_local_lcodes & LCODE_HXT) 
			p=xstrcat(p,(char *)"HXT,");
		if (emsi_local_lcodes & LCODE_HRQ) 
			p=xstrcat(p,(char *)"HRQ,");
		if (*(p+strlen(p)-1) == ',') 
			*(p+strlen(p)-1) = '}';
		else 
			p=xstrcat(p,(char *)"}");
	} else {
		p=xstrcat(p,(char *)"}{8N1");
		if (emsi_local_lcodes & LCODE_RH1)
			p = xstrcat(p, (char *)",RH1");
		if (caller) {
			if (emsi_local_lcodes & LCODE_PUA) 
				p=xstrcat(p,(char *)",PUA");
			else if (emsi_local_lcodes & LCODE_PUP) 
				p=xstrcat(p,(char *)",PUP");
			else if (emsi_local_lcodes & LCODE_NPU) 
				p=xstrcat(p,(char *)",NPU");
		} else {
			if (emsi_local_lcodes & LCODE_HAT) 
				p=xstrcat(p,(char *)",HAT");
			if (emsi_local_lcodes & LCODE_HXT) 
				p=xstrcat(p,(char *)",HXT");
			if (emsi_local_lcodes & LCODE_HRQ) 
				p=xstrcat(p,(char *)",HRQ");
		}

		p = xstrcat(p, (char *)"}");
	}

	p=xstrcat(p,(char *)"{");
	if (emsi_local_protos & PROT_TCP)
		p=xstrcat(p,(char *)"TCP,");
	if (emsi_local_protos & PROT_HYD)
		p=xstrcat(p,(char *)"HYD,");
	if (emsi_local_protos & PROT_JAN)
		p=xstrcat(p,(char *)"JAN,");
	if (emsi_local_protos & PROT_ZAP) 
		p=xstrcat(p,(char *)"ZAP,");
	if (emsi_local_protos & PROT_ZMO) 
		p=xstrcat(p,(char *)"ZMO,");
	if (emsi_local_protos & PROT_DZA);
		p=xstrcat(p,(char *)"DZA,");
	if (emsi_local_protos & PROT_KER) 
		p=xstrcat(p,(char *)"KER,");
	if (emsi_local_protos ==  0) 
		p=xstrcat(p,(char *)"NCP,");
	if (emsi_local_opts & OPT_NRQ) 
		p=xstrcat(p,(char *)"NRQ,");
	if (emsi_local_opts & OPT_ARC) 
		p=xstrcat(p,(char *)"ARC,");
	if (emsi_local_opts & OPT_XMA) 
		p=xstrcat(p,(char *)"XMA,");
	if (emsi_local_opts & OPT_FNC) 
		p=xstrcat(p,(char *)"FNC,");
	if (emsi_local_opts & OPT_CHT) 
		p=xstrcat(p,(char *)"CHT,");
	if (emsi_local_opts & OPT_SLK) 
		p=xstrcat(p,(char *)"SLK,");
	if (emsi_local_opts & OPT_EII) 
		p=xstrcat(p,(char *)"EII,");
	if (emsi_local_opts & OPT_DFB) 
		p=xstrcat(p,(char *)"DFB,");
	if (emsi_local_opts & OPT_FRQ) 
		p=xstrcat(p,(char *)"FRQ,");
	if (*(p+strlen(p)-1) == ',') 
		*(p+strlen(p)-1) = '}';
	else 
		p=xstrcat(p,(char *)"}");

	sprintf(cbuf,"{%X}",PRODCODE);
	p=xstrcat(p,cbuf);
	p=xstrcat(p,(char *)"{mbcico}{");
	p=xstrcat(p,(char *)VERSION);
	p=xstrcat(p,(char *)"}{");
	p=xstrcat(p,(char *)__DATE__);
	p=xstrcat(p,(char *)"}{IDENT}{[");
	p=xstrcat(p,name?emsiencode(name):(char *)"Unknown");
	p=xstrcat(p,(char *)"][");
	p=xstrcat(p,emsiencode(CFG.location));
	p=xstrcat(p,(char *)"][");
	p=xstrcat(p,emsiencode(CFG.sysop_name));
	p=xstrcat(p,(char *)"][");
	p=xstrcat(p,phone?emsiencode(phone):(char *)"-Unpublished-");
	p=xstrcat(p,(char *)"][");
	if (CFG.Speed) 
		sprintf(cbuf,"%ld",CFG.Speed);
	else 
		strcpy(cbuf,"9600");
	p=xstrcat(p,cbuf);
	p=xstrcat(p,(char *)"][");
	p=xstrcat(p,flags?emsiencode(flags):(char *)"");
	p=xstrcat(p,(char *)"]}{TRX#}{[");
	tt = time(NULL);
	sprintf(cbuf,"%08lX",mtime2sl(tt));
	p=xstrcat(p,cbuf);
	p=xstrcat(p,(char *)"]}{TZUTC}{[");
	p=xstrcat(p,gmtoffset(tt));
	p=xstrcat(p,(char *)"]}");

	sprintf(cbuf,"%04X",(unsigned int)strlen(p+12));
	memcpy(p+8,cbuf,4);
	Syslog('I',"Prepared: \"%s\"",p);
	return p;
}



char *sel_brace(char*);
char *sel_brace(char *s)
{
	static	char *save;
	char	*p,*q;
	int	i;

	if (s == NULL) 
		s=save;
	for (;*s && (*s != '{');s++);
	if (*s == '\0') {
		save=s;
		return NULL;
	} else
		s++;

	for (p=s,q=s;*p;p++) 
		switch (*p) {
			case '}':	if (*(p+1) == '}') 
						*q++=*p++;
					else {
						*q='\0';
						save=p+1;
						goto exit;
					}
					break;
			case '\\':	if (*(p+1) == '\\') 
						*q++=*p++;
					else {
						sscanf(p+1,"%02x",&i);
						*q++=i;
						p+=2;
					}
					break;
			default:	*q++=*p;
					break;
		}
exit:
	return s;
}



char *sel_bracket(char*);
char *sel_bracket(char *s)
{
	static	char *save;
	char	*p,*q;
	int	i;

	if (s == NULL) 
		s=save;
	for (;*s && (*s != '[');s++);
	if (*s == '\0') {
		save=s;
		return NULL;
	} else
		s++;

	for (p=s,q=s;*p;p++) 
		switch (*p) {
			case ']':	if (*(p+1) == ']') 
						*q++=*p++;
					else {
						*q='\0';
						save=p+1;
						goto exit;
					}
					break;
			case '\\':	if (*(p+1) == '\\') 
						*q++=*p++;
					else {
						sscanf(p+1,"%02x",&i);
						*q++=i;
						p+=2;
					}
					break;
			default:	*q++=*p;
					break;
		}
exit:
	return s;
}



int scanemsidat(char *buf)
{
    fa_list **tmp,*tmpa;
    faddr   *fa;
    char    *p, *q, *mailer_prod, *mailer_name, *mailer_version, *mailer_serial;
    int	    dupe;

    Syslog('I',"got data packet: \"%s\"",buf);

    p = sel_brace(buf);
    if (strcasecmp(p,"EMSI") != 0) {
	Syslog('?', "This can never occur. Got \"%s\" instead of \"EMSI\"",p);
	return 1;
    }
    p = sel_brace(NULL);

    /*
     * Clear remote address list, and build a new one from EMSI data
     */
    tidy_falist(&remote);
    remote = NULL;
    tmp = &remote;
    for (q = strtok(p," "); q; q = strtok(NULL," ")) {
	if ((fa = parsefnode(q))) {
	    dupe = FALSE;
	    for (tmpa = remote; tmpa; tmpa = tmpa->next) {
		if ((tmpa->addr->zone == fa->zone) && (tmpa->addr->net == fa->net) &&
		    (tmpa->addr->node == fa->node) && (tmpa->addr->point == fa->point) &&
		    (strcmp(tmpa->addr->domain, fa->domain) == 0)) {
		    dupe = TRUE;
		    Syslog('i', "Double address %s", ascfnode(tmpa->addr, 0x1f));
		    break;
		}
	    }
	    if (!dupe) {
		*tmp = (fa_list*)malloc(sizeof(fa_list));
		(*tmp)->next = NULL;
		(*tmp)->addr = fa;
		tmp = &((*tmp)->next);
	    }
	}
    }

    for (tmpa = remote; tmpa; tmpa = tmpa->next) {
	Syslog('+', "address : %s",ascfnode(tmpa->addr,0x1f));
	(void)nodelock(tmpa->addr);
	/*
	 * With the loaded flag we prevent removing the noderecord 
	 * when the remote presents us an address we don't know about.
	 */
	if (!Loaded) {
	    if (noderecord(tmpa->addr))
		Loaded = TRUE;
	}
    }

    history.aka.zone  = remote->addr->zone;
    history.aka.net   = remote->addr->net;
    history.aka.node  = remote->addr->node;
    history.aka.point = remote->addr->point;
    sprintf(history.aka.domain, "%s", remote->addr->domain);

    if (emsi_remote_password) 
	free(emsi_remote_password);
    emsi_remote_password=xstrcpy(sel_brace(NULL));

    p=sel_brace(NULL);
    Syslog('+', "link    : %s", MBSE_SS(p));
    for (q=strtok(p,",");q;q=strtok(NULL,",")) {
	if (((q[0] >= '5') && (q[0] <= '8')) && ((toupper(q[1]) == 'N') || (toupper(q[1]) == 'O') ||
	    (toupper(q[1]) == 'E') || (toupper(q[1]) == 'S') || (toupper(q[1]) == 'M')) && ((q[2] == '1') || (q[2] == '2'))) {
		strncpy(emsi_remote_comm,q,3);
	}
	else if (strcasecmp(q,"PUA") == 0) emsi_remote_lcodes |= LCODE_PUA;
	else if (strcasecmp(q,"PUP") == 0) emsi_remote_lcodes |= LCODE_PUP;
	else if (strcasecmp(q,"NPU") == 0) emsi_remote_lcodes |= LCODE_NPU;
	else if (strcasecmp(q,"HAT") == 0) emsi_remote_lcodes |= LCODE_HAT;
	else if (strcasecmp(q,"HXT") == 0) emsi_remote_lcodes |= LCODE_HXT;
	else if (strcasecmp(q,"HRQ") == 0) emsi_remote_lcodes |= LCODE_HRQ;
	else if (strcasecmp(q,"FNC") == 0) emsi_remote_lcodes |= LCODE_FNC;
	else if (strcasecmp(q,"RMA") == 0) emsi_remote_lcodes |= LCODE_RMA;
	else if (strcasecmp(q,"RH1") == 0) emsi_remote_lcodes |= LCODE_RH1;
	else Syslog('+', "unrecognized EMSI link code: \"%s\"",q);
    }

    p=sel_brace(NULL);
    Syslog('+', "comp    : %s", p);
    for (q=strtok(p,",");q;q=strtok(NULL,",")) {
	if (strcasecmp(q,"DZA") == 0) emsi_remote_protos |= PROT_DZA;
	else if (strcasecmp(q,"ZAP") == 0) emsi_remote_protos |= PROT_ZAP;
	else if (strcasecmp(q,"ZMO") == 0) emsi_remote_protos |= PROT_ZMO;
	else if (strcasecmp(q,"JAN") == 0) emsi_remote_protos |= PROT_JAN;
	else if (strcasecmp(q,"HYD") == 0) emsi_remote_protos |= PROT_HYD;
	else if (strcasecmp(q,"KER") == 0) emsi_remote_protos |= PROT_KER;
	else if (strcasecmp(q,"TCP") == 0) emsi_remote_protos |= PROT_TCP;
	else if (strcasecmp(q,"NCP") == 0) emsi_remote_protos = 0;
	else if (strcasecmp(q,"NRQ") == 0) emsi_remote_opts |= OPT_NRQ;
	else if (strcasecmp(q,"ARC") == 0) emsi_remote_opts |= OPT_ARC;
	else if (strcasecmp(q,"XMA") == 0) emsi_remote_opts |= OPT_XMA;
	else if (strcasecmp(q,"FNC") == 0) emsi_remote_opts |= OPT_FNC;
	else if (strcasecmp(q,"CHT") == 0) emsi_remote_opts |= OPT_CHT;
	else if (strcasecmp(q,"SLK") == 0) emsi_remote_opts |= OPT_SLK;
	else if (strcasecmp(q,"EII") == 0) emsi_remote_opts |= OPT_EII;
	else if (strcasecmp(q,"DFB") == 0) emsi_remote_opts |= OPT_DFB;
	else if (strcasecmp(q,"FRQ") == 0) emsi_remote_opts |= OPT_FRQ;
	else if (strcasecmp(q,"BBS") == 0) Syslog('+', "remote has BBS activity now");
	else Syslog('+', "unrecognized EMSI proto/option code: \"%s\"",q);
    }
    if ((emsi_remote_opts & OPT_FNC) == 0) 
	remote_flags &= ~SESSION_FNC;

    mailer_prod=sel_brace(NULL);
    mailer_name=sel_brace(NULL);
    mailer_version=sel_brace(NULL);
    mailer_serial=sel_brace(NULL);
    Syslog('+', "uses    : %s [%s] version %s/%s", mailer_name, mailer_prod, mailer_version, mailer_serial);

    while ((p=sel_brace(NULL))) {
	if (strcasecmp(p,"IDENT") == 0) {
	    p=sel_brace(NULL);
	    Syslog('+', "system  : %s",(p=sel_bracket(p)));
	    strncpy(history.system_name, p, 35);
	    Syslog('+', "location: %s",(p=sel_bracket(NULL)));
	    strncpy(history.location, p, 35);
	    Syslog('+', "operator: %s",(p=sel_bracket(NULL)));
	    strncpy(history.sysop, p, 35);
	    if (remote && remote->addr)
		remote->addr->name=xstrcpy(p);
	    Syslog('+', "phone   : %s",sel_bracket(NULL));
	    Syslog('+', "baud    : %s",sel_bracket(NULL));
	    Syslog('+', "flags   : %s",sel_bracket(NULL));
	} else if (strcasecmp(p, "TZUTC") == 0) {
	    p = sel_brace(NULL);
	    p = sel_bracket(p);
	    if ((strlen(p) == 4) || (strlen(p) == 5))
		Syslog('+', "timezone: %s", p);
	    else
		Syslog('+', "TZUTC   : %s", p);
	} else if (strcasecmp(p,"TRX#") == 0) {
	    time_t tt, now;
	    char ctt[32];

	    now = time(NULL);
	    p=sel_brace(NULL);
	    p=sel_bracket(p);
	    if (sscanf(p,"%08lx",&tt) == 1) {
		strcpy(ctt,date(sl2mtime(tt)));
		Syslog('+', "time    : %s",ctt);
		Syslog('+', "tranx   : %08lX/%08lX [%ld]", now, sl2mtime(tt), now - sl2mtime(tt));
	    } else
		Syslog('+', "remote     TRX#: %s",p);
	} else if (strcasecmp(p, "TRAF") == 0) {
	    unsigned long tt, tt1;

	    p = sel_brace(NULL);
	    if (sscanf(p, "%08lx %08lx", &tt, &tt1) == 2) {
		Syslog('+', "netmail : %u byte(s)", tt);
		Syslog('+', "echomail: %u byte(s)", tt1);
	    } else {
		Syslog('+', "TRAF    : %s", p);
	    }
	} else if (strcasecmp(p, "MOH#") == 0) {
	    unsigned long tt;

	    p = sel_brace(NULL);
	    p = sel_bracket(p);
	    if (sscanf(p, "%08lx", &tt) == 1)
		Syslog('+', "on hold : %u byte(s)", tt);
	    else
		Syslog('+', "MOH#    : %s", p);
	} else {
	    q=sel_brace(NULL);
	    Syslog('+', "extra   : \"%s\" value: \"%s\"",p,q);
	}
    }

    return 0;
}