[svn-commits] jdixon: branch jdixon/chan_usbradio-1.4 r140411 - /team/jdixon/chan_usbradio-...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Aug 28 20:45:02 CDT 2008


Author: jdixon
Date: Thu Aug 28 20:45:01 2008
New Revision: 140411

URL: http://svn.digium.com/view/asterisk?view=rev&rev=140411
Log:
Added chan_echolink to main SVN

Added:
    team/jdixon/chan_usbradio-1.4/channels/chan_echolink.c   (with props)

Added: team/jdixon/chan_usbradio-1.4/channels/chan_echolink.c
URL: http://svn.digium.com/view/asterisk/team/jdixon/chan_usbradio-1.4/channels/chan_echolink.c?view=auto&rev=140411
==============================================================================
--- team/jdixon/chan_usbradio-1.4/channels/chan_echolink.c (added)
+++ team/jdixon/chan_usbradio-1.4/channels/chan_echolink.c Thu Aug 28 20:45:01 2008
@@ -1,0 +1,2404 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Copyright (C) 2008, Scott Lawson/KI4LKF
+ * ScottLawson/KI4LKF <ham44865 at yahoo.com>
+ * Jim Dixon, WB6NIL <jim at lambdatel.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Echolink channel driver for Asterisk
+ * 
+ * \author Scott Lawson/KI4LKF <ham44865 at yahoo.com>
+ * \author Jim Dixon, WB6NIL <jim at lambdatel.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+	<depend>zlib</depend>
+ ***/
+
+/* Version 0.14, 08/27/2008
+Echolink channel driver for Asterisk/app_rpt.
+A lot more has to be added,
+Here is what comes to mind first:
+
+---> does not send its station info.
+---> does not process chat text.
+---> recognizes a few remote text commands.
+---> no busy, deaf or mute.
+---> no capacity limits.
+---> no banned or privare station list.
+---> no admin list, only local 127.0.0.1 access.
+---> no welcome text message.
+---> no login or connect timeouts.
+---> no max TX time limit.
+---> no activity reporting.
+---> no event notififications.
+---> no stats.
+---> no callsign prefix restrictions.
+---> no announcements on connects/disconnects.
+---> no loop detection.
+---> allows "doubles"
+
+Otherwise it works as an Echolink conference and it
+will accept multiple station connects.
+It also detects disconnect requests and 
+removes dead stations on heartbeat timeouts.
+Default ports are 5198,5199.
+
+Remote text commands thru netcat:
+o.conip <IPaddress>    (request a connect)
+o.dconip <IPaddress>   (request a disconnect)
+o.rec                  (turn on/off recording)
+
+It is invoked as echolink/identifier (like el0 for example)
+Example: 
+Under a node stanza in rpt.conf, 
+rxchannel=echolink/el0
+
+The el0 or whatever you put there must match the stanza in the
+echolink.conf file.
+
+If the linux box is protected by a NAT router,
+leave the IP address as 0.0.0.0,
+do not use 127.0.0.1
+
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <search.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <zlib.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/translate.h"
+#include "asterisk/astdb.h"
+
+#define	MAX_RXKEY_TIME 4
+
+/* 50 * 10 * 20ms iax2 = 10,000ms = 10 seconds heartbeat */
+#define	KEEPALIVE_TIME 50 * 10
+#define	BLOCKING_FACTOR 4
+#define	GSM_FRAME_SIZE 33
+#define QUEUE_OVERLOAD_THRESHOLD_AST 25
+#define QUEUE_OVERLOAD_THRESHOLD_EL 20
+
+#define EL_IP_SIZE 15
+#define EL_CALL_SIZE 15
+#define EL_NAME_SIZE 31
+#define EL_PWD_SIZE 15
+#define EL_EMAIL_SIZE 31
+#define EL_QTH_SIZE 31
+#define EL_MAX_SERVERS 3
+#define EL_SERVERNAME_SIZE 63
+#define	EL_MAX_INSTANCES 100
+
+/* 
+   If you want to compile/link this code
+   on "BIG-ENDIAN" platforms, then
+   use this: #define RTP_BIG_ENDIAN
+   Have only tested this code on "little-endian"
+   platforms running Linux.
+*/
+
+static const char tdesc[] = "Echolink channel driver by KI4LKF";
+static int prefformat = AST_FORMAT_GSM;
+static char type[] = "echolink";
+static char db_active = 'a';
+static char db_loading = 0;
+static char snapshot_id[50] = {'0',0};
+static int el_net_get_index = 0;
+static int el_net_get_nread = 0;
+
+
+/* Echolink audio packet heafer */
+struct gsmVoice_t {
+#ifdef RTP_BIG_ENDIAN
+  uint8_t version:2;
+  uint8_t pad:1;
+  uint8_t ext:1;
+  uint8_t csrc:4;
+  uint8_t marker:1;
+  uint8_t payt:7;
+#else
+  uint8_t csrc:4;
+  uint8_t ext:1;
+  uint8_t pad:1;
+  uint8_t version:2;
+  uint8_t payt:7;
+  uint8_t marker:1;
+#endif
+  uint16_t seqnum;
+  uint32_t time;
+  uint32_t ssrc;
+  unsigned char data[BLOCKING_FACTOR * GSM_FRAME_SIZE];
+};
+
+struct el_instance;
+struct el_pvt;
+
+/* Echolink node details */
+/* Also each node in binary tree in memory */
+struct el_node {
+   char ip[EL_IP_SIZE + 1]; 
+   char call[EL_CALL_SIZE + 1];
+   char name[EL_NAME_SIZE + 1];
+   unsigned int nodenum; /* not used yet */
+   short countdown;
+   uint16_t seqnum;
+   struct el_instance *instp;
+   struct el_pvt *p;
+   struct ast_channel *chan;
+};
+
+struct el_instance
+{
+	ast_mutex_t lock;
+	char name[EL_NAME_SIZE + 1];
+	char mycall[EL_CALL_SIZE + 1];
+	char myname[EL_NAME_SIZE + 1];
+	char mypwd[EL_PWD_SIZE + 1];
+	char myemail[EL_EMAIL_SIZE + 1];
+	char myqth[EL_QTH_SIZE + 1];
+	char elservers[EL_MAX_SERVERS][EL_SERVERNAME_SIZE + 1];
+	char ipaddr[EL_IP_SIZE + 1];
+	char port[EL_IP_SIZE + 1];
+	char astnode[EL_NAME_SIZE + 1];
+	char context[EL_NAME_SIZE + 1];
+	int maxstns;
+	/* missed 10 heartbeats, you're out */
+	short rtcptimeout;
+	unsigned int mynode;
+	char fdr_file[FILENAME_MAX];
+	int audio_sock;
+	int ctrl_sock;
+	uint16_t audio_port;
+	uint16_t ctrl_port;
+	int fdr;
+	unsigned long seqno;
+	int confmode;
+	struct el_pvt *confp;
+	struct gsmVoice_t audio_all_but_one;
+	struct gsmVoice_t audio_all;
+	struct el_node el_node_test;
+
+} ;
+
+struct el_rxqast {
+	struct el_rxqast *qe_forw;
+	struct el_rxqast *qe_back;
+	char buf[GSM_FRAME_SIZE];
+};
+
+struct el_rxqel {
+        struct el_rxqel *qe_forw;
+        struct el_rxqel *qe_back;
+        char buf[BLOCKING_FACTOR * GSM_FRAME_SIZE];
+        char fromip[EL_IP_SIZE + 1];
+};
+
+struct el_pvt {
+	struct ast_channel *owner;
+	struct el_instance *instp;
+	char app[16];		
+	char stream[80];
+	char ip[EL_IP_SIZE + 1]; 
+	char txkey;
+	int rxkey;
+	int keepalive;
+	struct ast_frame fr;	
+	int txindex;
+	struct el_rxqast rxqast;
+        struct el_rxqel rxqel;
+	char firstsent;
+	char firstheard;
+	struct ast_dsp *dsp;
+	struct ast_module_user *u;
+	struct ast_trans_pvt *xpath;
+	unsigned int nodenum;
+};
+
+struct rtcp_sdes_request_item {
+    unsigned char r_item;
+    char *r_text;
+};
+
+struct rtcp_sdes_request {
+   int nitems;
+   unsigned char ssrc[4];
+   struct rtcp_sdes_request_item item[10];
+};
+
+struct rtcp_common_t {
+#ifdef RTP_BIG_ENDIAN
+  uint8_t version:2;
+  uint8_t p:1;
+  uint8_t count:5;
+#else
+  uint8_t count:5;
+  uint8_t p:1;
+  uint8_t version:2;
+#endif
+  uint8_t pt:8;
+  uint16_t length;
+};
+
+struct rtcp_sdes_item_t {
+  uint8_t type;
+  uint8_t length;
+  char data[1];
+};
+
+struct rtcp_t {
+  struct rtcp_common_t common;
+  union {
+    struct {
+      uint32_t src[1];
+    } bye;
+
+    struct rtcp_sdes_t {
+      uint32_t src;
+      struct rtcp_sdes_item_t item[1];
+    } sdes;
+  } r;
+};
+
+int debug = 0;
+struct el_instance *instances[EL_MAX_INSTANCES];
+int ninstances = 0;
+
+/* binary search tree in memory, root node */
+static void *el_node_list = NULL;
+
+/* Echolink registration thread */
+static  pthread_t el_register_thread;
+static  pthread_t el_reader_thread;
+static  pthread_t el_directory_thread;
+static int run_forever = 1;
+
+static char *config = "echolink.conf";
+
+#ifdef OLD_ASTERISK
+#define ast_free free
+#define ast_malloc malloc
+#endif
+
+static struct ast_channel *el_request(const char *type, int format, void *data, int *cause);
+static int el_call(struct ast_channel *ast, char *dest, int timeout);
+static int el_hangup(struct ast_channel *ast);
+static struct ast_frame *el_xread(struct ast_channel *ast);
+static int el_xwrite(struct ast_channel *ast, struct ast_frame *frame);
+static int el_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen);
+static int el_digit_begin(struct ast_channel *c, char digit);
+static int el_digit_end(struct ast_channel *c, char digit, unsigned int duratiion);
+static int el_text(struct ast_channel *c, const char *text);
+
+static int rtcp_make_sdes(unsigned char *pkt, int pktLen, char *call, char *name);
+static int rtcp_make_bye(unsigned char *p, char *reason);
+static void parse_sdes(unsigned char *packet, struct rtcp_sdes_request *r);
+static void copy_sdes_item(char *source, char *dest, int destlen);
+static int is_rtcp_bye(unsigned char *p, int len);
+static int is_rtcp_sdes(unsigned char *p, int len);
+ /* remove binary tree functions if Asterisk has similar functionality */
+static int compare_ip(const void *pa, const void *pb);
+static void send_audio_all_but_one(const void *nodep, const VISIT which, const int depth);
+static void send_audio_all(const void *nodep, const VISIT which, const int depth);
+static void send_heartbeat(const void *nodep, const VISIT which, const int depth);
+static void print_users(const void *nodep, const VISIT which, const int depth);
+static void free_node(void *nodep);
+static void process_cmd(char *buf,char *fromip,struct el_instance *instp);
+static int find_delete(struct el_node *key);
+static int sendcmd(char *server,struct el_instance *instp);
+static int do_new_call(struct el_instance *instp, struct el_pvt *p, char *call, char *name);
+
+/* remove writen if Asterisk has similar functionality */
+static int writen(int fd, char *ptr, int nbytes);
+
+static const struct ast_channel_tech el_tech = {
+	.type = type,
+	.description = tdesc,
+	.capabilities = AST_FORMAT_GSM,
+	.requester = el_request,
+	.call = el_call,
+	.hangup = el_hangup,
+	.read = el_xread,
+	.write = el_xwrite,
+	.indicate = el_indicate,
+	.send_text = el_text,
+	.send_digit_begin = el_digit_begin,
+	.send_digit_end = el_digit_end,
+};
+
+static int rtcp_make_sdes(unsigned char *pkt, int pktLen, char *call, char *name)
+{
+    unsigned char zp[1500];
+    unsigned char *p = zp;
+    struct rtcp_t *rp;
+    unsigned char *ap;
+    char line[EL_CALL_SIZE + EL_NAME_SIZE + 1];
+    int l, hl, pl;
+
+    hl = 0;
+    *p++ = 3 << 6;
+    *p++ = 201;
+    *p++ = 0;
+    *p++ = 1;
+    *((long *) p) = htonl(0);
+    p += 4;
+    hl = 8;
+
+    rp = (struct rtcp_t *) p;
+    *((short *) p) = htons((3 << 14) | 202 | (1 << 8));
+    rp->r.sdes.src = htonl(0);
+    ap = (unsigned char *) rp->r.sdes.item;
+
+    strncpy(line,"CALLSIGN",EL_CALL_SIZE + EL_NAME_SIZE);
+    *ap++ = 1;
+    *ap++ = l = strlen(line);
+    memcpy(ap,line,l);
+    ap += l;
+
+    snprintf(line,EL_CALL_SIZE + EL_NAME_SIZE,"%s %s",call,name);
+    *ap++ = 2;
+    *ap++ = l = strlen(line);
+    memcpy(ap,line,l); 
+    ap += l;
+
+    /* enable DTMF keypad */
+    *ap++ = 8;
+    *ap++ = 3;
+    *ap++ = 1;
+    *ap++ = 'D';
+    *ap++ = '1';
+
+    *ap++ = 0;
+    *ap++ = 0;
+    l = ap - p;
+
+    rp->common.length = htons(((l + 3) / 4) - 1);
+    l = hl + ((ntohs(rp->common.length) + 1) * 4);
+
+    pl = (l & 4) ? l : l + 4;
+
+    if (pl > l) {
+       int pad = pl - l;
+       memset(zp + l, '\0', pad);  
+       zp[pl - 1] = pad;
+       p[0] |= 0x20;
+       rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4));
+       l = pl;
+    }
+
+    if (l > pktLen)
+       return 0;
+    memcpy(pkt,zp,l);
+    return l;
+}
+
+static int rtcp_make_bye(unsigned char *p, char *reason)
+{
+    struct rtcp_t *rp;
+    unsigned char *ap, *zp;
+    int l, hl, pl;
+
+    zp = p;
+    hl = 0;
+
+    *p++ = 3 << 6;
+    *p++ = 201;
+    *p++ = 0;
+    *p++ = 1;
+    *((long *) p) = htonl(0);
+    p += 4;
+    hl = 8;
+
+    rp = (struct rtcp_t *)p;
+    *((short *) p) = htons((3 << 14) | 203 | (1 << 8));
+    rp->r.bye.src[0] = htonl(0);
+    ap = (unsigned char *) rp->r.sdes.item;
+    l = 0; 
+    if (reason != NULL) {
+        l = strlen(reason);
+        if (l > 0) {
+            *ap++ = l;
+             memcpy(ap,reason,l);
+            ap += l;
+        }
+    }
+    while ((ap - p) & 3)
+        *ap++ = 0;
+    l = ap - p;
+    rp->common.length = htons((l / 4) - 1);
+    l = hl + ((ntohs(rp->common.length) + 1) * 4);
+
+    pl = (l & 4) ? l : l + 4;
+    if (pl > l) {
+       int pad = pl - l;
+       memset(zp + l, '\0', pad);
+       zp[pl - 1] = pad;
+       p[0] |= 0x20;
+       rp->common.length = htons(ntohs(rp->common.length) + ((pad) / 4));
+       l = pl;    
+    }
+    return l;
+}
+
+static void parse_sdes(unsigned char *packet, struct rtcp_sdes_request *r)
+{
+    int i;
+    unsigned char *p = packet;
+
+    for (i = 0; i < r->nitems; i++)
+        r->item[i].r_text = NULL;
+
+    while ((p[0] >> 6 & 3) == 3 || (p[0] >> 6 & 3) == 1) {
+        if ((p[1] == 202) && ((p[0] & 0x1F) > 0)) {
+            unsigned char *cp = p + 8,
+                          *lp = cp + (ntohs(*((short *) (p + 2))) + 1) * 4;
+            memcpy(r->ssrc, p + 4, 4); 
+            while (cp < lp) {
+                unsigned char itype = *cp;
+                if (itype == 0)
+                    break;
+
+                for (i = 0; i < r->nitems; i++) {
+                    if (r->item[i].r_item == itype &&
+                        r->item[i].r_text == NULL) {
+                        r->item[i].r_text = (char *) cp;
+                        break;
+                    }
+                }
+                cp += cp[1] + 2;
+            }
+            break;
+        }
+        p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+    }
+    return;
+}
+
+static void copy_sdes_item(char *source, char *dest, int destlen)
+{
+    int len = source[1] & 0xFF;
+    if (len > destlen)
+       len = destlen;
+    memcpy(dest,source + 2, len);
+    dest[len] = 0;
+    return;
+}
+
+static int is_rtcp_bye(unsigned char *p, int len)
+{
+    unsigned char *end;
+    int sawbye = 0;
+
+
+    if ((((p[0] >> 6) & 3) != 3 && ((p[0] >> 6) & 3) != 1) ||
+        ((p[0] & 0x20) != 0) ||
+        ((p[1] != 200) && (p[1] != 201)))
+      return 0;
+
+    end = p + len;
+
+    do {
+        if (p[1] == 203)
+            sawbye = 1;
+
+        p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+    } while (p < end && (((p[0] >> 6) & 3) == 3));
+
+    return sawbye;
+}
+
+static int is_rtcp_sdes(unsigned char *p, int len)
+{
+    unsigned char *end;
+    int sawsdes = 0;
+
+    if ((((p[0] >> 6) & 3) != 3 && ((p[0] >> 6) & 3) != 1) ||
+        ((p[0] & 0x20) != 0) ||
+        ((p[1] != 200) && (p[1] != 201)))
+      return 0;
+
+    end = p + len;
+    do {
+       if (p[1] == 202)
+          sawsdes = 1;
+
+        p += (ntohs(*((short *) (p + 2))) + 1) * 4;
+    } while (p < end && (((p[0] >> 6) & 3) == 3));
+
+    return sawsdes;
+}
+
+static int el_call(struct ast_channel *ast, char *dest, int timeout)
+{
+	struct el_pvt *p = ast->tech_pvt;
+	struct el_instance *instp = p->instp;
+	char buf[100];
+
+
+	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+		ast_log(LOG_WARNING, "el_call called on %s, neither down nor reserved\n", ast->name);
+		return -1;
+	}
+	/* When we call, it just works, really, there's no destination...  Just
+	   ring the phone and wait for someone to answer */
+//	if (option_debug)
+		ast_log(LOG_NOTICE, "Calling %s on %s\n", dest, ast->name);
+	if (*dest)  /* if number specified */
+	{
+		char *str,*cp;
+
+		str = strdup(dest);
+		cp = strchr(str,'/');
+		if (cp) *cp++ = 0; else cp = str;
+		snprintf(buf,sizeof(buf) - 1,"o.conip %s",cp);
+		free(str);
+		ast_mutex_lock(&instp->lock);
+		strcpy(instp->el_node_test.ip,cp);
+		do_new_call(instp,p,"OUTBOUND","OUTBOUND");
+		process_cmd(buf,"127.0.0.1",instp);
+		ast_mutex_unlock(&instp->lock);
+	}
+	ast_setstate(ast,AST_STATE_RINGING);
+	return 0;
+}
+
+static void el_destroy(struct el_pvt *p)
+{
+	if (p->dsp) ast_dsp_free(p->dsp);
+	if (p->xpath) ast_translator_free_path(p->xpath);
+	ast_module_user_remove(p->u);
+	ast_free(p);
+}
+
+static struct el_pvt *el_alloc(void *data)
+{
+	struct el_pvt *p;
+	int n;
+	/* int flags = 0; */
+	char stream[256];
+        
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(idstr);
+	);
+
+	if (ast_strlen_zero(data)) return NULL;
+
+	AST_STANDARD_APP_ARGS(args,data);
+
+	for(n = 0; n < ninstances; n++)
+	{
+		if (!strcmp(instances[n]->name,args.idstr)) break;
+	}
+	if (n >= ninstances)
+	{
+		ast_log(LOG_NOTICE,"Cannot find echolink channel %s\n",args.idstr);
+		return NULL;
+	}
+
+	p = ast_malloc(sizeof(struct el_pvt));
+	if (p) {
+		memset(p, 0, sizeof(struct el_pvt));
+		
+		sprintf(stream,"%s-%lu",args.idstr,instances[n]->seqno++);
+		strcpy(p->stream,stream);
+		p->rxqast.qe_forw = &p->rxqast;
+		p->rxqast.qe_back = &p->rxqast;
+
+                p->rxqel.qe_forw = &p->rxqel;
+                p->rxqel.qe_back = &p->rxqel;
+                
+		p->keepalive = KEEPALIVE_TIME;
+		p->instp = instances[n];
+		p->instp->confp = p;  /* save for conference mode */
+		if (!p->instp->confmode)
+		{
+			p->dsp = ast_dsp_new();
+			if (!p->dsp)
+			{
+				ast_log(LOG_ERROR,"Cannot get DSP!!\n");
+				return NULL;
+			}
+#ifdef  NEW_ASTERISK
+	                ast_dsp_set_features(p->dsp,DSP_FEATURE_DIGIT_DETECT);
+	                ast_dsp_set_digitmode(p->dsp,DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF);
+#else
+	                ast_dsp_set_features(p->dsp,DSP_FEATURE_DTMF_DETECT);
+	                ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF);
+#endif
+			p->xpath = ast_translator_build_path(AST_FORMAT_SLINEAR,AST_FORMAT_GSM);
+			if (!p->xpath)
+			{
+				ast_log(LOG_ERROR,"Cannot get translator!!\n");
+				return NULL;
+			}
+		}
+	}
+	return p;
+}
+
+static int el_hangup(struct ast_channel *ast)
+{
+	struct el_pvt *p = ast->tech_pvt;
+	struct el_instance *instp = p->instp;
+	int i,n;
+        unsigned char bye[50];
+	struct sockaddr_in sin;
+
+	if (!instp->confmode)
+	{
+		if (debug) ast_log(LOG_DEBUG,"Sent bye to IP address %s\n",p->ip);
+		ast_mutex_lock(&instp->lock);
+		strcpy(instp->el_node_test.ip,p->ip);
+		find_delete(&instp->el_node_test);
+		ast_mutex_unlock(&instp->lock);
+		n = rtcp_make_bye(bye,"disconnected");
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = inet_addr(p->ip);
+		sin.sin_port = htons(instp->ctrl_port);
+		for (i = 0; i < 20; i++)
+		{
+			sendto(instp->ctrl_sock, bye, n,
+				0,(struct sockaddr *)&sin,sizeof(sin));
+		}
+	}		
+	if (option_debug)
+		ast_log(LOG_DEBUG, "el_hangup(%s)\n", ast->name);
+	if (!ast->tech_pvt) {
+		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+		return 0;
+	}
+	el_destroy(p);
+	ast->tech_pvt = NULL;
+	ast_setstate(ast, AST_STATE_DOWN);
+	return 0;
+}
+
+static int el_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen)
+{
+	struct el_pvt *p = ast->tech_pvt;
+
+	switch (cond) {
+		case AST_CONTROL_RADIO_KEY:
+			p->txkey = 1;
+			break;
+		case AST_CONTROL_RADIO_UNKEY:
+			p->txkey = 0;
+			break;
+		case AST_CONTROL_HANGUP:
+			return -1;
+		default:
+			return 0;
+	}
+
+	return 0;
+}
+
+static int el_digit_begin(struct ast_channel *ast, char digit)
+{
+	return -1;
+}
+
+static int el_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+	return -1;
+}
+
+static int el_text(struct ast_channel *ast, const char *text)
+{
+	struct el_pvt *p = ast->tech_pvt;
+	char *cmd = NULL,*arg1 = NULL,*arg2 = NULL;
+	char *arg3 = NULL,delim = ' ',*saveptr;
+	char buf[200],*ptr,str[200],*arg4 = NULL;
+
+	strncpy(buf,text,sizeof(buf) - 1);
+	ptr = strchr(buf, (int)'\r'); 
+	if (ptr) *ptr = '\0';
+	ptr = strchr(buf, (int)'\n');    
+	if (ptr) *ptr = '\0';
+	cmd = strtok_r(buf, &delim, &saveptr);
+	if (!cmd)
+	{
+		return 0;
+	}
+
+	arg1 = strtok_r(NULL, &delim, &saveptr);
+	arg2 = strtok_r(NULL, &delim, &saveptr);
+	arg3 = strtok_r(NULL, &delim, &saveptr);
+	arg4 = strtok_r(NULL, &delim, &saveptr);
+
+	if (!strcasecmp(cmd,"D"))
+	{
+		sprintf(str,"3%06u",p->nodenum);
+		/* if not for this one, we cant go any farther */
+		if (strcmp(arg1,str)) return 0;
+		ast_senddigit(ast,*arg4);
+		return 0;
+	}
+	return 0;
+}
+
+static int compare_ip(const void *pa, const void *pb)
+{
+   return strncmp(((struct el_node *)pa)->ip,((struct el_node *)pb)->ip,EL_IP_SIZE); 
+}
+
+/* Echolink ---> Echolink */
+void send_audio_all_but_one(const void *nodep, const VISIT which, const int depth) 
+{
+   struct sockaddr_in sin;
+   struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+   if ((which == leaf) || (which == postorder)) {
+      if (strncmp((*(struct el_node **)nodep)->ip, instp->el_node_test.ip,EL_IP_SIZE) != 0) {
+         sin.sin_family = AF_INET;
+         sin.sin_port = htons(instp->audio_port);
+         sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+
+         instp->audio_all_but_one.version = 3;
+         instp->audio_all_but_one.pad = 0;
+         instp->audio_all_but_one.ext = 0;
+         instp->audio_all_but_one.csrc = 0;
+         instp->audio_all_but_one.marker = 0;
+         instp->audio_all_but_one.payt = 3;
+         instp->audio_all_but_one.seqnum = htons((*(struct el_node **)nodep)->seqnum++); 
+         instp->audio_all_but_one.time = htonl(0);
+         instp->audio_all_but_one.ssrc = instp->mynode;
+
+         /*
+         ast_log(LOG_NOTICE, "sending to %s(%s)\n", 
+                  (*(struct el_node **)nodep)->call, (*(struct el_node **)nodep)->ip);
+         */
+        
+         sendto(instp->audio_sock, (char *)&instp->audio_all_but_one, sizeof(instp->audio_all_but_one),
+                0,(struct sockaddr *)&sin,sizeof(sin));
+      }
+   }
+}
+
+static void send_audio_only_one(const void *nodep, const VISIT which, const int depth) 
+{
+   struct sockaddr_in sin;
+   struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+   if ((which == leaf) || (which == postorder)) {
+      if (strncmp((*(struct el_node **)nodep)->ip, instp->el_node_test.ip,EL_IP_SIZE) == 0) {
+         sin.sin_family = AF_INET;
+         sin.sin_port = htons(instp->audio_port);
+         sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+
+      instp->audio_all.version = 3;
+      instp->audio_all.pad = 0;
+      instp->audio_all.ext = 0;
+      instp->audio_all.csrc = 0;
+      instp->audio_all.marker = 0;
+      instp->audio_all.payt = 3;
+      instp->audio_all.seqnum = htons((*(struct el_node **)nodep)->seqnum++);
+      instp->audio_all.time = htonl(0);
+      instp->audio_all.ssrc = instp->mynode;
+
+      /*
+      ast_log(LOG_NOTICE, "sending to %s(%s)\n", 
+                  (*(struct el_node **)nodep)->call, (*(struct el_node **)nodep)->ip);
+      */
+      sendto(instp->audio_sock, (char *)&instp->audio_all, sizeof(instp->audio_all), 
+             0,(struct sockaddr *)&sin,sizeof(sin));
+      }
+   }
+}
+
+/* Asterisk ---> Echolink */
+void send_audio_all(const void *nodep, const VISIT which, const int depth)
+{
+   struct sockaddr_in sin;
+   struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+   if ((which == leaf) || (which == postorder)) {
+      sin.sin_family = AF_INET;
+      sin.sin_port = htons(instp->audio_port);
+      sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+
+      instp->audio_all.version = 3;
+      instp->audio_all.pad = 0;
+      instp->audio_all.ext = 0;
+      instp->audio_all.csrc = 0;
+      instp->audio_all.marker = 0;
+      instp->audio_all.payt = 3;
+      instp->audio_all.seqnum = htons((*(struct el_node **)nodep)->seqnum++);
+      instp->audio_all.time = htonl(0);
+      instp->audio_all.ssrc = instp->mynode;
+
+      /*
+      ast_log(LOG_NOTICE, "sending to %s(%s)\n", 
+                  (*(struct el_node **)nodep)->call, (*(struct el_node **)nodep)->ip);
+      */
+      sendto(instp->audio_sock, (char *)&instp->audio_all, sizeof(instp->audio_all), 
+             0,(struct sockaddr *)&sin,sizeof(sin));
+   }
+}
+
+static void print_users(const void *nodep, const VISIT which, const int depth)
+{
+   if ((which == leaf) || (which == postorder)) {
+      ast_log(LOG_NOTICE,"call=%s,ip=%s,name=%s\n",
+             (*(struct el_node **)nodep)->call,
+             (*(struct el_node **)nodep)->ip,
+             (*(struct el_node **)nodep)->name);
+   }
+}
+
+static void send_heartbeat(const void *nodep, const VISIT which, const int depth)
+{
+   struct sockaddr_in sin;
+   unsigned char  sdes_packet[256];
+   int sdes_length;
+   struct el_instance *instp = (*(struct el_node **)nodep)->instp;
+
+   if ((which == leaf) || (which == postorder)) {
+
+      if ((*(struct el_node **)nodep)->countdown >= 0)
+         (*(struct el_node **)nodep)->countdown --;
+  
+      if ((*(struct el_node **)nodep)->countdown < 0) {
+         strncpy(instp->el_node_test.ip,(*(struct el_node **)nodep)->ip,EL_IP_SIZE);
+         strncpy(instp->el_node_test.call,(*(struct el_node **)nodep)->call,EL_CALL_SIZE);
+         ast_log(LOG_NOTICE,"countdown for %s(%s) negative\n",instp->el_node_test.call,instp->el_node_test.ip);
+      }
+      memset(sdes_packet,0,sizeof(sdes_packet));
+      sdes_length = rtcp_make_sdes(sdes_packet,sizeof(sdes_packet),
+	instp->mycall,instp->myname);
+
+      sin.sin_family = AF_INET;
+      sin.sin_port = htons(instp->ctrl_port);
+      sin.sin_addr.s_addr = inet_addr((*(struct el_node **)nodep)->ip);
+      sendto(instp->ctrl_sock, sdes_packet, sdes_length, 
+             0,(struct sockaddr *)&sin,sizeof(sin));
+   }
+}
+
+static void free_node(void *nodep)
+{
+}
+
+static int find_delete(struct el_node *key)
+{
+   int found = 0;
+   struct el_node **found_key = NULL;
+
+   found_key = (struct el_node **)tfind(key, &el_node_list, compare_ip);
+   if (found_key) {
+       if (debug) ast_log(LOG_DEBUG,"...removing %s(%s)\n", (*found_key)->call, (*found_key)->ip); 
+       found = 1;
+       if (!(*found_key)->instp->confmode) 
+		ast_softhangup((*found_key)->chan,AST_SOFTHANGUP_DEV);
+       tdelete(key, &el_node_list, compare_ip);
+   }
+   return found;
+}
+
+static void process_cmd(char *buf, char *fromip,struct el_instance *instp)
+{
+   char *cmd = NULL;
+   char *arg1 = NULL;
+   char *arg2 = NULL;
+   char *arg3 = NULL;
+
+   char delim = ' ';
+   char *saveptr;
+   char *ptr;
+   struct sockaddr_in sin;
+   unsigned char  pack[256];
+   int pack_length;
+   unsigned short i;
+   struct el_node key;
+
+   if (strncmp(fromip,"127.0.0.1",EL_IP_SIZE) != 0)
+      return;
+   ptr = strchr(buf, (int)'\r'); 
+   if (ptr)
+      *ptr = '\0';
+   ptr = strchr(buf, (int)'\n');    
+   if (ptr)
+      *ptr = '\0';
+
+   /* all commands with no arguments go first */
+
+   if (strcmp(buf,"o.users") == 0) {
+      twalk(el_node_list, print_users);
+      return;
+   }
+
+   if (strcmp(buf, "o.rec") == 0) {
+      if (instp->fdr >= 0) {
+         close(instp->fdr);
+         instp->fdr = -1;
+         ast_log(LOG_NOTICE, "rec stopped\n");
+      }
+      else {
+         instp->fdr = open(instp->fdr_file, O_CREAT | O_WRONLY | O_APPEND | O_TRUNC, 
+                    S_IRUSR | S_IWUSR);
+         if (instp->fdr >= 0)
+            ast_log(LOG_NOTICE, "rec into %s started\n", instp->fdr_file);
+      }
+      return;
+   }
+
+   cmd = strtok_r(buf, &delim, &saveptr);
+   if (!cmd)
+   {
+      return;
+   }
+
+   /* This version:  up to 3 parameters */
+   arg1 = strtok_r(NULL, &delim, &saveptr);
+   arg2 = strtok_r(NULL, &delim, &saveptr);
+   arg3 = strtok_r(NULL, &delim, &saveptr);
+
+   if ((strcmp(cmd, "o.conip") == 0) ||
+       (strcmp(cmd, "o.dconip") == 0)) {
+      if (!arg1)
+      {
+         return;
+      }
+
+      if (strcmp(cmd, "o.conip") == 0)
+         pack_length = rtcp_make_sdes(pack,sizeof(pack),instp->mycall,instp->myname);
+      else
+         pack_length = rtcp_make_bye(pack,"bye");
+      sin.sin_family = AF_INET;
+      sin.sin_port = htons(instp->ctrl_port);
+      sin.sin_addr.s_addr = inet_addr(arg1);
+
+      if (strcmp(cmd, "o.dconip") == 0) {
+           strncpy(key.ip,arg1,EL_IP_SIZE);
+           if (find_delete(&key)) {
+              for (i = 0; i < 20; i++)
+                 sendto(instp->ctrl_sock, pack, pack_length,
+                        0,(struct sockaddr *)&sin,sizeof(sin));
+              ast_log(LOG_NOTICE,"disconnect request sent to %s\n",key.ip);
+           }
+           else
+              ast_log(LOG_NOTICE, "Did not find ip=%s to request disconnect\n",key.ip);
+      }
+      else {
+         for (i = 0; i < 20; i++)
+	 {
+            sendto(instp->ctrl_sock, pack, pack_length,
+                   0,(struct sockaddr *)&sin,sizeof(sin));
+	 }
+         ast_log(LOG_NOTICE,"connect request sent to %s\n", arg1);
+      }
+      return;
+   }
+   return;
+}
+
+static struct ast_frame  *el_xread(struct ast_channel *ast)
+{
+	struct el_pvt *p = ast->tech_pvt;
+  
+        p->fr.frametype = 0;
+        p->fr.subclass = 0;
+        p->fr.datalen = 0;
+        p->fr.samples = 0;
+        p->fr.data =  NULL;
+        p->fr.src = type;
+        p->fr.offset = 0;
+        p->fr.mallocd=0;
+        p->fr.delivery.tv_sec = 0;
+        p->fr.delivery.tv_usec = 0;
+        return &p->fr;
+}
+
+static int el_xwrite(struct ast_channel *ast, struct ast_frame *frame)
+{
+        int bye_length;
+        unsigned char bye[50];
+        unsigned short i;
+        struct sockaddr_in sin;
+	struct el_pvt *p = ast->tech_pvt;
+	struct el_instance *instp = p->instp;
+	struct ast_frame fr,*f1, *f2;
+	struct el_rxqast *qpast;
+	int n,m,x;
+        struct el_rxqel *qpel;
+	char buf[GSM_FRAME_SIZE + AST_FRIENDLY_OFFSET];
+
+	if (frame->frametype != AST_FRAME_VOICE) return 0;
+
+	if (!p->firstsent)
+	{
+		struct sockaddr_in sin;
+		unsigned char  sdes_packet[256];
+		int sdes_length;
+
+		p->firstsent = 1;
+		memset(sdes_packet,0,sizeof(sdes_packet));
+		sdes_length = rtcp_make_sdes(sdes_packet,sizeof(sdes_packet),
+			instp->mycall,instp->myname);
+
+		sin.sin_family = AF_INET;
+		sin.sin_port = htons(instp->ctrl_port);
+		sin.sin_addr.s_addr = inet_addr(p->ip);
+		sendto(instp->ctrl_sock, sdes_packet, sdes_length, 
+			0,(struct sockaddr *)&sin,sizeof(sin));
+	}
+
+        /* Echolink to Asterisk */
+	if (p->rxqast.qe_forw != &p->rxqast) {
+		for(n = 0,qpast = p->rxqast.qe_forw; qpast != &p->rxqast; qpast = qpast->qe_forw) {
+			n++;
+		}
+		if (n > QUEUE_OVERLOAD_THRESHOLD_AST) {
+			while(p->rxqast.qe_forw != &p->rxqast) {
+				qpast = p->rxqast.qe_forw;
+				remque((struct qelem *)qpast);
+				ast_free(qpast);
+			}
+			if (p->rxkey) p->rxkey = 1;
+		} else {		
+			if (!p->rxkey) {
+				fr.datalen = 0;
+				fr.samples = 0;
+				fr.frametype = AST_FRAME_CONTROL;
+				fr.subclass = AST_CONTROL_RADIO_KEY;
+				fr.data =  0;
+				fr.src = type;
+				fr.offset = 0;
+				fr.mallocd=0;
+				fr.delivery.tv_sec = 0;
+				fr.delivery.tv_usec = 0;
+				ast_queue_frame(ast,&fr);
+			} 
+			p->rxkey = MAX_RXKEY_TIME;
+			qpast = p->rxqast.qe_forw;
+			remque((struct qelem *)qpast);
+			memcpy(buf + AST_FRIENDLY_OFFSET,qpast->buf,GSM_FRAME_SIZE);
+			ast_free(qpast);
+
+			fr.datalen = GSM_FRAME_SIZE;
+			fr.samples = 160;
+			fr.frametype = AST_FRAME_VOICE;
+			fr.subclass = AST_FORMAT_GSM;
+			fr.data =  buf + AST_FRIENDLY_OFFSET;
+			fr.src = type;
+			fr.offset = AST_FRIENDLY_OFFSET;
+			fr.mallocd=0;
+			fr.delivery.tv_sec = 0;
+			fr.delivery.tv_usec = 0;
+
+			x = 0;
+			if (p->dsp && (!instp->confmode))
+			{
+				f2 = ast_translate(p->xpath,&fr,0);
+				f1 = ast_dsp_process(NULL,p->dsp,f2);
+				ast_frfree(f2);
+				if ((f1->frametype == AST_FRAME_DTMF_END) ||
+					(f1->frametype == AST_FRAME_DTMF_BEGIN))
+				{
+					if ((f1->subclass != 'm') && (f1->subclass != 'u'))
+					{
+						if (f1->frametype == AST_FRAME_DTMF_END)
+							ast_log(LOG_NOTICE,"Echolink %s Got DTMF char %c from IP %s\n",p->stream,f1->subclass,p->ip);
+						ast_queue_frame(ast,f1);
+						x = 1;
+					}
+				}
+				ast_frfree(f1);
+			} 
+			if (!x) ast_queue_frame(ast,&fr);
+		}
+	}
+	if (p->rxkey == 1) {
+		fr.datalen = 0;
+		fr.samples = 0;
+		fr.frametype = AST_FRAME_CONTROL;
+		fr.subclass = AST_CONTROL_RADIO_UNKEY;
+		fr.data =  0;
+		fr.src = type;
+		fr.offset = 0;
+		fr.mallocd=0;
+		fr.delivery.tv_sec = 0;
+		fr.delivery.tv_usec = 0;
+		ast_queue_frame(ast,&fr);
+	} 
+	if (p->rxkey) p->rxkey--;
+
+        if (instp->confmode && (p->rxqel.qe_forw != &p->rxqel))
+        {
+           for(m = 0,qpel = p->rxqel.qe_forw; qpel != &p->rxqel; qpel = qpel->qe_forw) 
+              m++;
+
+           if (m > QUEUE_OVERLOAD_THRESHOLD_EL)
+           {
+              while(p->rxqel.qe_forw != &p->rxqel) 
+              {
+                 qpel = p->rxqel.qe_forw;
+                 remque((struct qelem *)qpel);
+                 ast_free(qpel);
+              }
+           } 
+           else 
+           {
+              qpel = p->rxqel.qe_forw;
+              remque((struct qelem *)qpel);
+
+              memcpy(instp->audio_all_but_one.data,qpel->buf,BLOCKING_FACTOR * GSM_FRAME_SIZE);
+              strncpy(instp->el_node_test.ip, qpel->fromip, EL_IP_SIZE);
+
+              ast_free(qpel);
+	      ast_mutex_lock(&instp->lock);
+              twalk(el_node_list, send_audio_all_but_one);
+	      ast_mutex_unlock(&instp->lock);
+
+              if (instp->fdr >= 0)
+                 write(instp->fdr, instp->audio_all_but_one.data, BLOCKING_FACTOR * GSM_FRAME_SIZE);
+           }
+        }
+        else
+        {
+           /* Asterisk to Echolink */
+           if (!(frame->subclass & (AST_FORMAT_GSM))) {
+                ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+		ast_mutex_unlock(&instp->lock);
+                return 0;
+           }
+           if (p->txkey || p->txindex)  {
+                memcpy(instp->audio_all.data + (GSM_FRAME_SIZE * p->txindex++), frame->data,GSM_FRAME_SIZE);
+           }      
+           if (p->txindex >= BLOCKING_FACTOR) {
+		ast_mutex_lock(&instp->lock);
+                if (instp->confmode)
+		{
+			twalk(el_node_list, send_audio_all);
+		}
+		else
+		{
+			strcpy(instp->el_node_test.ip,p->ip);
+			twalk(el_node_list, send_audio_only_one);
+		}
+		ast_mutex_unlock(&instp->lock);
+                p->txindex = 0;
+           }
+        }
+
+	if (p->keepalive--) return 0;
+	p->keepalive = KEEPALIVE_TIME;
+
+        /* Echolink: send heartbeats and drop dead stations */
+	ast_mutex_lock(&instp->lock);
+        instp->el_node_test.ip[0] = '\0';
+        twalk(el_node_list, send_heartbeat); 
+        if (instp->el_node_test.ip[0] != '\0') {
+           if (find_delete(&instp->el_node_test)) {
+              bye_length = rtcp_make_bye(bye,"rtcp timeout");
+              sin.sin_family = AF_INET;
+              sin.sin_addr.s_addr = inet_addr(instp->el_node_test.ip);
+              sin.sin_port = htons(instp->ctrl_port);
+	      ast_mutex_lock(&instp->lock);
+              for (i = 0; i < 20; i++)
+                 sendto(instp->ctrl_sock, bye, bye_length,
+                        0,(struct sockaddr *)&sin,sizeof(sin));
+	      ast_mutex_unlock(&instp->lock);
+              ast_log(LOG_NOTICE,"call=%s RTCP timeout, removing\n",instp->el_node_test.call);
+           }
+           instp->el_node_test.ip[0] = '\0';
+        } 
+	ast_mutex_unlock(&instp->lock);
+	return 0;
+}
+
+static struct ast_channel *el_new(struct el_pvt *i, int state, unsigned int nodenum)
+{
+	struct ast_channel *tmp;
+	struct el_instance *instp = i->instp;
+
+	tmp = ast_channel_alloc(1, state, 0, 0, "", instp->astnode, instp->context, 0, "echolink/%s", i->stream);
+	if (tmp) {
+		tmp->tech = &el_tech;
+		tmp->nativeformats = prefformat;
+		tmp->rawreadformat = prefformat;
+		tmp->rawwriteformat = prefformat;
+		tmp->writeformat = prefformat;
+		tmp->readformat = prefformat;
+		if (state == AST_STATE_RING)
+			tmp->rings = 1;
+		tmp->tech_pvt = i;
+		ast_copy_string(tmp->context, instp->context, sizeof(tmp->context));
+		ast_copy_string(tmp->exten, instp->astnode,  sizeof(tmp->exten));
+		ast_string_field_set(tmp, language, "");
+		if (nodenum > 0)
+		{
+			char tmpstr[30];
+
+			sprintf(tmpstr,"3%06u",nodenum);
+			ast_set_callerid(tmp,tmpstr,NULL,NULL);
+		}
+		i->owner = tmp;
+		i->u = ast_module_user_add(tmp);
+		i->nodenum = nodenum;
+		if (state != AST_STATE_DOWN) {
+			if (ast_pbx_start(tmp)) {
+				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+				ast_hangup(tmp);
+			}
+		}
+	} else
+		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+	return tmp;
+}
+
+
+static struct ast_channel *el_request(const char *type, int format, void *data, int *cause)
+{
+	int oldformat,nodenum;
+	struct el_pvt *p;
+	struct ast_channel *tmp = NULL;
+	char *str,*cp;
+	
+	oldformat = format;
+	format &= (AST_FORMAT_GSM);
+	if (!format) {
+		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+		return NULL;
+	}
+	str = strdup((char *)data);
+	cp = strchr(str,'/');
+	if (cp) *cp++ = 0;
+	nodenum = 0;
+	if (*cp && *++cp) nodenum = atoi(cp);
+	p = el_alloc(str);
+	free(str);
+	if (p) {
+		tmp = el_new(p, AST_STATE_DOWN,nodenum);
+		if (!tmp)
+			el_destroy(p);
+	}
+	return tmp;
+}
+
+static int unload_module(void)
+{
+int	n;
+
+        run_forever = 0;
+        tdestroy(el_node_list, free_node);
+	for(n = 0; n < ninstances; n++)
+	{
+		if (instances[n]->audio_sock != -1)
+		{
+			close(instances[n]->audio_sock);
+			instances[n]->audio_sock = -1;
+		}
+		if (instances[n]->ctrl_sock != -1)
+		{
+			close(instances[n]->ctrl_sock);
+			instances[n]->ctrl_sock = -1;
+		}
+	}	
+	/* First, take us out of the channel loop */
+	ast_channel_unregister(&el_tech);
+	for(n = 0; n < ninstances; n++) ast_free(instances[n]);
+	return 0;
+}
+
+/* 
+   If asterisk has a function that writes at least n bytes to a TCP socket,
+   remove writen function and use the one provided by Asterisk
+*/
+static int writen(int fd, char *ptr, int nbytes)
+{
+   int nleft, nwritten;
+   char *local_ptr;
+
+   nleft = nbytes;
+   local_ptr = ptr;
+
+   while (nleft > 0)
+   {
+      nwritten = write(fd, local_ptr, nleft);
+      if (nwritten < 0)
+         return nwritten;
+      nleft -= nwritten;
+      local_ptr += nwritten;
+   }
+   return (nbytes - nleft);
+}
+
+/* Feel free to make this code smaller, I know it works, so I use it */
+static int sendcmd(char *server, struct el_instance *instp)
+{
+  struct hostent *ahp;
+  struct ast_hostent ah;
+  struct in_addr ia;
+
+  char ip[EL_IP_SIZE + 1];
+  struct sockaddr_in el;
+  int el_len;
+  int sd;
+  int  rc;
+  time_t now;
+  struct tm *p_tm;
+  char *id = NULL;
+  const size_t LOGINSIZE = 1023;
+  char buf[LOGINSIZE + 1];
+  size_t len;
+
+  ahp = ast_gethostbyname(server,&ah);
+  if (ahp) {
+     memcpy(&ia,ahp->h_addr,sizeof(in_addr_t));
+#ifdef  OLD_ASTERISK
+     ast_inet_ntoa(ip,EL_IP_SIZE,ia);
+#else
+     strncpy(ip,ast_inet_ntoa(ia),EL_IP_SIZE);
+#endif
+  } else {
+     ast_log(LOG_NOTICE, "Failed to resolve Echolink server %s\n", server);
+     return -1;
+  }
+
+  memset(&el,0,sizeof(struct sockaddr_in));
+  el.sin_family = AF_INET;
+  el.sin_port = htons(5200);
+  el.sin_addr.s_addr =  inet_addr(ip);
+  el_len = sizeof(el);
+

[... 1014 lines stripped ...]



More information about the svn-commits mailing list