
/*
 *
 * APRS Services for TNOS 2.30 - April/May, 2001
 *
 * Designed and written by VE4KLM, Maiko Langelaar
 *
 * For non-commercial use (Ham Radio) only !!!
 *
 */

#include "global.h"

#ifdef	APRSD

#include "ctype.h"
#include "commands.h"

#ifndef MSDOS
#include <time.h>
#endif

#include <sys/stat.h>
#include "mbuf.h"
#include "netuser.h"
#include "ip.h"
#include "files.h"
#include "session.h"
#include "iface.h"
#include "arp.h"
#include "netrom.h"
#include "trace.h"
#include "pktdrvr.h"
#include "udp.h"
#include "aprs.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: aprssrv.c,v 1.2 2001/05/07 10:28:20 brian Exp $";
#endif

extern char Mycall[AXALEN];

static int Hsocket = -1;	/* tcpip connection to aprs.net */
static int Asocket = -1;	/* udp listener */

static int monitor_aprsnet = 0;	/* monitoring flag Added 18Apr2001 */

int aprs_debug = 0;		/* debugging flag */

#define	MAXSRVNAMELEN 20

#define	MAXANETSRV	4

typedef struct {

	char server[MAXSRVNAMELEN];

	int	port;

} ANETSRV;

static ANETSRV	anetsrv[MAXANETSRV];

unsigned long internal_hostid = 0;	/* the only kludge for now */

static char logon_callsign[15];

char aprs_port[15];	/* added 30Apr2001 */

/*
 * APRS Server Commands
 */

static int doaprserver (int argc, char *argv[], void *p);
static int domonitor (int argc, char *argv[], void *p);
static int dosrvcfg (int argc, char *argv[], void *p);
static int doreconnect (int argc, char *argv[], void *p);
static int dointerface (int argc, char *argv[], void *p);
static int dointernal (int argc, char *argv[], void *p);
static int donetlogon (int argc, char *argv[], void *p);
static int domessage (int argc, char *argv[], void *p);
static int dodebug (int argc, char *argv[], void *p);
static int dofwdtorf (int argc, char *argv[], void *p);
static void aprsx (int, void *, void *);
static ANETSRV *next_server (int);

static int message_handler (char *data);


static struct cmds Acmds[] =
{
	{ "debug", dodebug, 0, 0, NULLCHAR },
	{ "fwdtorf", dofwdtorf, 0, 0, NULLCHAR },
	{ "interface", dointerface, 0, 0, NULLCHAR },
	{ "internal", dointernal, 0, 0, NULLCHAR },
	{ "listen", doaprserver, 0, 0, NULLCHAR },
	{ "logon", donetlogon, 0, 0, NULLCHAR },
	{ "message", domessage, 0, 0, NULLCHAR },
	{ "monitor", domonitor, 0, 0, NULLCHAR },
	{ "server", dosrvcfg, 0, 0, NULLCHAR },
	{ "timer", doreconnect, 0, 0, NULLCHAR },
	{ NULLCHAR, NULL, 0, 0, NULLCHAR }
};

/*
 * Pointer (to pointer) where we store a callsign list. This pointer
 * is initialized and the callsign list is loaded by the dofwdtorf ()
 * function at the end of this module.
 */
static char **fwdtorf = (char**)0;

/*
 * Function that compares the passed callsign with the entries in
 * our callsign list. If there is a match, then return 1. If there
 * is no match, then return 0.
 */
static int callsign_allowed (char *callsign)
{
	char **tptr = fwdtorf;

	int len;

	while (*tptr)
	{
		len = strlen (*tptr);	/* only compare using length from list */

		/* if we have a match, then return the value 1, success */
		if (memcmp (*tptr, callsign, len) == 0)
			return 1;

		tptr++;
	}

	return 0;	/* not allowed */
}

/* Send to tnos link level */

extern uint32 Ip_addr;

static void sendrf (char *data, int len)
{
	struct sockaddr_in server_in;

	int s = socket (AF_INET, SOCK_DGRAM, 0);

	server_in.sin_family = AF_INET;
	server_in.sin_port = 16161;
	server_in.sin_addr.s_addr = internal_hostid;

	sendto (s, data, len, 0, (char*)&server_in, sizeof(server_in));
}

/* 26Apr2001, Experimental Message Handler */

static int message_handler (char *data)
{
    char callsign[10], *csp = callsign;

    int retval = 0, ismsg = 0;

    while (*data)
    {
        if (*data == ':')
            ismsg++;
        else
            ismsg = 0;

        if (ismsg > 1)  /* if we get in here, it's a 3rd party message */
        {
            /* skip the data type identifier */
            data++;

            /* extract the callsign of the 3rd party */
            while (*data && *data != ' ' && *data != ':')
                *csp++ = *data++;

            *csp = 0;   /* terminate callsign string */
		/*
		 * 02May2001, VE4KLM, Previously I had hardcoded the VE4 to
		 * be the only callsign allowed (for my testing). Now we use
		 * a callsign list in memory. See dofwdtorf() function.
		 */
			if (!callsign_allowed (callsign))
				break;

            /* skip remaining white space */
            while (*data && *data != ':')
                data++;

            /* skip the message text delimiter */
            data++;

            /* at this point we have the message text */

			if (aprs_debug)
            	log (-1, "msg for %s, %s", callsign, data);

			retval = 1;	/* indicate message to our area */

            break;
        }

        data++;
    }

	return retval;
}

/*
 * As of April 11 2000 Steve Dimse has released this following doHash code
 * to the open source aprs community. This allows me to logon to the APRSD
 * system down in Miami, or whoever else is running an APRSD server.
 */

#define kKey 0x73e2	/* This is the key for the data */

static short doHash (const char *theCall)
{
	short hash, i, len;
	char rootCall[10];	/* need to copy call to remove ssid from parse */
	char *ptr, *p1 = rootCall;
	
	while ((*theCall != '-') && (*theCall != 0)) *p1++ = toupper(*theCall++);
	*p1 = 0;
	
	hash = kKey;	/* Initialize with the key value */
	i = 0;
	len = strlen(rootCall);
	ptr = rootCall;
	while (i<len)		/* Loop through the string two bytes at a time */
	{
		hash ^= (*ptr++)<<8;	/* xor high byte with accumulated hash */
		hash ^= (*ptr++);		/* xor low byte with accumulated hash */
		i += 2;
	}

	return hash & 0x7fff; /* mask off high bit so number is always positive */
}

/*
 * The following function is called from config.c, used when parsing
 * the CONSOLE command line or the AUTOEXEC.NOS commands
 */

int doaprs (int argc, char *argv[], void *p)
{
	return subcmd (Acmds, argc, argv, p);
}

/* Connect to World Wide APRS network */

static int connect_aprs_net (ANETSRV *anetsrvp)
{
	struct sockaddr_in fsocket;
	char logonstr[100];
	int comms_socket;

	fsocket.sin_family = AF_INET;

	log (-1, "Connecting Hostname [%s]", anetsrvp->server);

	if ((fsocket.sin_addr.s_addr = resolve (anetsrvp->server)) == 0)
	{
		log (-1, "Not resolvable");
		return -1;
	}

	fsocket.sin_port = anetsrvp->port;

	comms_socket = socket (AF_INET, SOCK_STREAM, 0);

	if (connect (comms_socket, (char*)&fsocket, SOCKSIZE) != 0)
	{
		log (-1, "Connect failed");
		close_s (comms_socket);
		return -1;
	}

	sprintf (logonstr, "user %s pass %u vers TNOS APRS Server 0.01\n",
		logon_callsign, doHash (logon_callsign));

	if (aprs_debug)
		log (-1, "Logon [%s]", logonstr);

	if (send (comms_socket, logonstr, strlen (logonstr), 0) < 1)
	{
		log (-1, "unable to send logon string");
		close_s (comms_socket);
		return -1;
	}

	log (-1, "we are now connected and logged in");

	return comms_socket;
}

/*
 * 01May2001, VE4KLM, Convert a message from the internet to
 * the proper format for delivery over the local RF network
 */

static int convert_3rd_party (unsigned char *buffer)
{
    unsigned char orgmsg[300], *orgptr = orgmsg;

    char tmp[AXBUF];

    strcpy (orgmsg, buffer);

    *buffer++ = '}';    /* 3rd Party Data Type Identifier */

    /*
     * copy from incoming source path header until the '*' or if
     * there is no '*', then do the entire source path header, up
     * to the ':' delimiter. Do not include the '*' or ':' chars.
     */

    while (*orgptr && *orgptr != '*' && *orgptr != ':')
        *buffer++ = *orgptr++;

    if (!(*orgptr)) /* just to be safe */
    {
        log (-1, "bad packet, discard");
        return -1;
    }

    /*
     * If there is a '*' at this point we need to skip over it and
     * any other digi callsigns, right up to the ':' character. If
     * there is a ':' at this point, then just skip over it.
     */

    if (*orgptr++ == '*')
    {
        while (*orgptr && *orgptr != ':')   /* skip any digis after '*' */
            orgptr++;

        if (!(*orgptr)) /* just to be safe */
        {
            log (-1, "bad packet, discard");
            return -1;
        }

        orgptr++;   /* skip the ':' delimiter */
    }

    /* the 'orgptr' now points at the APRS message itself ! */

    /*
     * Now insert 3rd party network identifier and gateway callsign,
     * don't forget to prepend the comma, and put '*' at end of 3rd
     * party header, oh yes, and the final ':' character.
     */

    buffer += sprintf (buffer, ",TCPIP,%s*:", pax25 (tmp, Mycall));

    /* Finally put in the original APRS message, and we're done */

    strcat (buffer, orgptr);

	return 0;
}

/* Read messages coming from the world wide APRS network */

static void aprs_net (int unused OPTIONAL, void *sp, void *p OPTIONAL)
{
	unsigned char buffer[300];

	int len;

	struct session *asp = (struct session*)sp;

	while (1)
	{
		if ((len = recvline (Hsocket, buffer, sizeof(buffer) - 2)) == -1)
			break;

		if (len == 0)
			continue;

		buffer[len] = 0;

		/* 16Apr2001, VE4KLM, experimental message handler */
		if (message_handler (buffer))
		{
/*
				log (Hsocket, "FRMAPRSN: [%s]", buffer);
*/
            /* 01May2001, VE4KLM, convert message for bc to rf */
            if (convert_3rd_party (buffer) != -1)
			{
/*
					log (Hsocket, "3RDPARTY: [%s]", buffer);
*/
				len = strlen (buffer);	/* MUST recalculate buffer length */

				sendrf (buffer, len + 1);
			}
		}

		if (monitor_aprsnet)
			log (Hsocket, "APRSN: [%.*s]", len, buffer);
	}

	log (Asocket, "APRS_NET died, sockerr [%s]", sockerr (Hsocket));

	asp->proc1 = NULLPROC;

	if (Hsocket != -1)
	{
		close_s (Hsocket);
		Hsocket = -1;
	}
	if (Asocket != -1)
	{
		close_s (Asocket);
		Asocket = -1;
	}
}

/* Process to receive all APRS related messages */

static void aprsx (int unused OPTIONAL, void *u OPTIONAL, void *p OPTIONAL)
{
	struct sockaddr_in sock, from;
	struct session sp;
	struct mbuf *bp;
	ANETSRV *nxtsrv;
	int fromlen;
	int foo;

	server_disconnect_io ();

	/*
	 * Try to connect to the world wide aprs network. Start
	 * by going through the entire server list, see which one
	 * we can connect with first.
	 */

	if ((nxtsrv = next_server (1)) == (ANETSRV*)0)
		return;

	while (1)
	{
		if ((Hsocket = connect_aprs_net (nxtsrv)) == -1)
		{
			if ((nxtsrv = next_server (0)) == (ANETSRV*)0)
				return;
		}
		else break;
	}

	/* setup the UDP listener to listen to 'myself' for APRS ax25 frames */

	Asocket = socket (AF_INET, SOCK_DGRAM, 0);
	sock.sin_family = AF_INET;
	sock.sin_addr.s_addr = INADDR_ANY;
	sock.sin_port = 93;

	if (bind (Asocket, (char *) &sock, sizeof (sock)) == -1)
	{
		log (-1, "APRSD: Can't bind");
		Asocket = -1;
		close_s (Hsocket);
		Hsocket = -1;
		return;
	}

	/* Fork off process to listen to aprs network */
	sp.proc1 = newproc ("aprs_net", 1024, aprs_net, 0, &sp, NULL, 0);

	/* Now loop forever, processing queries */

	log (Asocket, "listening for my own UDP traffic");

	while (1)
	{
		fromlen = sizeof (from);
		if ((foo = recv_mbuf (Asocket, &bp, 0, (char *) &from, &fromlen)) == -1)
			break;	/* Server closing */
		if (foo == 0)
			continue;

		if (aprs_debug)
			log (Asocket, "Got RF, %.*s", bp->cnt, bp->data);

		/* send Packet off to the internet APRS server */
		if (send (Hsocket, bp->data, bp->cnt, 0) < 0)
		{
			log (-1, "unable to send to internet APRS network");
			break;
		}
	}

	log (Asocket, "APRSX died, sockerr [%s]", sockerr (Hsocket));

	killproc (sp.proc1);

	sp.proc1 = NULLPROC;

	if (Hsocket != -1)
	{
		close_s (Hsocket);
		Hsocket = -1;
	}
	if (Asocket != -1)
	{
		close_s (Asocket);
		Asocket = -1;
	}
}

/* Start or Stop  the APRS daemon */

static int doaprserver (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc == 1)
		tprintf ("APRS Server: O%s\n", (Asocket != -1) ? "n" : "ff");
	else {
		if (!stricmp (argv[1], "on")) {
			if (Asocket == -1)
				/* Start domain server task */
				(void) newproc ("APRS Server", 1024, aprsx, 0, NULL, NULL, 0);
		} else {
			close_s (Asocket);
			Asocket = -1;
		}
	}
	return 0;
}

/* Enable or Disable debugging for the APRS Server Module */

static int dodebug (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc == 1)
		tprintf ("Debugging is O%s\n", aprs_debug ? "n" : "ff");
	else
	{
		if (!stricmp (argv[1], "on"))
			aprs_debug = 1;
		else
			aprs_debug = 0;
	}
	return 0;
}

/* Enable or Disable monitoring of APRSNET traffic */

static int domonitor (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc == 1)
		tprintf ("monitoring APRS.NET Traffic is O%s\n",
			monitor_aprsnet ? "n" : "ff");
	else
	{
		if (!stricmp (argv[1], "on"))
			monitor_aprsnet = 1;
		else
			monitor_aprsnet = 0;
	}
	return 0;
}

/* Return the next available APRSNET server */

static ANETSRV *next_server (int beginning)
{
	static int current_server = 0;

	int cnt;

	if (beginning)
		current_server = 0;

	for (cnt = current_server; cnt < MAXANETSRV; cnt++)
	{
		if (anetsrv[cnt].port)
		{
			current_server = cnt + 1;	/* point to the next one */
			return &anetsrv[cnt];
		}
	}

	log (-1, "No more servers to try out");

	return (ANETSRV*)0;
}

/* Add or Delete from list of APRSNET servers */

static int dosrvcfg (int argc, char *argv[], void *p OPTIONAL)
{
	int cnt, port;

	if (argc == 1)
	{
		tprintf ("Usage: aprs server add|delete|list [server port]\n");
	}
	else if (!stricmp (argv[1], "add"))
	{
		for (cnt = 0; cnt < MAXANETSRV; cnt++)
		{
			if (!anetsrv[cnt].port)
			{
				if ((port = atoi (argv[3])) < 1)
				{
					tprintf ("invalid port\n"); 
					break;
				}
				if (strlen (argv[2]) > MAXSRVNAMELEN)
				{
					tprintf ("server name is too long\n"); 
					break;
				}

				strcpy (anetsrv[cnt].server, argv[2]);
				anetsrv[cnt].port = port;
				break;
			}
		}

		if (cnt == MAXANETSRV)
			tprintf ("no more room to add additional servers\n");
	}
	else if (!stricmp (argv[1], "delete"))
	{
		for (cnt = 0; cnt < MAXANETSRV; cnt++)
		{
			if (strcmp (anetsrv[cnt].server, argv[2]) == 0)
			{
				anetsrv[cnt].port = 0;
				break;
			}
		}

		if (cnt == MAXANETSRV)
			tprintf ("could find the requested server for deletion\n");
	}
	else if (!stricmp (argv[1], "list"))
	{
		tprintf ("\nCurrent APRS.NET Servers configured\n");

		for (cnt = 0; cnt < MAXANETSRV; cnt++)
		{
			if (anetsrv[cnt].port)
			{
				tprintf ("%-20.20s %5d\n", anetsrv[cnt].server, anetsrv[cnt].port);
			}
		}
	}

	return 0;
}

static struct timer aprs_srv_t;

static void aprsreconnect (void *t OPTIONAL)
{
	start_detached_timer (&aprs_srv_t);	/* and restart the timer */

	if (Asocket == -1)
		(void) newproc ("APRS Server", 1024, aprsx, 0, NULL, NULL, 0);
}

/*
 * Start and set the APRS.NET reconnect interval so that we
 * stay connected to the network as much as possible. I borrowed
 * this code from smtpcli.c - no point reinventing the wheel.
 */

static int doreconnect (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)
	{
		tprintf ("aprs reconnect timer = %lu/%lu\n",
			read_timer (&aprs_srv_t) / 1000L,
				dur_timer (&aprs_srv_t) / 1000L);

		return 0;
	}

	aprs_srv_t.func = (void (*)(void*)) aprsreconnect; /* call when expired */

	aprs_srv_t.arg = NULL;

	set_timer (&aprs_srv_t, atol (argv[1]) * 1000L); /* set timer duration */

	start_detached_timer (&aprs_srv_t);	/* and fire it up */

	return 0;
}

static int dointernal (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)
	{
		tprintf ("aprs server internal hostid %s\n", inet_ntoa (internal_hostid));
		return 0;
	}

	if ((internal_hostid = resolve (argv[1])) == 0)
	{
		tprintf ("internal hostid not resolvable\n"); 
		return -1;
	}

	log (-1, "internal hostid %s", inet_ntoa (internal_hostid));

	return 0;
}

static int donetlogon (int argc, char *argv[], void *p OPTIONAL)
{
	if (argc < 2)
	{
		tprintf ("aprs logon callsign %s\n", logon_callsign);
		return 0;
	}

	if (strlen (argv[1]) > 10)
	{
		/* I should probably validate this better */
		tprintf ("callsign is too long\n");
		return 0;
	}

	strcpy (logon_callsign, argv[1]);

	log (-1, "aprs logon callsign [%s]", logon_callsign);

	return 0;
}

/* Internal test function that lets me send messages */

static int domessage (int argc, char *argv[], void *p OPTIONAL)
{
    char msgbuf[200];

    if (argc < 4)
    {
        tprintf ("usage : aprs message <destination> <callsign> <message>\n");
        return 0;
    }

    sprintf (msgbuf, "%s>%s::%-9.9s:%s\n",
		logon_callsign, argv[1], argv[2], argv[3]);

	if (aprs_debug)
    	log (-1, "sending msg [%s]", msgbuf);

    if (send (Hsocket, msgbuf, strlen (msgbuf) + 1, 0) < 0)
        tprintf ("unable to send message\n");

    return 0;
}

static int dointerface (int argc, char *argv[], void *p OPTIONAL)
{
    if (argc < 2)
    {
        tprintf ("aprs interface (port) is '%s'\n", aprs_port);
        return 0;
    }

    if (strlen (argv[1]) > 10)
    {
        tprintf ("too long, use shorter name\n");
        return -1;
    }

    strcpy (aprs_port, argv[1]);

    log (-1, "aprs interface (port) is '%s'\n", aprs_port);

    return 0;
}

/*
 * Load into memory a list of callsigns and/or callsign patterns that we
 * will allow to be forwarded from the internet to the local RF port.
 *
 * This function uses a somewhat elegant method to create a DYNAMICALLY
 * allocated list of strings in memory. Nothing is presumed, memory is
 * allocated as needed to store a simple list of strings. Using pointers
 * like this is a good way to conserve memory.
 *
 * May 2, 2001, VE4KLM, Pointers to pointers are the way to go :-)
 *
 * Use the function, callsign_allowed (char*) to verify whether or not
 * a particular callsign is in the callsign list.
 *
 */

static int dofwdtorf (int argc, char *argv[], void *p OPTIONAL)
{
	char **tptr;

	int cnt;

    if (argc < 2)
    {
		/* show it to us if already loaded into memory */
		if (fwdtorf)
		{
			tptr = fwdtorf;

			tprintf ("These callsigns are allowed to go out the RF port\n");

			while (*tptr)
			{
				tprintf ("%s\n", *tptr);

				tptr++;
			}
		}
		/* otherwise display usage */
		else
		{
			tprintf ("aprs fwdtorf [callsigns &| patterns]\n");
			return 0;
		}
	}

	/*
	 * If the callsign list is already allocated, then tell the user
	 * about it. I should really change this so that the user can delete
	 * the list, then recreate it again. This will do for now. It's only
	 * a prototype at this stage, still have some ideas floating around.
	 */
	if (fwdtorf)
	{
		tprintf ("Sorry, you have already loaded a callsign list\n");
		return 0;
	}
	/* Otherwise, if the callsign is not present, then build it */
	else
	{
		if ((fwdtorf = (char**)malloc (sizeof(char*) * (argc+2))) == (char**)0)
		{
			tprintf ("not enough memory for callsign list\n");
			return -1;
		}

		/* now load the arguments into the memory list */
		tptr = fwdtorf;
		for (cnt = 0; cnt < (argc - 1); cnt++)
			*tptr++ = strdup (argv[cnt+1]);

		*tptr = (char*)0;	/* terminate the list with NULL */
	}

	return 0;
}

int aprsd_frame (udp)
	struct udp *udp;
{
    return (udp->dest == 16161);
}

void aprsd_torf (iface, bp, ip)
      struct iface *iface;
      struct mbuf *bp;
      struct ip *ip OPTIONAL;
{
	struct mbuf *hbp, *data;
	char recipient[AXALEN];
	struct ax_route *axr;
	char dest[AXALEN];
	char const *idest;
	struct ax25 addr;
	char *rptr, *ptr;
	char rtmp[15];

	/*
	 * First thing we have to do is make sure we have the correct
	 * interface to send this packet out on. Just re-use the iface
	 * pointer passed to this function, since we don't use it.
	 */
	for (iface = Ifaces; iface != NULLIF; iface = iface->next)
		if (strcmp (iface->name, aprs_port) == 0)
			break;

	if (iface == NULLIF)
	{
        log (-1, "There is no APRS interface (port) '%s'", aprs_port);
		return;
	}	

	/*
	 * Extract the recipient from the APRS text message, because
	 * we need to setup routing based on the recipient. I know, I
	 * know, that goes against the concepts of WIDE, RELAY, etc,
	 * but I designed this TNOS implementation for a bit more
	 * flexibility. Why should people 3 netrom nodes away not
	 * be able to participate in the APRS network ???
	 */

	ptr = bp->data;

	/* look for message datatype id */
	while (*ptr != ':')
		ptr++;

	ptr++;	/* skip it, check next one */

	if (*ptr != ':')
		return;

	ptr++;	/* skip it */

	/* extract the message recipient */
	rptr = rtmp;
	while (*ptr && *ptr != ' ' && *ptr != ':')
		*rptr++ = *ptr++;

	/* terminate the recipient string */
	*rptr = 0;

	if (setcall (recipient, rtmp) == -1)
	{
		log (-1, "setcall failed [%s]", rtmp);
		return;
	}

	if (setcall (dest, "APZ000") == -1)
	{
		log (-1, "setcall failed [APZ000]");
		return;
	}

	/*
	 * If there's a digipeater route, get it. Please note, I originally
	 * wanted to use the TNOS 'axsend' function here to save code space,
	 * but the digipeater route needs to be setup based on the dest call
	 * of the message receipient, NOT the APRS dest call of APZ000. So I
	 * have to essentially write my own version of axsend to do this.
	 *
	axsend (iface, dest, Mycall, LAPB_COMMAND, UI, data);
	 */

	axr = ax_lookup (Mycall, recipient, iface, AX_NONSETUP);

	memcpy (addr.dest, dest, AXALEN);
	memcpy (addr.source, Mycall, AXALEN);

	addr.cmdrsp = LAPB_COMMAND;

    if (axr != NULLAXR)
	{
        memcpy (addr.digis, axr->digis, (size_t)axr->ndigis * AXALEN);
        addr.ndigis = axr->ndigis;
        idest = addr.digis[0];
    }
	else
	{
        addr.ndigis = 0;
        idest = dest;
    }

    addr.nextdigi = 0;

	/* allocate memory for new message (include 2 bytes for CTL & PID) */
    if ((hbp = alloc_mbuf ((int16)(bp->cnt + 2))) == NULLBUF)
        return;

	/* set control and pid fields */
	hbp->data[0] = uchar (UI);
	hbp->data[1] = uchar (PID_NO_L3);

	/* copy the APRS data to buffer */
    hbp->cnt = (int16)(bp->cnt + 2);
    memcpy (hbp->data + 2, bp->data, bp->cnt);

	/* now put in the ax25 header */
    if ((data = htonax25 (&addr, hbp)) == NULLBUF)
	{
        free_p (hbp);
        return;
    }

	if (aprs_debug)
	{
    	log (-1, "sending recipient [%s] port [%s] %d digis",
			rtmp, aprs_port, addr.ndigis);
	}

	/* send the packet out !!! */
	(*iface->raw)(iface, data);

	return;
}

#endif
