/* net/rom user command processing */

#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "ax25.h"
#include "netrom.h"
#include "timer.h"
#include "iface.h"
#include "lapb.h"
#include "cmdparse.h"
/*#include <ctype.h>*/

static int dointerface(), dobcnodes(), donodetimer(), donrroute(),
		   doobsotimer(), donodefilter() ;

static struct cmds nrcmds[] = {
	"bcnodes",	dobcnodes,	2,	"netrom bcnodes <interface>", NULLCHAR,
	"interface",	dointerface,	4,
		"netrom interface <interface> <alias> <quality>",	NULLCHAR,
	"nodefilter",	donodefilter,	0,	NULLCHAR,	NULLCHAR,
	"nodetimer",	donodetimer,	0,	NULLCHAR,	NULLCHAR,
	"obsotimer",	doobsotimer,	0,	NULLCHAR,	NULLCHAR,
	"route",	donrroute,	0,	NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,		0,
	"netrom subcommands: bcnodes interface nodetimer nodefilter obsotimer route",
	NULLCHAR
} ;

static struct timer nodetimer ;	/* timer for nodes broadcasts */
static struct timer obsotimer ;	/* timer for aging routes */

/* Command multiplexer */
donetrom(argc,argv)
int argc ;
char *argv[] ;
{
	return subcmd(nrcmds,argc,argv) ;
}

static int dorouteadd(), doroutedrop(), doroutedump(), dorouteinfo() ;

static struct cmds routecmds[] = {
	"add",	dorouteadd,	6,
		"netrom route add <alias> <destination> <interface> <quality> <neighbor>",
		"add failed",
	"drop",	doroutedrop, 4,
		"netrom route drop <destination> <neighbor> <interface>",
		"drop failed",
	"info", dorouteinfo, 2,
		"netrom route info <destination>", NULLCHAR,
	NULLCHAR,	NULLFP,	0,
		"netrom route subcommands: add drop info",
		NULLCHAR
} ;

/* Route command multiplexer */
static
donrroute(argc, argv)
int argc ;
char *argv[] ;
{
	if (argc < 2) {
		doroutedump() ;
		return 0 ;
	}
	return subcmd(routecmds,argc,argv) ;
}

/* Dump a list of known routes */
static
doroutedump()
{
	register struct nrroute_tab *rp ;
	register int i, column ;
	char buf[16] ;
	char *cp ;
	
	column = 1 ;
	
	for (i = 0 ; i < NRNUMCHAINS ; i++)
		for (rp = nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rp->next) {
			strcpy(buf,rp->alias) ;
			/* remove trailing spaces */
			if ((cp = index(buf,' ')) == NULLCHAR)
				cp = &buf[strlen(buf)] ;
			if (cp != buf)		/* don't include colon for null alias */
				*cp++ = ':' ;
			pax25(cp,&rp->call) ;
			printf("%-16s  ",buf) ;
			if (column++ == 4) {
				printf("\n") ;
				column = 1 ;
			}
		}

	if (column != 1)
		printf("\n") ;
		
	return 0 ;
}

/* print detailed information on an individual route */
dorouteinfo(argc,argv)
int argc ;
char *argv[] ;
{
	register struct nrroute_tab *rp ;
	register struct nr_bind *bp ;
	register struct nrnbr_tab *np ;
	struct ax25_addr dest ;
	char neighbor[60] ;

	if (setcall(&dest,argv[1]) == -1) {
		printf ("bad destination name\n") ;
		return -1 ;
	}
		
	if ((rp = find_nrroute(&dest)) == NULLNRRTAB) {
		printf("no such route\n") ;
		return -1 ;
	}

	for (bp = rp->routes ; bp != NULLNRBIND ; bp = bp->next) {
		np = bp->via ;
		psax25(neighbor,np->call) ;
		printf("%1s %3d  %3d  %-8s  %s\n",
				(bp->flags & NRB_PERMANENT ? "P" : " "),
				bp->quality,bp->obsocnt,
				nrifaces[np->interface].interface->name,
				neighbor) ;
	}
	return 0 ;
}
		
/* convert a null-terminated alias name to a blank-filled, upcased */
/* version.  Return -1 on failure. */
static int
putalias(to,from)
register char *to, *from ;
{
	int len, i ;
	
	if ((len = strlen(from)) > ALEN) {
		printf ("alias too long - six characters max\n") ;
		return -1 ;
	}
	
	for (i = 0 ; i < ALEN ; i++) {
		if (i < len) {
			if (islower(*from))
				*to++ = toupper(*from++) ;
			else
				*to++ = *from++ ;
		}
		else
			*to++ = ' ' ;
	}
			
	*to = '\0' ;
	return 0 ;
}

/* Add a route */
dorouteadd(argc, argv)
int argc ;
char *argv[] ;
{
	char alias[7] ;
	struct ax25_addr dest ;
	unsigned quality ;
	char neighbor[AXALEN * 3] ;
	register int i ;
	int naddr ;

	/* format alias (putalias prints error message if necessary) */
	if (putalias(alias,argv[1]) == -1)
		return -1 ;

	/* format destination callsign */
	if (setcall(&dest,argv[2]) == -1) {
		printf("bad destination callsign\n") ;
		return -1 ;
	}

	/* find interface */
	for (i = 0 ; i < nr_numiface ; i++)
		if (!strcmp(nrifaces[i].interface->name,argv[3]))
			break ;
	if (i == nr_numiface) {
		printf("Interface \"%s\" not found\n",argv[3]) ;
		return -1 ;
	}
	
	/* get and check quality value */
	if ((quality = atoi(argv[4])) > 255) {
		printf("maximum route quality is 255\n") ;
		return -1 ;
	}

	/* make sure no more than 2 digis */
	naddr = argc - 5 ;
	if (naddr > 3) {
		printf("no more than 2 digipeaters for a net/rom neighbor\n") ;
		return -1 ;
	}
	
	/* format neighbor address string */
	setpath(neighbor,&argv[5],naddr) ;

	return nr_routeadd(alias,&dest,i,quality,neighbor,1) ;
}


/* drop a route */
static
doroutedrop(argc,argv)
int argc ;
char *argv[] ;
{
	struct ax25_addr dest, neighbor ;
	register int i ;

	/* format destination and neighbor callsigns */
	if (setcall(&dest,argv[1]) == -1) {
		printf("bad destination callsign\n") ;
		return -1 ;
	}
	if (setcall(&neighbor,argv[2]) == -1) {
		printf("bad neighbor callsign\n") ;
		return -1 ;
	}

	/* find interface */
	for (i = 0 ; i < nr_numiface ; i++)
		if (!strcmp(nrifaces[i].interface->name,argv[3]))
			break ;
	if (i == nr_numiface) {
		printf("Interface \"%s\" not found\n",argv[3]) ;
		return -1 ;
	}

	return nr_routedrop(&dest,&neighbor,i) ;
}
	
	
/* make an interface available to net/rom */
int
dointerface(argc,argv)
int argc ;
char *argv[] ;
{
	register char *sp, *dp ;
	int i, len ;
	register struct interface *ifp ;
	extern struct interface *ifaces ;

	if (nr_interface == NULLIF) {
		printf("Attach netrom interface first\n") ;
		return 1 ;
	}
	
	if (nr_numiface >= NRNUMIFACE) {
		printf("Only %d net/rom interfaces available\n",NRNUMIFACE) ;
		return 1 ;
	}
	
	for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
		if(strcmp(argv[1],ifp->name) == 0)
			break;
	}
	if(ifp == NULLIF){
		printf("Interface \"%s\" unknown\n",argv[1]);
		return 1;
	}
	if (!(ifp->flags != CONNECT_MODE)) {	/* DG2KK: was: IF_AX25 */
		printf("Must be an ax.25 interface\n") ;
		return 1 ;
	}
	for (i = 0 ; i < nr_numiface ; i++)
		if (nrifaces[i].interface == ifp) {
			printf("Interface \"%s\" is already registered\n",argv[1]) ;
			return 1 ;
		}
		
	nrifaces[nr_numiface].interface = ifp ;

	if (putalias(nrifaces[nr_numiface].alias,argv[2]) == -1)
		return 1 ;
		
	if ((nrifaces[nr_numiface].quality = atoi(argv[3])) > 255) {
		printf("Quality cannot be greater than 255\n") ;
		return 1 ;
	}
		
	nr_numiface++ ;			/* accept this interface */
	return 0 ;
}

/* Broadcast nodes list on named interface. */

int
dobcnodes(argc,argv)
int argc ;
char *argv[] ;
{
	register int i ;
	for (i = 0 ; i < nr_numiface ; i++)
		if (!strcmp(nrifaces[i].interface->name,argv[1]))
			break ;
	if (i == nr_numiface) {
		printf("Interface \"%s\" not found\n",argv[1]) ;
		return 1 ;
	}
		
	nr_bcnodes(i) ;
}

#define TICKSPERSEC	(1000 / MSPTICK)	/* Ticks per second */

/* Set outbound node broadcast interval */
static int
donodetimer(argc,argv)
int argc;
char *argv[];
{
	int donodetick();

	if(argc < 2){
		printf("%d/%d\n",(nodetimer.start - nodetimer.count)/TICKSPERSEC,
		nodetimer.start/TICKSPERSEC);
		return 0;
	}
	stop_timer(&nodetimer) ;	/* in case it's already running */
	nodetimer.func = (void (*)())donodetick;/* what to call on timeout */
	nodetimer.arg = NULLCHAR;		/* dummy value */
	nodetimer.start = atoi(argv[1])*TICKSPERSEC;	/* set timer duration */
	start_timer(&nodetimer);		/* and fire it up */
	return 0;
}

static int
donodetick()
{
	register int i ;

	for (i = 0 ; i < nr_numiface ; i++)
		nr_bcnodes(i) ;

	/* Restart timer */
	start_timer(&nodetimer) ;
}

/* Set timer for aging routes */
static int
doobsotimer(argc,argv)
int argc;
char *argv[];
{
	extern int doobsotick();

	if(argc < 2){
		printf("%d/%d\n",(obsotimer.start - obsotimer.count)/TICKSPERSEC,
		obsotimer.start/TICKSPERSEC);
		return 0;
	}
	stop_timer(&obsotimer) ;	/* just in case it's already running */
	obsotimer.func = (void (*)())doobsotick;/* what to call on timeout */
	obsotimer.arg = NULLCHAR;		/* dummy value */
	obsotimer.start = atoi(argv[1])*TICKSPERSEC;	/* set timer duration */
	start_timer(&obsotimer);		/* and fire it up */
	return 0;
}


/* Go through the routing table, reducing the obsolescence count of
 * non-permanent routes, and purging them if the count reaches 0
 */
static int
doobsotick()
{
	register struct nrnbr_tab *np ;
	register struct nrroute_tab *rp, *rpnext ;
	register struct nr_bind *bp, *bpnext ;
	struct ax25_addr neighbor ;
	int i ;

	for (i = 0 ; i < NRNUMCHAINS ; i++) {
		for (rp = nrroute_tab[i] ; rp != NULLNRRTAB ; rp = rpnext) {
			rpnext = rp->next ; 	/* save in case we free this route */
			for (bp = rp->routes ; bp != NULLNRBIND ; bp = bpnext) {
				bpnext = bp->next ;	/* in case we free this binding */
				if (bp->flags & NRB_PERMANENT)	/* don't age these */
					continue ;
				if (--bp->obsocnt == 0) {		/* time's up! */
					if (bp->next != NULLNRBIND)
						bp->next->prev = bp->prev ;
					if (bp->prev != NULLNRBIND)
						bp->prev->next = bp->next ;
					else
						rp->routes = bp->next ;
					rp->num_routes-- ;			/* one less binding */
					np = bp->via ;				/* find the neighbor */
					free(bp) ;					/* now we can free the bind */
					/* Check to see if we can free the neighbor */
					if (--np->refcnt == 0) {
						if (np->next != NULLNTAB)
							np->next->prev = np->prev ;
						if (np->prev != NULLNTAB)
							np->prev->next = np->next ;
						else {
							memcpy(neighbor.call,np->call,ALEN) ;
							neighbor.ssid = np->call[ALEN] ;
							nrnbr_tab[nrhash(&neighbor)] = np->next ;
						}
						free(np) ;	/* free the storage */
					}
				}
			}
			if (rp->num_routes == 0) {		/* did we free them all? */
				if (rp->next != NULLNRRTAB)
					rp->next->prev = rp->prev ;
				if (rp->prev != NULLNRRTAB)
					rp->prev->next = rp->next ;
				else
					nrroute_tab[i] = rp->next ;

				free(rp) ;
			}
		}
	}

	start_timer(&obsotimer) ;
}


static int donfadd(), donfdrop(), donfmode() ;

static struct cmds nfcmds[] = {
	"add",	donfadd,	3,
		"netrom nodefilter add <neighbor> <interface>",
		"add failed",
	"drop",	donfdrop,	3,
		"netrom nodefilter drop <neighbor> <interface>",
		"drop failed",
	"mode",	donfmode,	0,	NULLCHAR,	NULLCHAR,
	NULLCHAR,	NULLFP,	0,
		"nodefilter subcommands: add drop mode",
		NULLCHAR
} ;

/* nodefilter command multiplexer */
static
donodefilter(argc,argv)
int argc ;
char *argv[] ;
{
	if (argc < 2) {
		donfdump() ;
		return 0 ;
	}
	return subcmd(nfcmds,argc,argv) ;
}

/* display a list of <callsign,interface> pairs from the filter
 * list.
 */
static
donfdump()
{
	int i, column = 1 ;
	struct nrnf_tab *fp ;
	char buf[16] ;

	for (i = 0 ; i < NRNUMCHAINS ; i++)
		for (fp = nrnf_tab[i] ; fp != NULLNRNFTAB ; fp = fp->next) {
			pax25(buf,&fp->neighbor) ;
			printf("%-7s %-8s  ",
					buf,nrifaces[fp->interface].interface->name) ;
			if (column++ == 4) {
				printf("\n") ;
				column = 1 ;
			}
		}

	if (column != 1)
		printf("\n") ;

	return 0 ;
}

/* add an entry to the filter table */
static
donfadd(argc,argv)
int argc ;
char *argv[] ;
{
	struct ax25_addr neighbor ;
	register int i ;

	/* format callsign */
	if (setcall(&neighbor,argv[1]) == -1) {
		printf("bad neighbor callsign\n") ;
		return -1 ;
	}

	/* find interface */
	for (i = 0 ; i < nr_numiface ; i++)
		if (!strcmp(nrifaces[i].interface->name,argv[2]))
			break ;
	if (i == nr_numiface) {
		printf("Interface \"%s\" not found\n",argv[2]) ;
		return -1 ;
	}

	return nr_nfadd(&neighbor,i) ;
}

/* drop an entry from the filter table */
static
donfdrop(argc,argv)
int argc ;
char *argv[] ;
{
	struct ax25_addr neighbor ;
	register int i ;

	/* format neighbor callsign */
	if (setcall(&neighbor,argv[1]) == -1) {
		printf("bad neighbor callsign\n") ;
		return -1 ;
	}

	/* find interface */
	for (i = 0 ; i < nr_numiface ; i++)
		if (!strcmp(nrifaces[i].interface->name,argv[2]))
			break ;
	if (i == nr_numiface) {
		printf("Interface \"%s\" not found\n",argv[2]) ;
		return -1 ;
	}

	return nr_nfdrop(&neighbor,i) ;
}

/* nodefilter mode subcommand */
static
donfmode(argc,argv)
int argc ;
char *argv[] ;
{
	if (argc < 2) {
		printf("filter mode is ") ;
		switch (nr_nfmode) {
			case NRNF_NOFILTER:
				printf("none\n") ;
				break ;
			case NRNF_ACCEPT:
				printf("accept\n") ;
				break ;
			case NRNF_REJECT:
				printf("reject\n") ;
				break ;
			default:
				printf("some strange, unknown value\n") ;
		}
		return 0 ;
	}
	
	switch (argv[1][0]) {
		case 'n':
		case 'N':
			nr_nfmode = NRNF_NOFILTER ;
			break ;
		case 'a':
		case 'A':
			nr_nfmode = NRNF_ACCEPT ;
			break ;
		case 'r':
		case 'R':
			nr_nfmode = NRNF_REJECT ;
			break ;
		default:
			printf("modes are: none accept reject\n") ;
			return -1 ;
	}

	return 0 ;
}
