/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: Fidonet mailer 
 *
 *****************************************************************************
 * Copyright (C) 1997-2004
 *   
 * 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 "../lib/mbselib.h"
#include "../lib/users.h"
#include "../lib/nodelist.h"
#include "../lib/mbsedb.h"
#include "emsi.h"
#include "session.h"
#include "lutil.h"
#include "config.h"
#include "emsidat.h"
#include "filetime.h"


extern int	Loaded;
extern int	mypid;
extern int	emsi_akas;



/*
 * Encode EMSI string. If called with a NULL pointer memory is freed.
 */
char *emsiencode(char *s)
{
    char	Base16Code[]="0123456789ABCDEF";
    static char	*buf;
    char	*p, *q;

    if (buf)
	free(buf);
    buf = NULL;
    if (s == NULL)
	return NULL;
    
    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], *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.IP_Speed) && (emsi_local_protos & PROT_TCP))
	sprintf(cbuf,"%ld",CFG.IP_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);
    emsiencode(NULL);   /* Free memory */
    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;

    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);
	    }
	}
    }

    tmp = &remote;
    while (*tmp) {
	if (nodelock((*tmp)->addr, mypid)) {
	    Syslog('+', "address : %s is locked, removed from aka list",ascfnode((*tmp)->addr,0x1f));
	    tmpa=*tmp;
	    *tmp=(*tmp)->next;
	    free(tmpa);
	} else {
	    /*
	     * With the loaded flag we prevent removing the noderecord
	     * when the remote presents us an address we don't know about.
	     */
	    emsi_akas++;
	    Syslog('+', "address : %s",ascfnode((*tmp)->addr,0x1f));
	    if (!Loaded) {
		if (noderecord((*tmp)->addr))
		    Loaded = TRUE;
	    }
	    tmp = &((*tmp)->next);
	}
    }

    if (emsi_akas) {	/* Only if any aka's left */
	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", printable(remote->addr->domain, 0));
    }

    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;
}