/*
 * Copyright (C) 2004-2005 by Objective Systems, Inc.
 *
 * This software is furnished under an open source license and may be 
 * used and copied only in accordance with the terms of this license. 
 * The text of the license may generally be found in the root 
 * directory of this installation in the COPYING file.  It 
 * can also be viewed online at the following URL:
 *
 *   http://www.obj-sys.com/open/license.html
 *
 * Any redistributions of this file including modified versions must 
 * maintain this copyright notice.
 *
 *****************************************************************************/

/* Reworked version I, Nov-2009, by Alexandr Anikin, may@telecom-service.ru */


/*** MODULEINFO
	<defaultenabled>no</defaultenabled>
	<support_level>extended</support_level>
 ***/

#include "chan_ooh323.h"
#include <math.h>

#define FORMAT_STRING_SIZE	512

/* Defaults */
#define DEFAULT_CONTEXT "default"
#define DEFAULT_H323ID "Asterisk PBX"
#define DEFAULT_LOGFILE "/var/log/asterisk/h323_log"
#define DEFAULT_H323ACCNT "ast_h323"

/* Flags */
#define H323_SILENCESUPPRESSION (1<<0)
#define H323_GKROUTED           (1<<1)
#define H323_TUNNELING          (1<<2)
#define H323_FASTSTART          (1<<3)
#define H323_OUTGOING           (1<<4)
#define H323_ALREADYGONE        (1<<5)
#define H323_NEEDDESTROY        (1<<6)
#define H323_DISABLEGK          (1<<7)
#define H323_NEEDSTART		(1<<8)

#define MAXT30	240
#define T38TOAUDIOTIMEOUT 30
#define T38_DISABLED 0
#define T38_ENABLED 1
#define T38_FAXGW 1

/* Channel description */
static const char type[] = "OOH323";
static const char tdesc[] = "Objective Systems H323 Channel Driver";
static const char config[] = "ooh323.conf";

struct ast_module *myself;

static struct ast_jb_conf default_jbconf =
{
	.flags = 0,
	.max_size = -1,
	.resync_threshold = -1,
	.impl = ""
};
static struct ast_jb_conf global_jbconf;

/* Channel Definition */
static struct ast_channel *ooh323_request(const char *type, format_t format, 
			const struct ast_channel *requestor,  void *data, int *cause);
static int ooh323_digit_begin(struct ast_channel *ast, char digit);
static int ooh323_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int ooh323_call(struct ast_channel *ast, char *dest, int timeout);
static int ooh323_hangup(struct ast_channel *ast);
static int ooh323_answer(struct ast_channel *ast);
static struct ast_frame *ooh323_read(struct ast_channel *ast);
static int ooh323_write(struct ast_channel *ast, struct ast_frame *f);
static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
static int ooh323_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);

static enum ast_rtp_glue_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **rtp);
static enum ast_rtp_glue_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **rtp);
static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, 
          struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active);

static struct ast_udptl *ooh323_get_udptl_peer(struct ast_channel *chan);
static int ooh323_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl);

static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);

struct ooh323_peer *find_friend(const char *name, int port);


static const struct ast_channel_tech ooh323_tech = {
	.type = type,
	.description = tdesc,
	.capabilities = -1,
	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
	.requester = ooh323_request,
	.send_digit_begin = ooh323_digit_begin,
	.send_digit_end = ooh323_digit_end,
	.call = ooh323_call,
	.hangup = ooh323_hangup,
	.answer = ooh323_answer,
	.read = ooh323_read,
	.write = ooh323_write,
	.exception = ooh323_read,
	.indicate = ooh323_indicate,
	.fixup = ooh323_fixup,
	.send_html = 0,
	.queryoption = ooh323_queryoption,
	.bridge = ast_rtp_instance_bridge,		/* XXX chan unlocked ? */
	.early_bridge = ast_rtp_instance_early_bridge,

};

static struct ast_rtp_glue ooh323_rtp = {
	.type = type,
	.get_rtp_info = ooh323_get_rtp_peer,
	.get_vrtp_info = ooh323_get_vrtp_peer,
	.update_peer = ooh323_set_rtp_peer,
};

static struct ast_udptl_protocol ooh323_udptl = {
	.type = "H323",
	.get_udptl_info = ooh323_get_udptl_peer,
	.set_udptl_peer = ooh323_set_udptl_peer,
};



struct ooh323_user;

/* H.323 channel private structure */
static struct ooh323_pvt {
	ast_mutex_t lock;		/* Channel private lock */
	struct ast_rtp_instance *rtp;
	struct ast_rtp_instance *vrtp; /* Placeholder for now */

	int t38support;			/* T.38 mode - disable, transparent, faxgw */
	int rtptimeout;
	struct ast_udptl *udptl;
	int faxmode;
	int t38_tx_enable;
	int t38_init;
	struct sockaddr_in udptlredirip;
	time_t lastTxT38;
	int chmodepend;

	struct ast_channel *owner;	/* Master Channel */
   	union {
    		char  *user;	/* cooperating user/peer */
    		char  *peer;
   	} neighbor;
	time_t lastrtptx;
	time_t lastrtprx;
	unsigned int flags;
	unsigned int call_reference;
	char *callToken;
	char *username;
	char *host;
	char *callerid_name;
	char *callerid_num;
	char caller_h323id[AST_MAX_EXTENSION];
	char caller_dialedDigits[AST_MAX_EXTENSION];
	char caller_email[AST_MAX_EXTENSION];
	char caller_url[256];
	char callee_h323id[AST_MAX_EXTENSION];
	char callee_dialedDigits[AST_MAX_EXTENSION];
	char callee_email[AST_MAX_EXTENSION];
	char callee_url[AST_MAX_EXTENSION];
 
	int port;
	format_t readformat;   /* negotiated read format */
	format_t writeformat;  /* negotiated write format */
	format_t capability;
	struct ast_codec_pref prefs;
	int dtmfmode;
	int dtmfcodec;
	char exten[AST_MAX_EXTENSION];	/* Requested extension */
	char context[AST_MAX_EXTENSION];	/* Context where to start */
	char accountcode[256];	/* Account code */
	int nat;
	int amaflags;
	int progsent;			/* progress is sent */
	int alertsent;			/* alerting is sent */
	int g729onlyA;			/* G.729 only A */
	struct ast_dsp *vad;
	struct OOH323Regex *rtpmask;	/* rtp ip regexp */
	char rtpmaskstr[120];
	int rtdrcount, rtdrinterval;	/* roundtripdelayreq */
	int faststart, h245tunneling;	/* faststart & h245 tunneling */
	struct ooh323_pvt *next;	/* Next entity */
} *iflist = NULL;

/* Protect the channel/interface list (ooh323_pvt) */
AST_MUTEX_DEFINE_STATIC(iflock);

/* Profile of H.323 user registered with PBX*/
struct ooh323_user{
	ast_mutex_t lock;
	char        name[256];
	char        context[AST_MAX_EXTENSION];
	int         incominglimit;
	unsigned    inUse;
	char        accountcode[20];
	int         amaflags;
	format_t    capability;
	struct ast_codec_pref prefs;
	int         dtmfmode;
	int	    dtmfcodec;
	int	    t38support;
	int         rtptimeout;
	int         mUseIP;        /* Use IP address or H323-ID to search user */
	char        mIP[20];
	struct OOH323Regex	    *rtpmask;
	char	    rtpmaskstr[120];
	int	    rtdrcount, rtdrinterval;
	int	    faststart, h245tunneling;
	int	    g729onlyA;
	struct ooh323_user *next;
};

/* Profile of valid asterisk peers */
struct ooh323_peer{
	ast_mutex_t lock;
	char        name[256];
	unsigned    outgoinglimit;
	unsigned    outUse;
	format_t    capability;
	struct ast_codec_pref prefs;
	char        accountcode[20];
	int         amaflags;
	int         dtmfmode;
	int	    dtmfcodec;
	int	    t38support;
	int         mFriend;    /* indicates defined as friend */
	char        ip[20];
	int         port;
	char        *h323id;    /* H323-ID alias, which asterisk will register with gk to reach this peer*/
	char        *email;     /* Email alias, which asterisk will register with gk to reach this peer*/
	char        *url;       /* url alias, which asterisk will register with gk to reach this peer*/
	char        *e164;      /* e164 alias, which asterisk will register with gk to reach this peer*/
	int         rtptimeout;
	struct OOH323Regex	    *rtpmask;
	char	    rtpmaskstr[120];
	int	    rtdrcount,rtdrinterval;
	int	    faststart, h245tunneling;
	int	    g729onlyA;
	struct ooh323_peer *next;
};


/* List of H.323 users known to PBX */
static struct ast_user_list {
	struct ooh323_user *users;
	ast_mutex_t lock;
} userl;

static struct ast_peer_list {
	struct ooh323_peer *peers;
	ast_mutex_t lock;
} peerl;

/* Mutex to protect H.323 reload process */
static int h323_reloading = 0;
AST_MUTEX_DEFINE_STATIC(h323_reload_lock);

/* Mutex to protect usage counter */
static int usecnt = 0;
AST_MUTEX_DEFINE_STATIC(usecnt_lock);

AST_MUTEX_DEFINE_STATIC(ooh323c_cmd_lock);

static long callnumber = 0;
AST_MUTEX_DEFINE_STATIC(ooh323c_cn_lock);

/* stack callbacks */
int onAlerting(ooCallData *call);
int onProgress(ooCallData *call);
int onNewCallCreated(ooCallData *call);
int onOutgoingCall(ooCallData *call);
int onCallEstablished(ooCallData *call);
int onCallCleared(ooCallData *call);
void onModeChanged(ooCallData *call, int t38mode);

extern OOH323EndPoint gH323ep;

static char gLogFile[256] = DEFAULT_LOGFILE;
static int  gPort = 1720;
static char gIP[20];
static char gCallerID[AST_MAX_EXTENSION] = "";
static struct ooAliases *gAliasList;
static format_t gCapability = AST_FORMAT_ULAW;
static struct ast_codec_pref gPrefs;
static int  gDTMFMode = H323_DTMF_RFC2833;
static int  gDTMFCodec = 101;
static int  gT38Support = T38_FAXGW;
static char gGatekeeper[100];
static enum RasGatekeeperMode gRasGkMode = RasNoGatekeeper;

static int  gIsGateway = 0;
static int  gFastStart = 1;
static int  gTunneling = 1;
static int  gBeMaster = 0;
static int  gMediaWaitForConnect = 0;
static int  gTOS = 0;
static int  gRTPTimeout = 60;
static int  g729onlyA = 0;
static char gAccountcode[80] = DEFAULT_H323ACCNT;
static int  gAMAFLAGS;
static char gContext[AST_MAX_EXTENSION] = DEFAULT_CONTEXT;
static int  gIncomingLimit = 1024;
static int  gOutgoingLimit = 1024;
OOBOOL gH323Debug = FALSE;
static int gTRCLVL = OOTRCLVLERR;
static int gRTDRCount = 0, gRTDRInterval = 0;

static int t35countrycode = 0;
static int t35extensions = 0;
static int manufacturer = 0;
static char vendor[AST_MAX_EXTENSION] =  "";
static char version[AST_MAX_EXTENSION] = "";

static struct ooh323_config
{
   int  mTCPPortStart;
   int  mTCPPortEnd;
} ooconfig;

/** Asterisk RTP stuff*/
static struct sched_context *sched;
static struct io_context *io;

/* Protect the monitoring thread, so only one process can kill or start it, 
   and not when it's doing something critical. */
AST_MUTEX_DEFINE_STATIC(monlock);


/* This is the thread for the monitor which checks for input on the channels
   which are not currently in use.  */
static pthread_t monitor_thread = AST_PTHREADT_NULL;


static struct ast_channel *ooh323_new(struct ooh323_pvt *i, int state,
                                             const char *host, int capability, const char *linkedid) 
{
	struct ast_channel *ch = NULL;
	int fmt = 0;
	if (gH323Debug)
		ast_verbose("---   ooh323_new - %s, %d\n", host, capability);


	/* Don't hold a h323 pvt lock while we allocate a channel */
	ast_mutex_unlock(&i->lock);
   	ast_mutex_lock(&ooh323c_cn_lock);
   	ch = ast_channel_alloc(1, state, i->callerid_num, i->callerid_name, 
				i->accountcode, i->exten, i->context, linkedid, i->amaflags,
				"OOH323/%s-%ld", host, callnumber);
   	callnumber++;
   	ast_mutex_unlock(&ooh323c_cn_lock);
   
	ast_mutex_lock(&i->lock);

	if (ch) {
		ast_channel_lock(ch);
		ch->tech = &ooh323_tech;

		if (capability)
			fmt = ast_best_codec(capability);
		if (!fmt) 
			fmt = ast_codec_pref_index(&i->prefs, 0);

		ch->nativeformats = ch->rawwriteformat = ch->rawreadformat = fmt;

		ast_channel_set_fd(ch, 0, ast_rtp_instance_fd(i->rtp, 0));
		ast_channel_set_fd(ch, 1, ast_rtp_instance_fd(i->rtp, 1));
		ast_channel_set_fd(ch, 5, ast_udptl_fd(i->udptl));

		ast_jb_configure(ch, &global_jbconf);

		if (state == AST_STATE_RING)
			ch->rings = 1;

		ch->adsicpe = AST_ADSI_UNAVAILABLE;
		ast_set_write_format(ch, fmt);
		ast_set_read_format(ch, fmt);
		ch->tech_pvt = i;
		i->owner = ch;
		ast_module_ref(myself);

		/* Allocate dsp for in-band DTMF support */
		if (i->dtmfmode & H323_DTMF_INBAND) {
			i->vad = ast_dsp_new();
			ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT);
       			ast_dsp_set_features(i->vad,
						DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_FAX_DETECT);
        		ast_dsp_set_faxmode(i->vad,
						DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED);

			if (i->dtmfmode & H323_DTMF_INBANDRELAX)
				ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
		}

		ast_mutex_lock(&usecnt_lock);
		usecnt++;
		ast_mutex_unlock(&usecnt_lock);

		/* Notify the module monitors that use count for resource has changed*/
		ast_update_use_count();

		ast_copy_string(ch->context, i->context, sizeof(ch->context));
		ast_copy_string(ch->exten, i->exten, sizeof(ch->exten));

		ch->priority = 1;

      		if(!ast_test_flag(i, H323_OUTGOING)) {
		
			if (!ast_strlen_zero(i->caller_h323id)) {
				pbx_builtin_setvar_helper(ch, "_CALLER_H323ID", i->caller_h323id);

			}
			if (!ast_strlen_zero(i->caller_dialedDigits)) {
				pbx_builtin_setvar_helper(ch, "_CALLER_H323DIALEDDIGITS", 
				i->caller_dialedDigits);
			}
			if (!ast_strlen_zero(i->caller_email)) {
				pbx_builtin_setvar_helper(ch, "_CALLER_H323EMAIL", 
				i->caller_email);
			}
			if (!ast_strlen_zero(i->caller_url)) {
				pbx_builtin_setvar_helper(ch, "_CALLER_H323URL", i->caller_url);
			}
		}

		if (!ast_strlen_zero(i->accountcode))
			ast_string_field_set(ch, accountcode, i->accountcode);
		
		if (i->amaflags)
			ch->amaflags = i->amaflags;

		ast_setstate(ch, state);
		if (state != AST_STATE_DOWN) {
         		if (ast_pbx_start(ch)) {
				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name);
            			ast_channel_unlock(ch);
				ast_hangup(ch);
				ch = NULL;
			} 
	 	}

		if (ch) {
			manager_event(EVENT_FLAG_SYSTEM, "ChannelUpdate", 
				"Channel: %s\r\nChanneltype: %s\r\n"
				"CallRef: %d\r\n", ch->name, "OOH323", i->call_reference);
		}
	} else
		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");


   	if(ch)   ast_channel_unlock(ch);

	if (gH323Debug)
		ast_verbose("+++   h323_new\n");

	return ch;
}



static struct ooh323_pvt *ooh323_alloc(int callref, char *callToken) 
{
	struct ooh323_pvt *pvt = NULL;
	struct sockaddr_in ouraddr;
	struct ast_sockaddr tmp;
	struct in_addr ipAddr;
	if (gH323Debug)
		ast_verbose("---   ooh323_alloc\n");

	if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
		ast_log(LOG_ERROR, "Couldn't allocate private ooh323 structure\n");
		return NULL;
	}

	ast_mutex_init(&pvt->lock);
	ast_mutex_lock(&pvt->lock);


	if (!inet_aton(gIP, &ipAddr)) {
		ast_log(LOG_ERROR, "Invalid OOH323 driver ip address\n");
		ast_mutex_unlock(&pvt->lock);
		ast_mutex_destroy(&pvt->lock);
		ast_free(pvt);
		return NULL;
	}

	ouraddr.sin_family = AF_INET;
	ouraddr.sin_addr = ipAddr;
	ast_sockaddr_from_sin(&tmp, &ouraddr);
	if (!(pvt->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
		ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", 
				  strerror(errno));
		ast_mutex_unlock(&pvt->lock);
		ast_mutex_destroy(&pvt->lock);
		ast_free(pvt);
		return NULL;
	}
 
	ast_rtp_instance_set_qos(pvt->rtp, gTOS, 0, "ooh323-rtp");

	if (!(pvt->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, &tmp))) {
		ast_log(LOG_WARNING, "Unable to create UDPTL session: %s\n",
				strerror(errno));
		ast_mutex_unlock(&pvt->lock);
		ast_mutex_destroy(&pvt->lock);
		ast_free(pvt);
		return NULL;
	}

	ast_udptl_set_error_correction_scheme(pvt->udptl, UDPTL_ERROR_CORRECTION_NONE);
	ast_udptl_set_far_max_datagram(pvt->udptl, 144);
	pvt->faxmode = 0;
	pvt->t38support = gT38Support;
	pvt->rtptimeout = gRTPTimeout;
	pvt->rtdrinterval = gRTDRInterval;
	pvt->rtdrcount = gRTDRCount;
	pvt->g729onlyA = g729onlyA;

	pvt->call_reference = callref;
	if (callToken)
		pvt->callToken = strdup(callToken);

	/* whether to use gk for this call */
	if (gRasGkMode == RasNoGatekeeper)
		OO_SETFLAG(pvt->flags, H323_DISABLEGK);

	pvt->dtmfmode = gDTMFMode;
	pvt->dtmfcodec = gDTMFCodec;
	ast_copy_string(pvt->context, gContext, sizeof(pvt->context));
	ast_copy_string(pvt->accountcode, gAccountcode, sizeof(pvt->accountcode));

	pvt->amaflags = gAMAFLAGS;
	pvt->capability = gCapability;
	memcpy(&pvt->prefs, &gPrefs, sizeof(pvt->prefs));

	ast_mutex_unlock(&pvt->lock); 
	/* Add to interface list */
	ast_mutex_lock(&iflock);
	pvt->next = iflist;
	iflist = pvt;
	ast_mutex_unlock(&iflock);

	if (gH323Debug)
		ast_verbose("+++   ooh323_alloc\n");

	return pvt;
}


/*
	Possible data values - peername, exten/peername, exten@ip
 */
static struct ast_channel *ooh323_request(const char *type, format_t format,
		const struct ast_channel *requestor, void *data, int *cause)

{
	struct ast_channel *chan = NULL;
	struct ooh323_pvt *p = NULL;
	struct ooh323_peer *peer = NULL;
	char *dest = NULL; 
	char *ext = NULL;
	char tmp[256];
	char formats[FORMAT_STRING_SIZE];
	int port = 0;

	if (gH323Debug)
		ast_verbose("---   ooh323_request - data %s format %s\n", (char*)data,  
										ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,format));

	format &= AST_FORMAT_AUDIO_MASK;
	if (!format) {
		ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%lld'\n", (long long) format);
		return NULL;
	}

	p = ooh323_alloc(0,0); /* Initial callRef is zero */

	if (!p) {
		ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char*)data);
		return NULL;
	}
	ast_mutex_lock(&p->lock);

	/* This is an outgoing call, since ooh323_request is called */
	ast_set_flag(p, H323_OUTGOING);


   	ast_copy_string(tmp, data, sizeof(tmp));

	dest = strchr(tmp, '/');

	if (dest) {  
		*dest = '\0';
		dest++;
		ext = dest;
		dest = tmp;
	} else if ((dest = strchr(tmp, '@'))) {
		*dest = '\0';
		dest++;
		ext = tmp;
	} else {
		dest = tmp;
		ext = NULL;
	}

#if 0
	if ((sport = strchr(dest, ':'))) {
		*sport = '\0';
		sport++;
		port = atoi(sport);
	}
#endif

	if (dest) {
		peer = find_peer(dest, port);
	} else{
		ast_mutex_lock(&iflock);
		ast_mutex_unlock(&p->lock);
		ooh323_destroy(p);
		ast_mutex_unlock(&iflock);
		ast_log(LOG_ERROR, "Destination format is not supported\n");
		*cause = AST_CAUSE_INVALID_NUMBER_FORMAT;
		return NULL;
	}

	if (peer) {
		p->username = strdup(peer->name);
		p->host = strdup(peer->ip);
		p->port = peer->port;
		/* Disable gk as we are going to call a known peer*/
		/* OO_SETFLAG(p->flags, H323_DISABLEGK); */

		if (ext)
			ast_copy_string(p->exten, ext, sizeof(p->exten));

		p->capability = peer->capability;
		memcpy(&p->prefs, &peer->prefs, sizeof(struct ast_codec_pref));
		p->g729onlyA = peer->g729onlyA;
		p->dtmfmode |= peer->dtmfmode;
		p->dtmfcodec  = peer->dtmfcodec;
		p->t38support = peer->t38support;
		p->rtptimeout = peer->rtptimeout;
		p->faststart = peer->faststart;
		p->h245tunneling = peer->h245tunneling;
		if (peer->rtpmask && peer->rtpmaskstr[0]) {
			p->rtpmask = peer->rtpmask;
			ast_copy_string(p->rtpmaskstr, peer->rtpmaskstr, sizeof(p->rtpmaskstr));
		}

		if (peer->rtdrinterval) {
			p->rtdrinterval = peer->rtdrinterval;
			p->rtdrcount = peer->rtdrcount;
		}

		ast_copy_string(p->accountcode, peer->accountcode, sizeof(p->accountcode));
		p->amaflags = peer->amaflags;
	} else {
		if (gRasGkMode ==  RasNoGatekeeper) {
			/* no gk and no peer */
			ast_log(LOG_ERROR, "Call to undefined peer %s", dest);
			ast_mutex_lock(&iflock);
			ast_mutex_unlock(&p->lock);
			ooh323_destroy(p);
			ast_mutex_unlock(&iflock);
			return NULL;
		} else if (gH323ep.gkClient && gH323ep.gkClient->state != GkClientRegistered) {
			ast_log(LOG_ERROR, "Gatekeeper client is configured but not registered\n");
			*cause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
			return NULL;
		}
		p->g729onlyA = g729onlyA;
		p->dtmfmode = gDTMFMode;
		p->dtmfcodec = gDTMFCodec;
		p->t38support = gT38Support;
		p->rtptimeout = gRTPTimeout;
		p->capability = gCapability;
		p->rtdrinterval = gRTDRInterval;
		p->rtdrcount = gRTDRCount;
		p->faststart = gFastStart;
		p->h245tunneling = gTunneling;

		memcpy(&p->prefs, &gPrefs, sizeof(struct ast_codec_pref));
		p->username = strdup(dest);

		p->host = strdup(dest);
		if (port > 0) {
			p->port = port;
		}
		if (ext) {
			ast_copy_string(p->exten, ext, sizeof(p->exten));
		}
	}


	chan = ooh323_new(p, AST_STATE_DOWN, p->username, format,
				 requestor ? requestor->linkedid : NULL);
	
	ast_mutex_unlock(&p->lock);

	if (!chan) {
		ast_mutex_lock(&iflock);
		ooh323_destroy(p);
		ast_mutex_unlock(&iflock);
   	} else {
      		ast_mutex_lock(&p->lock);
      		p->callToken = (char*)ast_malloc(AST_MAX_EXTENSION);
      		if(!p->callToken) {
       			ast_mutex_unlock(&p->lock);
       			ast_mutex_lock(&iflock);
       			ooh323_destroy(p);
       			ast_mutex_unlock(&iflock);
       			ast_log(LOG_ERROR, "Failed to allocate memory for callToken\n");
       			return NULL;
      		}

      		ast_mutex_unlock(&p->lock);
      		ast_mutex_lock(&ooh323c_cmd_lock);
      		ooMakeCall(data, p->callToken, AST_MAX_EXTENSION, NULL);
      		ast_mutex_unlock(&ooh323c_cmd_lock);
	}

	restart_monitor();
	if (gH323Debug)
		ast_verbose("+++   ooh323_request\n");

	return chan;

}


static struct ooh323_pvt* find_call(ooCallData *call)
{
	struct ooh323_pvt *p;

	if (gH323Debug)
		ast_verbose("---   find_call\n");

	ast_mutex_lock(&iflock);

	for (p = iflist; p; p = p->next) {
		if (p->callToken && !strcmp(p->callToken, call->callToken)) {
			break;
		}
	}
	ast_mutex_unlock(&iflock);

	if (gH323Debug)
		ast_verbose("+++   find_call\n");

	return p;
}

struct ooh323_user *find_user(const char * name, const char* ip)
{
	struct ooh323_user *user;

	if (gH323Debug)
      ast_verbose("---   find_user: %s, %s\n",name,ip);

	ast_mutex_lock(&userl.lock);

	for (user = userl.users; user; user = user->next) {
		if (ip && user->mUseIP && !strcmp(user->mIP, ip)) {
			break;
		}
		if (name && !strcmp(user->name, name)) {
			break;
		}
	}

	ast_mutex_unlock(&userl.lock);

	if (gH323Debug)
		ast_verbose("+++   find_user\n");

	return user;
}

struct ooh323_peer *find_friend(const char *name, int port)
{
	struct ooh323_peer *peer;  

	if (gH323Debug)
		ast_verbose("---   find_friend \"%s\"\n", name);


	ast_mutex_lock(&peerl.lock);
	for (peer = peerl.peers; peer; peer = peer->next) {
		if (gH323Debug) {
			ast_verbose("		comparing with \"%s\"\n", peer->ip);
		}
		if (!strcmp(peer->ip, name)) {
			if (port <= 0 || (port > 0 && peer->port == port)) {
				break;
			}
		}
	}
	ast_mutex_unlock(&peerl.lock);

	if (gH323Debug) {
		if (peer) {
			ast_verbose("		found matching friend\n");
		}
		ast_verbose("+++   find_friend \"%s\"\n", name);
	}

	return peer;		
}


struct ooh323_peer *find_peer(const char * name, int port)
{
	struct ooh323_peer *peer;

	if (gH323Debug)
		ast_verbose("---   find_peer \"%s\"\n", name);


	ast_mutex_lock(&peerl.lock);
	for (peer = peerl.peers; peer; peer = peer->next) {
		if (gH323Debug) {
			ast_verbose("		comparing with \"%s\"\n", peer->ip);
		}
		if (!strcasecmp(peer->name, name))
			break;
		if (peer->h323id && !strcasecmp(peer->h323id, name))
			break;
		if (peer->e164 && !strcasecmp(peer->e164, name))
			break;
		/*
		if (!strcmp(peer->ip, name)) {
			if (port > 0 && peer->port == port) { break; }
			else if (port <= 0) { break; }
		}
		*/
	}
	ast_mutex_unlock(&peerl.lock);

	if (gH323Debug) {
		if (peer) {
			ast_verbose("		found matching peer\n");
		}
		ast_verbose("+++   find_peer \"%s\"\n", name);
	}

	return peer;		
}

static int ooh323_digit_begin(struct ast_channel *chan, char digit)
{
	char dtmf[2];
	struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt;
	int res = 0;
	
	if (gH323Debug)
		ast_verbose("---   ooh323_digit_begin\n");

	if (!p) {
		ast_log(LOG_ERROR, "No private structure for call\n");
		return -1;
	}
	ast_mutex_lock(&p->lock);


	if (digit == 'e' && !p->faxmode && p->t38support != T38_DISABLED)  {
		if (!p->chmodepend) {
			if (gH323Debug)
				ast_verbose("request to change %s to t.38 because fax cng\n",
						p->callToken);
			p->chmodepend = 1;
			ooRequestChangeMode(p->callToken, 1);
		}

	} else if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO))) {
		ast_rtp_instance_dtmf_begin(p->rtp, digit);
	} else if (((p->dtmfmode & H323_DTMF_Q931) ||
						 (p->dtmfmode & H323_DTMF_H245ALPHANUMERIC) ||
						 (p->dtmfmode & H323_DTMF_H245SIGNAL))) {
		dtmf[0] = digit;
		dtmf[1] = '\0';
		ooSendDTMFDigit(p->callToken, dtmf);
	} else if (p->dtmfmode & H323_DTMF_INBAND) {
		res = -1; // tell Asterisk to generate inband indications
	}
	ast_mutex_unlock(&p->lock);
	if (gH323Debug) {
		ast_verbose("+++   ooh323_digit_begin %d\n", res);
	}

	return res;
}

static int ooh323_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
{
	struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt;
	int res = 0;

	if (gH323Debug)
		ast_verbose("---   ooh323_digit_end\n");

	if (!p) {
		ast_log(LOG_ERROR, "No private structure for call\n");
		return -1;
	}
	ast_mutex_lock(&p->lock);
	if (p->rtp && ((p->dtmfmode & H323_DTMF_RFC2833) || (p->dtmfmode & H323_DTMF_CISCO)) ) {
		ast_rtp_instance_dtmf_end(p->rtp, digit);
	} else if(p->dtmfmode & H323_DTMF_INBAND) {
		res = -1; // tell Asterisk to stop inband indications
	}

	ast_mutex_unlock(&p->lock);
	if (gH323Debug) {
		ast_verbose("+++   ooh323_digit_end, res = %d\n", res);
	}

	return res;
}


static int ooh323_call(struct ast_channel *ast, char *dest, int timeout)
{
	struct ooh323_pvt *p = ast->tech_pvt;
	char destination[256];
   	int res=0, i;
	const char *val = NULL;
	ooCallOptions opts = {
		.fastStart = TRUE,
		.tunneling = TRUE,
		.disableGk = TRUE,
      		.callMode = OO_CALLMODE_AUDIOCALL,
      		.transfercap = 0
	};

	if (gH323Debug)
		ast_verbose("---   ooh323_call- %s\n", dest);


   	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
		ast_log(LOG_WARNING, "ooh323_call called on %s, neither down nor "
								"reserved\n", ast->name);
		return -1;
	}
	ast_mutex_lock(&p->lock);
	ast_set_flag(p, H323_OUTGOING);
	if (ast->connected.id.number.valid && ast->connected.id.number.str) {
		free(p->callerid_num);
		p->callerid_num = strdup(ast->connected.id.number.str);
	}

	if (ast->connected.id.name.valid && ast->connected.id.name.str) {
		free(p->callerid_name);
		p->callerid_name = strdup(ast->connected.id.name.str);
	} else if (ast->connected.id.number.valid && ast->connected.id.number.str) {
		free(p->callerid_name);
		p->callerid_name = strdup(ast->connected.id.number.str);
	} else {
		ast->connected.id.name.valid = 1;
		free(ast->connected.id.name.str);
		ast->connected.id.name.str = strdup(gCallerID);
		free(p->callerid_name);
		p->callerid_name = strdup(ast->connected.id.name.str);
	}

	/* Retrieve vars */


	if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323ID"))) {
		ast_copy_string(p->caller_h323id, val, sizeof(p->caller_h323id));
	}
	
	if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323DIALEDDIGITS"))) {
		ast_copy_string(p->caller_dialedDigits, val, sizeof(p->caller_dialedDigits));
      		if(!p->callerid_num)
			p->callerid_num = strdup(val);
	}

	if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323EMAIL"))) {
		ast_copy_string(p->caller_email, val, sizeof(p->caller_email));
	}

	if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323URL"))) {
		ast_copy_string(p->caller_url, val, sizeof(p->caller_url));
	}

	if (p->host && p->port != 0)
		snprintf(destination, sizeof(destination), "%s:%d", p->host, p->port);
	else if (p->host)
		snprintf(destination, sizeof(destination), "%s", p->host);
	else
		ast_copy_string(destination, dest, sizeof(destination));

	destination[sizeof(destination)-1]='\0';

	opts.transfercap = ast->transfercapability;
	opts.fastStart = p->faststart;
	opts.tunneling = p->h245tunneling;

	for (i=0;i<480 && !isRunning(p->callToken);i++) usleep(12000);

	if(OO_TESTFLAG(p->flags, H323_DISABLEGK)) {
		res = ooRunCall(destination, p->callToken, AST_MAX_EXTENSION, &opts);
	} else {
		res = ooRunCall(destination, p->callToken, AST_MAX_EXTENSION, NULL);
 	}

	ast_mutex_unlock(&p->lock);
	if (res != OO_OK) {
		ast_log(LOG_ERROR, "Failed to make call\n");
      		return -1; /* ToDO: cleanup */
	}
	if (gH323Debug)
		ast_verbose("+++   ooh323_call\n");

  return 0;
}

static int ooh323_hangup(struct ast_channel *ast)
{
	struct ooh323_pvt *p = ast->tech_pvt;
   	int q931cause = AST_CAUSE_NORMAL_CLEARING;

	if (gH323Debug)
		ast_verbose("---   ooh323_hangup\n");

	if (p) {
		ast_mutex_lock(&p->lock);

        if (ast->hangupcause) {
                q931cause = ast->hangupcause;
        } else {
                const char *cause = pbx_builtin_getvar_helper(ast, "DIALSTATUS");
                if (cause) {
                        if (!strcmp(cause, "CONGESTION")) {
                                q931cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
                        } else if (!strcmp(cause, "BUSY")) {
                                q931cause = AST_CAUSE_USER_BUSY;
                        } else if (!strcmp(cause, "CHANISUNVAIL")) {
                                q931cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
                        } else if (!strcmp(cause, "NOANSWER")) {
                                q931cause = AST_CAUSE_NO_ANSWER;
                        } else if (!strcmp(cause, "CANCEL")) {
                                q931cause = AST_CAUSE_CALL_REJECTED;
                        }
                }
        }



		if (gH323Debug)
			ast_verbose("    hanging %s with cause: %d\n", p->username, q931cause);
		ast->tech_pvt = NULL; 
		if (!ast_test_flag(p, H323_ALREADYGONE)) {
         		ooHangCall(p->callToken, 
				ooh323_convert_hangupcause_asteriskToH323(q931cause), q931cause);
			ast_set_flag(p, H323_ALREADYGONE);
			/* ast_mutex_unlock(&p->lock); */
      		} else 
			ast_set_flag(p, H323_NEEDDESTROY);
		/* detach channel here */
		if (p->owner) {
			p->owner->tech_pvt = NULL;
			p->owner = NULL;
			ast_module_unref(myself);
		}

		ast_mutex_unlock(&p->lock);
		ast_mutex_lock(&usecnt_lock);
		usecnt--;
		ast_mutex_unlock(&usecnt_lock);

		/* Notify the module monitors that use count for resource has changed */
		ast_update_use_count();
	  
	} else {
		ast_debug(1, "No call to hangup\n" );
	}
	
	if (gH323Debug)
		ast_verbose("+++   ooh323_hangup\n");

  return 0;
}

static int ooh323_answer(struct ast_channel *ast)
{
	struct ooh323_pvt *p = ast->tech_pvt;
	char *callToken = (char *)NULL;

	if (gH323Debug)
		ast_verbose("--- ooh323_answer\n");

	if (p) {

		ast_mutex_lock(&p->lock);
		callToken = (p->callToken ? strdup(p->callToken) : NULL);
		if (ast->_state != AST_STATE_UP) {
			ast_channel_lock(ast);
			if (!p->alertsent) {
	    			if (gH323Debug) {
					ast_debug(1, "Sending forced ringback for %s, res = %d\n", 
						callToken, ooManualRingback(callToken));
				} else {
	    				ooManualRingback(callToken);
				}
				p->alertsent = 1;
			}
			ast_setstate(ast, AST_STATE_UP);
      			if (option_debug)
				ast_debug(1, "ooh323_answer(%s)\n", ast->name);
			ast_channel_unlock(ast);
			ooAnswerCall(p->callToken);
		}
		if (callToken) {
			free(callToken);
		}
		ast_mutex_unlock(&p->lock);
	}

	if (gH323Debug)
		ast_verbose("+++ ooh323_answer\n");

  return 0;
}

static struct ast_frame *ooh323_read(struct ast_channel *ast)
{
	struct ast_frame *fr;
	static struct ast_frame null_frame = { AST_FRAME_NULL, };
	struct ooh323_pvt *p = ast->tech_pvt;

	if (!p) return &null_frame;

	ast_mutex_lock(&p->lock);
	if (p->rtp)
		fr = ooh323_rtp_read(ast, p);
	else
		fr = &null_frame;
	/* time(&p->lastrtprx); */
	ast_mutex_unlock(&p->lock);
	return fr;
}

static int ooh323_write(struct ast_channel *ast, struct ast_frame *f)
{
	struct ooh323_pvt *p = ast->tech_pvt;
	int res = 0;
	char buf[256];

	if (p) {
		ast_mutex_lock(&p->lock);

		p->lastrtptx = time(NULL);

		if (f->frametype == AST_FRAME_MODEM) {
			ast_debug(1, "Send UDPTL %d/%d len %d for %s\n",
				f->frametype, f->subclass.integer, f->datalen, ast->name);
			if (p->udptl)
				res = ast_udptl_write(p->udptl, f);
			ast_mutex_unlock(&p->lock);
			return res;
		}

	
		if (f->frametype == AST_FRAME_VOICE) {
/* sending progress for first */
			if (!ast_test_flag(p, H323_OUTGOING) && !p->progsent &&
			 		p->callToken) {
				ooManualProgress(p->callToken);
				p->progsent = 1;
			}


			if (!(f->subclass.codec & ast->nativeformats)) {
				if (ast->nativeformats != 0) {
					ast_log(LOG_WARNING,
							"Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
							ast_getformatname(f->subclass.codec),
							ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
							ast_getformatname(ast->readformat),
							ast_getformatname(ast->writeformat));

					ast_set_write_format(ast, f->subclass.codec);
				} else {
					/* ast_set_write_format(ast, f->subclass);
					ast->nativeformats = f->subclass; */
				}
			ast_mutex_unlock(&p->lock);
			return 0;
			}

		if (p->rtp)
			res = ast_rtp_instance_write(p->rtp, f);

		ast_mutex_unlock(&p->lock);

		} else if (f->frametype == AST_FRAME_IMAGE) {
			ast_mutex_unlock(&p->lock);
			return 0;
		} else {
			ast_log(LOG_WARNING, "Can't send %d type frames with OOH323 write\n", 
									 f->frametype);
			ast_mutex_unlock(&p->lock);
			return 0;
		}

	}

	return res;
}

static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
{

	struct ooh323_pvt *p = (struct ooh323_pvt *) ast->tech_pvt;
	char *callToken = (char *)NULL;
	int res = -1;

	if (!p) return -1;

	ast_mutex_lock(&p->lock);
	callToken = (p->callToken ? strdup(p->callToken) : NULL);
	ast_mutex_unlock(&p->lock);

	if (!callToken) {
		if (gH323Debug)
			ast_verbose("	ooh323_indicate - No callToken\n");
		return -1;
	}

	if (gH323Debug)
		ast_verbose("----- ooh323_indicate %d on call %s\n", condition, callToken);
	 
   	ast_mutex_lock(&p->lock);
	switch (condition) {
	case AST_CONTROL_INCOMPLETE:
		/* While h323 does support overlapped dialing, this channel driver does not
		 * at this time.  Treat a response of Incomplete as if it were congestion.
		 */
	case AST_CONTROL_CONGESTION:
		if (!ast_test_flag(p, H323_ALREADYGONE)) {
			ooHangCall(callToken, OO_REASON_LOCAL_CONGESTED, AST_CAUSE_SWITCH_CONGESTION);
		}
		break;
	case AST_CONTROL_BUSY:
		if (!ast_test_flag(p, H323_ALREADYGONE)) {
			ooHangCall(callToken, OO_REASON_LOCAL_BUSY, AST_CAUSE_USER_BUSY);
		}
		break;
	case AST_CONTROL_HOLD:
		ast_moh_start(ast, data, NULL);
		break;
	case AST_CONTROL_UNHOLD:
		ast_moh_stop(ast);
		break;
	case AST_CONTROL_PROGRESS:
		if (ast->_state != AST_STATE_UP) {
	    		if (!p->progsent) {
	     			if (gH323Debug) 
					ast_log(LOG_DEBUG,"Sending manual progress for %s, res = %d\n", callToken,
             				ooManualProgress(callToken));	
				else
	     				ooManualProgress(callToken);
	     			p->progsent = 1;
	    		}
		}
	    break;
      case AST_CONTROL_RINGING:
	    if (ast->_state == AST_STATE_RING || ast->_state == AST_STATE_RINGING) {
		if (!p->alertsent) {
	    		if (gH323Debug) {
				ast_debug(1, "Sending manual ringback for %s, res = %d\n", 
					callToken,
            				ooManualRingback(callToken));
			} else {
	    				ooManualRingback(callToken);
			}
			p->alertsent = 1;
		}
	    }
	 break;
	case AST_CONTROL_SRCUPDATE:
		if (p->rtp) {
			ast_rtp_instance_update_source(p->rtp);
		}
		break;
	case AST_CONTROL_SRCCHANGE:
		if (p->rtp) {
			ast_rtp_instance_change_source(p->rtp);
		}
		break;
	case AST_CONTROL_CONNECTED_LINE:
		if (!ast->connected.id.name.valid
			|| ast_strlen_zero(ast->connected.id.name.str)) {
			break;
		}
		if (gH323Debug)
			ast_log(LOG_DEBUG, "Sending connected line info for %s (%s)\n",
				callToken, ast->connected.id.name.str);
		ooSetANI(callToken, ast->connected.id.name.str);
		break;

      case AST_CONTROL_T38_PARAMETERS:
		if (p->t38support != T38_ENABLED) {
			struct ast_control_t38_parameters parameters = { .request_response = 0 };
			parameters.request_response = AST_T38_REFUSED;
			ast_queue_control_data(ast, AST_CONTROL_T38_PARAMETERS,
						 &parameters, sizeof(parameters));
			break;
		}
		if (datalen != sizeof(struct ast_control_t38_parameters)) {
			ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. "
					   "Expected %d, got %d\n",
				(int)sizeof(enum ast_control_t38), (int)datalen);
		} else {
			const struct ast_control_t38_parameters *parameters = data;
			enum ast_control_t38 message = parameters->request_response;
			switch (message) {

			case AST_T38_REQUEST_NEGOTIATE:

				if (!p->chmodepend && !p->faxmode) {
					ooRequestChangeMode(p->callToken, 1);
					p->chmodepend = 1;
					res = 0;
				}
				break;

			case AST_T38_REQUEST_TERMINATE:

				if (!p->chmodepend && p->faxmode) {
					ooRequestChangeMode(p->callToken, 0);
					p->chmodepend = 1;
					res = 0;
				}
				break;


			default:
				;

			}

		}
		break;
      case AST_CONTROL_PROCEEDING:
	case -1:
		break;
	default:
		ast_log(LOG_WARNING, "Don't know how to indicate condition %d on %s\n",
									condition, callToken);
	}

   	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("++++  ooh323_indicate %d on %s\n", condition, callToken);

   	free(callToken);
	return res;
}

static int ooh323_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
{

	struct ooh323_pvt *p = (struct ooh323_pvt *) ast->tech_pvt;
	int res = -1;
	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
	char* cp;

	if (!p) return -1;

	ast_mutex_lock(&p->lock);

	if (gH323Debug)
		ast_verbose("----- ooh323_queryoption %d on channel %s\n", option, ast->name);
	 
	switch (option) {

		case AST_OPTION_T38_STATE:

			if (*datalen != sizeof(enum ast_t38_state)) {
                        	ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option."
				" Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
				break;
			}
			if (p->t38support != T38_DISABLED)
				state = T38_STATE_UNKNOWN;
			if (p->faxmode)
				state = (p->chmodepend) ? T38_STATE_UNKNOWN : T38_STATE_NEGOTIATED;
			else if (p->chmodepend)
				state = T38_STATE_NEGOTIATING;


			*((enum ast_t38_state *) data) = state;
			res = 0;
			break;


		case AST_OPTION_DIGIT_DETECT:

			cp = (char *) data;
			*cp = p->vad ? 1 : 0;
			ast_debug(1, "Reporting digit detection %sabled on %s\n",
							 *cp ? "en" : "dis", ast->name);

			res = 0;
			break;

		default:	;

	}

	if (gH323Debug)
		ast_verbose("+++++ ooh323_queryoption %d on channel %s\n", option, ast->name);
	 
   	ast_mutex_unlock(&p->lock);

	return res;
}



static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
	struct ooh323_pvt *p = newchan->tech_pvt;

	if (!p) return -1;

	if (gH323Debug)
		ast_verbose("--- ooh323c ooh323_fixup\n");

	ast_mutex_lock(&p->lock);
	if (p->owner != oldchan) {
		ast_log(LOG_WARNING, "Old channel wasn't %p but was %p\n", oldchan, p->owner);
		ast_mutex_unlock(&p->lock);
		return -1;
	}

	if (p->owner == oldchan) {
		p->owner = newchan;
	} else {
		p->owner = oldchan;
	}

	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++ ooh323c ooh323_fixup \n");

	return 0;
}


void ooh323_set_write_format(ooCallData *call, int fmt, int txframes)
{
	struct ooh323_pvt *p = NULL;
	char formats[FORMAT_STRING_SIZE];

	if (gH323Debug)
		ast_verbose("---   ooh323_update_writeformat %s/%d\n", 
				ast_getformatname_multiple(formats,FORMAT_STRING_SIZE, fmt), txframes);
	
	p = find_call(call);
	if (!p) {
		ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken);
		return;
	}

	ast_mutex_lock(&p->lock);

	p->writeformat = fmt;

	if (p->owner) {
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return;
		}
		if (gH323Debug)
	  		ast_verbose("Writeformat before update %s/%s\n", 
			  ast_getformatname_multiple(formats,FORMAT_STRING_SIZE, p->owner->writeformat),
			  ast_getformatname_multiple(formats,FORMAT_STRING_SIZE, p->owner->nativeformats));
		if (txframes)
			ast_codec_pref_setsize(&p->prefs, fmt, txframes);
		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs);
		if (p->dtmfmode & H323_DTMF_RFC2833 && p->dtmfcodec) {
			ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp),
				 p->rtp, p->dtmfcodec, "audio", "telephone-event", 0);
		}
		if (p->dtmfmode & H323_DTMF_CISCO && p->dtmfcodec) {
			ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp),
				 p->rtp, p->dtmfcodec, "audio", "cisco-telephone-event", 0);
		}

		p->owner->nativeformats = fmt;
	  	ast_set_write_format(p->owner, p->owner->writeformat);
	  	ast_set_read_format(p->owner, p->owner->readformat);
		ast_channel_unlock(p->owner);
   	} else
		ast_log(LOG_ERROR, "No owner found\n");


	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++   ooh323_update_writeformat\n");
}

void ooh323_set_read_format(ooCallData *call, int fmt)
{
	struct ooh323_pvt *p = NULL;
	char formats[FORMAT_STRING_SIZE];

	if (gH323Debug)
		ast_verbose("---   ooh323_update_readformat %s\n", 
				ast_getformatname_multiple(formats,FORMAT_STRING_SIZE, fmt));
	
	p = find_call(call);
	if (!p) {
		ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken);
		return;
	}

	ast_mutex_lock(&p->lock);

	p->readformat = fmt;


	if (p->owner) {
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return;
		}

		if (gH323Debug)
	  		ast_verbose("Readformat before update %s\n", 
				  ast_getformatname_multiple(formats,FORMAT_STRING_SIZE, p->owner->readformat));
		p->owner->nativeformats = fmt;
	  	ast_set_read_format(p->owner, p->owner->readformat);
		ast_channel_unlock(p->owner);
   	} else
		ast_log(LOG_ERROR, "No owner found\n");

	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++   ooh323_update_readformat\n");
}


int onAlerting(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;
	struct ast_channel *c = NULL;

	if (gH323Debug)
		ast_verbose("--- onAlerting %s\n", call->callToken);

   	p = find_call(call);

   	if(!p) {
		ast_log(LOG_ERROR, "No matching call found\n");
		return -1;
	}  
	ast_mutex_lock(&p->lock);
	if (!p->owner) {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return 0;
	}
	while (p->owner && ast_channel_trylock(p->owner)) {
		ast_debug(1,"Failed to grab lock, trying again\n");
		DEADLOCK_AVOIDANCE(&p->lock);
	}
	if (!p->owner) {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return 0;
	}
	c = p->owner;

	if (call->remoteDisplayName) {
		struct ast_party_connected_line connected;
		struct ast_set_party_connected_line update_connected;

		memset(&update_connected, 0, sizeof(update_connected));
		update_connected.id.name = 1;
		ast_party_connected_line_init(&connected);
		connected.id.name.valid = 1;
		connected.id.name.str = (char *) call->remoteDisplayName;
		connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
		ast_channel_queue_connected_line_update(c, &connected, &update_connected);
	}
	if (c->_state != AST_STATE_UP)
		ast_setstate(c, AST_STATE_RINGING);

	ast_queue_control(c, AST_CONTROL_RINGING);
      	ast_channel_unlock(c);
      	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++ onAlerting %s\n", call->callToken);

	return OO_OK;
}

int onProgress(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;
	struct ast_channel *c = NULL;

	if (gH323Debug)
		ast_verbose("--- onProgress %s\n", call->callToken);

   	p = find_call(call);

   	if(!p) {
		ast_log(LOG_ERROR, "No matching call found\n");
		return -1;
	}  
	ast_mutex_lock(&p->lock);
	if (!p->owner) {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return 0;
	}
	while (p->owner && ast_channel_trylock(p->owner)) {
		ast_debug(1,"Failed to grab lock, trying again\n");
		DEADLOCK_AVOIDANCE(&p->lock);
	}
	if (!p->owner) {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return 0;
	}
	c = p->owner;

	if (call->remoteDisplayName) {
		struct ast_party_connected_line connected;
		struct ast_set_party_connected_line update_connected;

		memset(&update_connected, 0, sizeof(update_connected));
		update_connected.id.name = 1;
		ast_party_connected_line_init(&connected);
		connected.id.name.valid = 1;
		connected.id.name.str = (char *) call->remoteDisplayName;
		connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
		ast_channel_queue_connected_line_update(c, &connected, &update_connected);
	}
	if (c->_state != AST_STATE_UP)
		ast_setstate(c, AST_STATE_RINGING);

	ast_queue_control(c, AST_CONTROL_PROGRESS);
      	ast_channel_unlock(c);
      	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++ onProgress %s\n", call->callToken);

	return OO_OK;
}

/**
  * Callback for sending digits from H.323 up to asterisk
  *
  */
int ooh323_onReceivedDigit(OOH323CallData *call, const char *digit)
{
	struct ooh323_pvt *p = NULL;
	struct ast_frame f;
	int res;

	ast_debug(1, "Received Digit: %c\n", digit[0]);
	p = find_call(call);
	if (!p) {
		ast_log(LOG_ERROR, "Failed to find a matching call.\n");
		return -1;
	}
	if (!p->owner) {
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return -1;
	}
	ast_mutex_lock(&p->lock);
	memset(&f, 0, sizeof(f));
	f.frametype = AST_FRAME_DTMF;
	f.subclass.integer = digit[0];
	f.datalen = 0;
	f.samples = 800;
	f.offset = 0;
	f.data.ptr = NULL;
	f.mallocd = 0;
	f.src = "SEND_DIGIT";

	while (p->owner && ast_channel_trylock(p->owner)) {
		ast_debug(1,"Failed to grab lock, trying again\n");
		DEADLOCK_AVOIDANCE(&p->lock);
	}
	if (!p->owner) {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return 0;
	}
	res = ast_queue_frame(p->owner, &f);
   	ast_channel_unlock(p->owner);
   	ast_mutex_unlock(&p->lock);
	return res;
}

int ooh323_onReceivedSetup(ooCallData *call, Q931Message *pmsg)
{
	struct ooh323_pvt *p = NULL;
	struct ooh323_user *user = NULL;
   	struct ast_channel *c = NULL;
	ooAliases *alias = NULL;
	char *at = NULL;
	char number [OO_MAX_NUMBER_LENGTH];

	if (gH323Debug)
		ast_verbose("---   ooh323_onReceivedSetup %s\n", call->callToken);


	if (!(p = ooh323_alloc(call->callReference, call->callToken))) {
		ast_log(LOG_ERROR, "Failed to create a new call.\n");
		return -1;
	}
	ast_mutex_lock(&p->lock);
	ast_clear_flag(p, H323_OUTGOING);
  

	if (call->remoteDisplayName) {
		p->callerid_name = strdup(call->remoteDisplayName);
	}

	if (ooCallGetCallingPartyNumber(call, number, OO_MAX_NUMBER_LENGTH) == OO_OK) {
		p->callerid_num = strdup(number);
	}

	if (call->remoteAliases) {
		for (alias = call->remoteAliases; alias; alias = alias->next) {
			if (alias->type == T_H225AliasAddress_h323_ID) {
				if (!p->callerid_name) {
					p->callerid_name = strdup(alias->value);
				}
				ast_copy_string(p->caller_h323id, alias->value, sizeof(p->caller_h323id));
				}
         else if(alias->type == T_H225AliasAddress_dialedDigits)
         {
            if(!p->callerid_num)
               p->callerid_num = strdup(alias->value);
				ast_copy_string(p->caller_dialedDigits, alias->value, 
															sizeof(p->caller_dialedDigits));
         }
         else if(alias->type == T_H225AliasAddress_email_ID)
         {
				ast_copy_string(p->caller_email, alias->value, sizeof(p->caller_email));
         }
         else if(alias->type == T_H225AliasAddress_url_ID)
         {
				ast_copy_string(p->caller_url, alias->value, sizeof(p->caller_url));
			}
		}
	}

	number[0] = '\0';
   	if(ooCallGetCalledPartyNumber(call, number, OO_MAX_NUMBER_LENGTH)== OO_OK) {
      		strncpy(p->exten, number, sizeof(p->exten)-1);
   	} else {
		update_our_aliases(call, p);
		if (!ast_strlen_zero(p->callee_dialedDigits)) {
         		ast_copy_string(p->exten, p->callee_dialedDigits, sizeof(p->exten));
      		} else if(!ast_strlen_zero(p->callee_h323id)) {
			ast_copy_string(p->exten, p->callee_h323id, sizeof(p->exten));
      		} else if(!ast_strlen_zero(p->callee_email)) {
			ast_copy_string(p->exten, p->callee_email, sizeof(p->exten));
			if ((at = strchr(p->exten, '@'))) {
				*at = '\0';
			}
		}
	}

	/* if no extension found, set to default 's' */
	if (ast_strlen_zero(p->exten)) {
      		p->exten[0]='s';
      		p->exten[1]='\0';
	}

      	user = find_user(p->callerid_name, call->remoteIP);
      	if(user && (user->incominglimit == 0 || user->inUse < user->incominglimit)) {
		ast_mutex_lock(&user->lock);
		p->username = strdup(user->name);
 		p->neighbor.user = user->mUseIP ? ast_strdup(user->mIP) :
						  ast_strdup(user->name);
		ast_copy_string(p->context, user->context, sizeof(p->context));
		ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode));
		p->amaflags = user->amaflags;
		p->capability = user->capability;
		p->g729onlyA = user->g729onlyA;
		memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref));
		p->dtmfmode |= user->dtmfmode;
		p->dtmfcodec = user->dtmfcodec;
		p->t38support = user->t38support;
		p->rtptimeout = user->rtptimeout;
		p->h245tunneling = user->h245tunneling;
		p->faststart = user->faststart;

		if (p->faststart)
         		OO_SETFLAG(call->flags, OO_M_FASTSTART);
		else
			OO_CLRFLAG(call->flags, OO_M_FASTSTART);
		/* if we disable h245tun for this user then we clear flag */
		/* in any other case we don't must touch this */
		/* ie if we receive setup without h245tun but enabled
		   				we can't enable it per call */
		if (!p->h245tunneling)
			OO_CLRFLAG(call->flags, OO_M_TUNNELING);

		if (user->rtpmask && user->rtpmaskstr[0]) {
			p->rtpmask = user->rtpmask;
			ast_copy_string(p->rtpmaskstr, user->rtpmaskstr, 
							 sizeof(p->rtpmaskstr));
		}
		if (user->rtdrcount > 0 && user->rtdrinterval > 0) {
			p->rtdrcount = user->rtdrcount;
			p->rtdrinterval = user->rtdrinterval;
		}
	 	if (user->incominglimit) user->inUse++;
		ast_mutex_unlock(&user->lock);
	} else {
	 if (!OO_TESTFLAG(p->flags,H323_DISABLEGK)) {
		p->username = strdup(call->remoteIP);
	} else {
	  ast_mutex_unlock(&p->lock);
	  ast_log(LOG_ERROR, "Unacceptable ip %s\n", call->remoteIP);
	  if (!user) 
	   ooHangCall(call->callToken, ooh323_convert_hangupcause_asteriskToH323(AST_CAUSE_CALL_REJECTED), AST_CAUSE_CALL_REJECTED);
	  else
	   ooHangCall(call->callToken, ooh323_convert_hangupcause_asteriskToH323(AST_CAUSE_NORMAL_CIRCUIT_CONGESTION), AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
	  ast_set_flag(p, H323_NEEDDESTROY);
	  return -1;
	 }
	}

	ooh323c_set_capability_for_call(call, &p->prefs, p->capability, p->dtmfmode, p->dtmfcodec,
					 p->t38support, p->g729onlyA);
	configure_local_rtp(p, call);

/* Incoming call */
  	c = ooh323_new(p, AST_STATE_RING, p->username, 0, NULL);
  	if(!c) {
   	ast_mutex_unlock(&p->lock);
   	ast_log(LOG_ERROR, "Could not create ast_channel\n");
         return -1;
  }
	ast_mutex_unlock(&p->lock);

	if (gH323Debug)
		ast_verbose("+++   ooh323_onReceivedSetup - Determined context %s, "
						"extension %s\n", p->context, p->exten);

	return OO_OK;
}



int onOutgoingCall(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;
	int i = 0;

	if (gH323Debug)
		ast_verbose("---   onOutgoingCall %lx: %s\n", (long unsigned int) call, call->callToken);

	if (!strcmp(call->callType, "outgoing")) {
		p = find_call(call);
		if (!p) {
      			ast_log(LOG_ERROR, "Failed to find a matching call.\n");
			return -1;
		}
		ast_mutex_lock(&p->lock);

		if (!ast_strlen_zero(p->callerid_name)) {
			ooCallSetCallerId(call, p->callerid_name);
		}
		if (!ast_strlen_zero(p->callerid_num)) {
			i = 0;
			while (*(p->callerid_num + i) != '\0') {
            			if(!isdigit(*(p->callerid_num+i))) { break; }
				i++;
			}
         		if(*(p->callerid_num+i) == '\0')
				ooCallSetCallingPartyNumber(call, p->callerid_num);
         		else {
            			if(!p->callerid_name)
					ooCallSetCallerId(call, p->callerid_num);
			}
		}
		
		if (!ast_strlen_zero(p->caller_h323id))
			ooCallAddAliasH323ID(call, p->caller_h323id);

		if (!ast_strlen_zero(p->caller_dialedDigits)) {
			if (gH323Debug) {
				ast_verbose("Setting dialed digits %s\n", p->caller_dialedDigits);
			}
			ooCallAddAliasDialedDigits(call, p->caller_dialedDigits);
		} else if (!ast_strlen_zero(p->callerid_num)) {
			if (ooIsDailedDigit(p->callerid_num)) {
				if (gH323Debug) {
					ast_verbose("setting callid number %s\n", p->callerid_num);
				}
				ooCallAddAliasDialedDigits(call, p->callerid_num);
			} else if (ast_strlen_zero(p->caller_h323id)) {
				ooCallAddAliasH323ID(call, p->callerid_num);
			}
		}
		if (p->rtpmask && p->rtpmaskstr[0]) {
			call->rtpMask = p->rtpmask;
			ast_mutex_lock(&call->rtpMask->lock);
			call->rtpMask->inuse++;
			ast_mutex_unlock(&call->rtpMask->lock);
			ast_copy_string(call->rtpMaskStr, p->rtpmaskstr, sizeof(call->rtpMaskStr));
		}

		ast_mutex_unlock(&p->lock);
	}

	if (gH323Debug)
		ast_verbose("+++   onOutgoingCall %s\n", call->callToken);
	return OO_OK;
}


int onNewCallCreated(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;
	int i = 0;

	if (gH323Debug)
		ast_verbose("---   onNewCallCreated %lx: %s\n", (long unsigned int) call, call->callToken);

   	ast_mutex_lock(&call->Lock);
   	if (ooh323c_start_call_thread(call)) {
    		ast_log(LOG_ERROR,"Failed to create call thread.\n");
    		ast_mutex_unlock(&call->Lock);
    		return -1;
   	}

	if (!strcmp(call->callType, "outgoing")) {
		p = find_call(call);
		if (!p) {
      			ast_log(LOG_ERROR, "Failed to find a matching call.\n");
			ast_mutex_unlock(&call->Lock);
			return -1;
		}
		ast_mutex_lock(&p->lock);

		if (!ast_strlen_zero(p->callerid_name)) {
			ooCallSetCallerId(call, p->callerid_name);
		}
		if (!ast_strlen_zero(p->callerid_num)) {
			i = 0;
			while (*(p->callerid_num + i) != '\0') {
            			if(!isdigit(*(p->callerid_num+i))) { break; }
				i++;
			}
         		if(*(p->callerid_num+i) == '\0')
				ooCallSetCallingPartyNumber(call, p->callerid_num);
         		else {
            			if(ast_strlen_zero(p->callerid_name))
					ooCallSetCallerId(call, p->callerid_num);
			}
		}
		
		if (!ast_strlen_zero(p->caller_h323id))
			ooCallAddAliasH323ID(call, p->caller_h323id);

		if (!ast_strlen_zero(p->caller_dialedDigits)) {
			if (gH323Debug) {
				ast_verbose("Setting dialed digits %s\n", p->caller_dialedDigits);
			}
			ooCallAddAliasDialedDigits(call, p->caller_dialedDigits);
		} else if (!ast_strlen_zero(p->callerid_num)) {
			if (ooIsDailedDigit(p->callerid_num)) {
				if (gH323Debug) {
					ast_verbose("setting callid number %s\n", p->callerid_num);
				}
				ooCallAddAliasDialedDigits(call, p->callerid_num);
			} else if (ast_strlen_zero(p->caller_h323id)) {
				ooCallAddAliasH323ID(call, p->callerid_num);
			}
		}
  

		if (!ast_strlen_zero(p->exten))  {
			if (ooIsDailedDigit(p->exten)) {
				ooCallSetCalledPartyNumber(call, p->exten);
				ooCallAddRemoteAliasDialedDigits(call, p->exten);
			} else {
			  ooCallAddRemoteAliasH323ID(call, p->exten);
			}
		}

		if (gH323Debug) {
			char prefsBuf[256];
			ast_codec_pref_string(&p->prefs, prefsBuf, sizeof(prefsBuf));
			ast_verbose(" Outgoing call %s(%s) - Codec prefs - %s\n", 
				p->username?p->username:"NULL", call->callToken, prefsBuf);
		}

      		ooh323c_set_capability_for_call(call, &p->prefs, p->capability, 
                                     p->dtmfmode, p->dtmfcodec, p->t38support, p->g729onlyA);

		configure_local_rtp(p, call);
		ast_mutex_unlock(&p->lock);
	}

   	ast_mutex_unlock(&call->Lock);
	if (gH323Debug)
		ast_verbose("+++   onNewCallCreated %s\n", call->callToken);
	return OO_OK;
}

int onCallEstablished(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;

	if (gH323Debug)
		ast_verbose("---   onCallEstablished %s\n", call->callToken);


	if (!(p = find_call(call))) {
		ast_log(LOG_ERROR, "Failed to find a matching call.\n");
		return -1;
	}

   	if(ast_test_flag(p, H323_OUTGOING)) {
		ast_mutex_lock(&p->lock);
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return -1;
		}
	
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (p->owner) {
			struct ast_channel* c = p->owner;

			if (call->remoteDisplayName) {
				struct ast_party_connected_line connected;
				struct ast_set_party_connected_line update_connected;

				memset(&update_connected, 0, sizeof(update_connected));
				update_connected.id.name = 1;
				ast_party_connected_line_init(&connected);
				connected.id.name.valid = 1;
				connected.id.name.str = (char *) call->remoteDisplayName;
				connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
				ast_channel_queue_connected_line_update(c, &connected, &update_connected);
			}

			ast_queue_control(c, AST_CONTROL_ANSWER);
   			ast_channel_unlock(p->owner);
			manager_event(EVENT_FLAG_SYSTEM,"ChannelUpdate","Channel: %s\r\nChanneltype: %s\r\n"
				"CallRef: %d\r\n", c->name, "OOH323", p->call_reference);
		}
		ast_mutex_unlock(&p->lock);

	}

	if (gH323Debug)
		ast_verbose("+++   onCallEstablished %s\n", call->callToken);

	return OO_OK;
}

int onCallCleared(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;
	int ownerLock = 0;

	if (gH323Debug)
		ast_verbose("---   onCallCleared %s \n", call->callToken);


   if ((p = find_call(call))) {
	ast_mutex_lock(&p->lock);
  
	while (p->owner) {
		if (ast_channel_trylock(p->owner)) {
			ooTrace(OOTRCLVLINFO, "Failed to grab lock, trying again\n");
         		ast_log(LOG_DEBUG,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		} else {
         		ownerLock = 1; break;
		}
	}

	if (ownerLock) {
		if (!ast_test_flag(p, H323_ALREADYGONE)) { 

			ast_set_flag(p, H323_ALREADYGONE);
			p->owner->hangupcause = call->q931cause;
			p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
			ast_queue_hangup_with_cause(p->owner,call->q931cause);
		}
   	}

   	if(p->owner) {
    		p->owner->tech_pvt = NULL;
		ast_channel_unlock(p->owner);
    		p->owner = NULL;
		ast_module_unref(myself);
	}

	ast_set_flag(p, H323_NEEDDESTROY);

   	ooh323c_stop_call_thread(call);

	ast_mutex_unlock(&p->lock);
   	ast_mutex_lock(&usecnt_lock);
   	usecnt--;
   	ast_mutex_unlock(&usecnt_lock);

    }

	if (gH323Debug)
		ast_verbose("+++   onCallCleared\n");

	return OO_OK;
}

/* static void ooh323_delete_user(struct ooh323_user *user)
{
	struct ooh323_user *prev = NULL, *cur = NULL;

	if (gH323Debug)
		ast_verbose("---   ooh323_delete_user\n");

	if (user) {	
		cur = userl.users;
		ast_mutex_lock(&userl.lock);
		while (cur) {
			if (cur == user) break;
			prev = cur;
			cur = cur->next;
		}

		if (cur) {
			if (prev)
				prev->next = cur->next;
			else
				userl.users = cur->next;
		}
		ast_mutex_unlock(&userl.lock);

		free(user);
	}  

	if (gH323Debug)
		ast_verbose("+++   ooh323_delete_user\n");

} */

void ooh323_delete_peer(struct ooh323_peer *peer)
{
	struct ooh323_peer *prev = NULL, *cur = NULL;

	if (gH323Debug)
		ast_verbose("---   ooh323_delete_peer\n");

	if (peer) {	
      cur = peerl.peers;
		ast_mutex_lock(&peerl.lock);
      while(cur) {
         if(cur==peer) break;
         prev = cur;
         cur = cur->next;
		}

		if (cur) {
         if(prev)
				prev->next = cur->next;
         else
				peerl.peers = cur->next;
			}
		ast_mutex_unlock(&peerl.lock);

      if(peer->h323id)   free(peer->h323id);
      if(peer->email)    free(peer->email);
      if(peer->url)      free(peer->url);
      if(peer->e164)     free(peer->e164);

		free(peer);
	}  

	if (gH323Debug)
		ast_verbose("+++   ooh323_delete_peer\n");

}



static struct ooh323_user *build_user(const char *name, struct ast_variable *v)
{
	struct ooh323_user *user = NULL;

	if (gH323Debug)
		ast_verbose("---   build_user\n");

   	user = ast_calloc(1,sizeof(struct ooh323_user));
	if (user) {
		memset(user, 0, sizeof(struct ooh323_user));
		ast_mutex_init(&user->lock);
		ast_copy_string(user->name, name, sizeof(user->name));
		user->capability = gCapability;
		memcpy(&user->prefs, &gPrefs, sizeof(user->prefs));
		user->rtptimeout = gRTPTimeout;
		user->dtmfmode = gDTMFMode;
		user->dtmfcodec = gDTMFCodec;
		user->t38support = gT38Support;
		user->faststart = gFastStart;
		user->h245tunneling = gTunneling;
		user->g729onlyA = g729onlyA;
		/* set default context */
		ast_copy_string(user->context, gContext, sizeof(user->context));
		ast_copy_string(user->accountcode, gAccountcode, sizeof(user->accountcode));
		user->amaflags = gAMAFLAGS;

		while (v) {
			if (!strcasecmp(v->name, "context")) {
				ast_copy_string(user->context, v->value, sizeof(user->context));
			} else if (!strcasecmp(v->name, "incominglimit")) {
				user->incominglimit = atoi(v->value);
				if (user->incominglimit < 0)
					user->incominglimit = 0;
			} else if (!strcasecmp(v->name, "accountcode")) {
            			strncpy(user->accountcode, v->value, 
						sizeof(user->accountcode)-1);
			} else if (!strcasecmp(v->name, "roundtrip")) {
				sscanf(v->value, "%d,%d", &user->rtdrcount, &user->rtdrinterval);
			} else if (!strcasecmp(v->name, "faststart")) {
				user->faststart = ast_true(v->value);
			} else if (!strcasecmp(v->name, "h245tunneling")) {
				user->h245tunneling = ast_true(v->value);
			} else if (!strcasecmp(v->name, "g729onlyA")) {
				user->g729onlyA = ast_true(v->value);
			} else if (!strcasecmp(v->name, "rtptimeout")) {
				user->rtptimeout = atoi(v->value);
				if (user->rtptimeout < 0)
					user->rtptimeout = gRTPTimeout;
			} else if (!strcasecmp(v->name, "rtpmask")) {
				if ((user->rtpmask = malloc(sizeof(struct OOH323Regex))) &&
					(regcomp(&user->rtpmask->regex, v->value, REG_EXTENDED) 
											== 0)) {
					ast_mutex_init(&user->rtpmask->lock);
					user->rtpmask->inuse = 1;
					ast_copy_string(user->rtpmaskstr, v->value, 
								sizeof(user->rtpmaskstr));
				} else user->rtpmask = NULL;
			} else if (!strcasecmp(v->name, "disallow")) {
				ast_parse_allow_disallow(&user->prefs, 
					&user->capability,  v->value, 0);
			} else if (!strcasecmp(v->name, "allow")) {
				const char* tcodecs = v->value;
				if (!strcasecmp(v->value, "all")) {
					tcodecs = "ulaw,alaw,g729,g723,gsm";
				}
				ast_parse_allow_disallow(&user->prefs,
					 &user->capability,  tcodecs, 1);
			} else if (!strcasecmp(v->name, "amaflags")) {
				user->amaflags = ast_cdr_amaflags2int(v->value);
			} else if (!strcasecmp(v->name, "ip")) {
            			strncpy(user->mIP, v->value, sizeof(user->mIP)-1);
            			user->mUseIP = 1;
         		} else if (!strcasecmp(v->name, "host")) {
            			strncpy(user->mIP, v->value, sizeof(user->mIP)-1);
				user->mUseIP = 1;
	 		} else if (!strcasecmp(v->name, "dtmfmode")) {
				if (!strcasecmp(v->value, "rfc2833"))
					user->dtmfmode = H323_DTMF_RFC2833;
				if (!strcasecmp(v->value, "cisco"))
					user->dtmfmode = H323_DTMF_CISCO;
				else if (!strcasecmp(v->value, "q931keypad"))
					user->dtmfmode = H323_DTMF_Q931;
				else if (!strcasecmp(v->value, "h245alphanumeric"))
					user->dtmfmode = H323_DTMF_H245ALPHANUMERIC;
				else if (!strcasecmp(v->value, "h245signal"))
					user->dtmfmode = H323_DTMF_H245SIGNAL;
				else if (!strcasecmp(v->value, "inband"))
					user->dtmfmode = H323_DTMF_INBAND;
			} else if (!strcasecmp(v->name, "relaxdtmf")) {
				user->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0;
			} else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) {
				user->dtmfcodec = atoi(v->value);
	 		} else if (!strcasecmp(v->name, "t38support")) {
				if (!strcasecmp(v->value, "disabled"))
					user->t38support = T38_DISABLED;
				if (!strcasecmp(v->value, "no"))
					user->t38support = T38_DISABLED;
				else if (!strcasecmp(v->value, "faxgw"))
					user->t38support = T38_FAXGW;
				else if (!strcasecmp(v->value, "yes"))
					user->t38support = T38_ENABLED;
			}
			v = v->next;
		}
	}

	if (gH323Debug)
		ast_verbose("+++   build_user\n");

	return user;
}

static struct ooh323_peer *build_peer(const char *name, struct ast_variable *v, int friend_type)
{
	struct ooh323_peer *peer = NULL;

	if (gH323Debug)
		ast_verbose("---   build_peer\n");

	peer = ast_calloc(1, sizeof(*peer));
	if (peer) {
		memset(peer, 0, sizeof(struct ooh323_peer));
		ast_mutex_init(&peer->lock);
		ast_copy_string(peer->name, name, sizeof(peer->name));
		peer->capability = gCapability;
      		memcpy(&peer->prefs, &gPrefs, sizeof(peer->prefs));
		peer->rtptimeout = gRTPTimeout;
		ast_copy_string(peer->accountcode, gAccountcode, sizeof(peer->accountcode));
		peer->amaflags = gAMAFLAGS;
		peer->dtmfmode = gDTMFMode;
		peer->dtmfcodec = gDTMFCodec;
		peer->t38support = gT38Support;
		peer->faststart = gFastStart;
		peer->h245tunneling = gTunneling;
		peer->g729onlyA = g729onlyA;
      		peer->port = 1720;
		if (0 == friend_type) {
			peer->mFriend = 1;
		}

		while (v) {
			if (!strcasecmp(v->name, "h323id")) {
	    if (!(peer->h323id = ast_strdup(v->value))) {
					ast_log(LOG_ERROR, "Could not allocate memory for h323id of "
											 "peer %s\n", name);
					ooh323_delete_peer(peer);
					return NULL;
				}
			} else if (!strcasecmp(v->name, "e164")) {
				if (!(peer->e164 = ast_strdup(v->value))) {
					ast_log(LOG_ERROR, "Could not allocate memory for e164 of "
											 "peer %s\n", name);
					ooh323_delete_peer(peer);
					return NULL;
				}
			} else  if (!strcasecmp(v->name, "email")) {
				if (!(peer->email = ast_strdup(v->value))) {
					ast_log(LOG_ERROR, "Could not allocate memory for email of "
											 "peer %s\n", name);
					ooh323_delete_peer(peer);
					return NULL;
				}
			} else if (!strcasecmp(v->name, "url")) {
				if (!(peer->url = ast_strdup(v->value))) {
					ast_log(LOG_ERROR, "Could not allocate memory for h323id of "
											 "peer %s\n", name);
					ooh323_delete_peer(peer);
					return NULL;
				}
			} else if (!strcasecmp(v->name, "port")) {
				peer->port = atoi(v->value);
			} else if (!strcasecmp(v->name, "ip")) {
				ast_copy_string(peer->ip, v->value, sizeof(peer->ip));
         		} else if (!strcasecmp(v->name, "host")) {
            			ast_copy_string(peer->ip, v->value, sizeof(peer->ip));
			} else if (!strcasecmp(v->name, "outgoinglimit")) {
            			peer->outgoinglimit = atoi(v->value);
            			if (peer->outgoinglimit < 0)
					peer->outgoinglimit = 0;
			} else if (!strcasecmp(v->name, "accountcode")) {
				ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode));
			} else if (!strcasecmp(v->name, "faststart")) {
				peer->faststart = ast_true(v->value);
			} else if (!strcasecmp(v->name, "h245tunneling")) {
				peer->h245tunneling = ast_true(v->value);
			} else if (!strcasecmp(v->name, "g729onlyA")) {
				peer->g729onlyA = ast_true(v->value);
			} else if (!strcasecmp(v->name, "rtptimeout")) {
            			peer->rtptimeout = atoi(v->value);
            			if(peer->rtptimeout < 0)
					peer->rtptimeout = gRTPTimeout;
			} else if (!strcasecmp(v->name, "rtpmask")) {
				if ((peer->rtpmask = malloc(sizeof(struct OOH323Regex))) &&
					(regcomp(&peer->rtpmask->regex, v->value, REG_EXTENDED) 
											== 0)) {
					ast_mutex_init(&peer->rtpmask->lock);
					peer->rtpmask->inuse = 1;
					ast_copy_string(peer->rtpmaskstr, v->value, 
								sizeof(peer->rtpmaskstr));
				} else peer->rtpmask = NULL;
			} else if (!strcasecmp(v->name, "disallow")) {
				ast_parse_allow_disallow(&peer->prefs, &peer->capability, 
												 v->value, 0); 
			} else if (!strcasecmp(v->name, "allow")) {
				const char* tcodecs = v->value;
				if (!strcasecmp(v->value, "all")) {
					tcodecs = "ulaw,alaw,g729,g723,gsm";
				}
				ast_parse_allow_disallow(&peer->prefs, &peer->capability, 
												 tcodecs, 1);				 
			} else if (!strcasecmp(v->name,  "amaflags")) {
				peer->amaflags = ast_cdr_amaflags2int(v->value);
			} else if (!strcasecmp(v->name, "roundtrip")) {
				sscanf(v->value, "%d,%d", &peer->rtdrcount, &peer->rtdrinterval);
			} else if (!strcasecmp(v->name, "dtmfmode")) {
				if (!strcasecmp(v->value, "rfc2833"))
					peer->dtmfmode = H323_DTMF_RFC2833;
				if (!strcasecmp(v->value, "cisco"))
					peer->dtmfmode = H323_DTMF_CISCO;
				else if (!strcasecmp(v->value, "q931keypad"))
					peer->dtmfmode = H323_DTMF_Q931;
				else if (!strcasecmp(v->value, "h245alphanumeric"))
					peer->dtmfmode = H323_DTMF_H245ALPHANUMERIC;
				else if (!strcasecmp(v->value, "h245signal"))
					peer->dtmfmode = H323_DTMF_H245SIGNAL;
				else if (!strcasecmp(v->value, "inband"))
					peer->dtmfmode = H323_DTMF_INBAND;
			} else if (!strcasecmp(v->name, "relaxdtmf")) {
				peer->dtmfmode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0;
			} else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) {
				peer->dtmfcodec = atoi(v->value);
	 		} else if (!strcasecmp(v->name, "t38support")) {
				if (!strcasecmp(v->value, "disabled"))
					peer->t38support = T38_DISABLED;
				if (!strcasecmp(v->value, "no"))
					peer->t38support = T38_DISABLED;
				else if (!strcasecmp(v->value, "faxgw"))
					peer->t38support = T38_FAXGW;
				else if (!strcasecmp(v->value, "yes"))
					peer->t38support = T38_ENABLED;
			}
			v = v->next;
		}
	}

	if (gH323Debug)
		ast_verbose("+++   build_peer\n");

	return peer;
}

static int ooh323_do_reload(void)
{
	if (gH323Debug) {
		ast_verbose("---   ooh323_do_reload\n");
	}

   	reload_config(1);

	if (gH323Debug) {
		ast_verbose("+++   ooh323_do_reload\n");
	}

	return 0;
}

/*--- h323_reload: Force reload of module from cli ---*/

char *handle_cli_ooh323_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{

       switch (cmd) {
       case CLI_INIT:
               e->command = "ooh323 reload";
               e->usage =
                       "Usage: ooh323 reload\n"
                       "                Reload OOH323 config.\n";
               return NULL;
       case CLI_GENERATE:
               return NULL;
       }

       if (a->argc != 2)
               return CLI_SHOWUSAGE;

	if (gH323Debug)
		ast_verbose("---   ooh323_reload\n");

	ast_mutex_lock(&h323_reload_lock);
	if (h323_reloading) {
		ast_verbose("Previous OOH323 reload not yet done\n");
   } else {
		h323_reloading = 1;
	}
	ast_mutex_unlock(&h323_reload_lock);
	restart_monitor();

	if (gH323Debug)
		ast_verbose("+++   ooh323_reload\n");

	return 0;
}

int reload_config(int reload)
{
	int format;
	struct ooAliases  *pNewAlias = NULL, *cur, *prev;
	struct ast_config *cfg;
	struct ast_variable *v;
	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
	struct ooh323_user *user = NULL;
	struct ooh323_peer *peer = NULL;
	char *cat;
	const char *utype;

	if (gH323Debug)
		ast_verbose("---   reload_config\n");

	cfg = ast_config_load((char*)config, config_flags);

	/* We *must* have a config file otherwise stop immediately */
	if (!cfg) {
		ast_log(LOG_NOTICE, "Unable to load config %s, OOH323 disabled\n", config);
		return 1;
	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
		return RESULT_SUCCESS;

	if (reload) {
		delete_users();
		delete_peers();
		if (gH323Debug) {
			ast_verbose("  reload_config - Freeing up alias list\n");
		}
		cur = gAliasList;
		while (cur) {
			prev = cur;
	  		cur = cur->next;
	  		free(prev->value);
	  		free(prev);
		}
		gAliasList = NULL;
	}

	/* Inintialize everything to default */
	strcpy(gLogFile, DEFAULT_LOGFILE);
	gPort = 1720;
	gIP[0] = '\0';
	strcpy(gCallerID, DEFAULT_H323ID);
   	gCapability = AST_FORMAT_ALAW;
	memset(&gPrefs, 0, sizeof(struct ast_codec_pref));
	gDTMFMode = H323_DTMF_RFC2833;
	gDTMFCodec = 101;
	gT38Support = T38_FAXGW;
	gTRCLVL = OOTRCLVLERR;
	gRasGkMode = RasNoGatekeeper;
	gGatekeeper[0] = '\0';
	gRTPTimeout = 60;
	gRTDRInterval = 0;
	gRTDRCount = 0;
	strcpy(gAccountcode, DEFAULT_H323ACCNT);
	gFastStart = 1;
	gTunneling = 1;
	gTOS = 0;
	strcpy(gContext, DEFAULT_CONTEXT);
	gAliasList = NULL;
	gMediaWaitForConnect = 0;
	ooconfig.mTCPPortStart = 12030;
	ooconfig.mTCPPortEnd = 12230;
	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));

	v = ast_variable_browse(cfg, "general");
	while (v) {

		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
			v = v->next;
			continue;
		}
	
		if (!strcasecmp(v->name, "port")) {
			gPort = (int)strtol(v->value, NULL, 10);
		} else if (!strcasecmp(v->name, "bindaddr")) {
			ast_copy_string(gIP, v->value, sizeof(gIP));
		} else if (!strcasecmp(v->name, "h225portrange")) {
			char* endlimit = 0;
         		char temp[512];
			ast_copy_string(temp, v->value, sizeof(temp));
			endlimit = strchr(temp, ',');
			if (endlimit) {
				*endlimit = '\0';
				endlimit++;
				ooconfig.mTCPPortStart = atoi(temp);
				ooconfig.mTCPPortEnd = atoi(endlimit);

			} else {
				ast_log(LOG_ERROR, "h225portrange: Invalid format, separate port range with \",\"\n");
			}
		} else if (!strcasecmp(v->name, "gateway")) {
			gIsGateway = ast_true(v->value);
      		} else if (!strcasecmp(v->name, "faststart")) {
			gFastStart = ast_true(v->value);
			if (gFastStart)
				ooH323EpEnableFastStart();
			else
				ooH323EpDisableFastStart();
		} else if (!strcasecmp(v->name, "mediawaitforconnect")) {
			gMediaWaitForConnect = ast_true(v->value);
			if (gMediaWaitForConnect)
				ooH323EpEnableMediaWaitForConnect();
			else 
				ooH323EpDisableMediaWaitForConnect();
		} else if (!strcasecmp(v->name, "h245tunneling")) {
			gTunneling = ast_true(v->value);
			if (gTunneling)
				ooH323EpEnableH245Tunneling();
			else
				ooH323EpDisableH245Tunneling();
		} else if (!strcasecmp(v->name, "g729onlyA")) {
			g729onlyA = ast_true(v->value);
		} else if (!strcasecmp(v->name, "roundtrip")) {
			sscanf(v->value, "%d,%d", &gRTDRCount, &gRTDRInterval);
      		} else if (!strcasecmp(v->name, "trybemaster")) {
			gBeMaster = ast_true(v->value);
			if (gBeMaster)
				ooH323EpTryBeMaster(1);
			else 
				ooH323EpTryBeMaster(0);
		} else if (!strcasecmp(v->name, "h323id")) {
         		pNewAlias = malloc(sizeof(struct ooAliases));
			if (!pNewAlias) {
				ast_log(LOG_ERROR, "Failed to allocate memory for h323id alias\n");
				ast_config_destroy(cfg);
				return 1;
			}
	 		if (gAliasList == NULL) { /* first h323id - set as callerid if callerid is not set */
	  			ast_copy_string(gCallerID, v->value, sizeof(gCallerID));
	 		}
			pNewAlias->type =  T_H225AliasAddress_h323_ID;
			pNewAlias->value = strdup(v->value);
			pNewAlias->next = gAliasList;
			gAliasList = pNewAlias;
			pNewAlias = NULL;
		} else if (!strcasecmp(v->name, "e164")) {
         		pNewAlias = malloc(sizeof(struct ooAliases));
			if (!pNewAlias) {
				ast_log(LOG_ERROR, "Failed to allocate memory for e164 alias\n");
				ast_config_destroy(cfg);
				return 1;
			}
			pNewAlias->type =  T_H225AliasAddress_dialedDigits;
			pNewAlias->value = strdup(v->value);
			pNewAlias->next = gAliasList;
			gAliasList = pNewAlias;
			pNewAlias = NULL;
		} else if (!strcasecmp(v->name, "email")) {
         		pNewAlias = malloc(sizeof(struct ooAliases));
			if (!pNewAlias) {
				ast_log(LOG_ERROR, "Failed to allocate memory for email alias\n");
				ast_config_destroy(cfg);
				return 1;
			}
			pNewAlias->type =  T_H225AliasAddress_email_ID;
			pNewAlias->value = strdup(v->value);
			pNewAlias->next = gAliasList;
			gAliasList = pNewAlias;
			pNewAlias = NULL;
      } else if (!strcasecmp(v->name, "t35country")) {
         t35countrycode = atoi(v->value);
      } else if (!strcasecmp(v->name, "t35extensions")) {
         t35extensions = atoi(v->value);
      } else if (!strcasecmp(v->name, "manufacturer")) {
         manufacturer = atoi(v->value);
      } else if (!strcasecmp(v->name, "vendorid")) {
         ast_copy_string(vendor, v->value, sizeof(vendor));
      } else if (!strcasecmp(v->name, "versionid")) {
         ast_copy_string(version, v->value, sizeof(version));
		} else if (!strcasecmp(v->name, "callerid")) {
			ast_copy_string(gCallerID, v->value, sizeof(gCallerID));
		} else if (!strcasecmp(v->name, "incominglimit")) {
			gIncomingLimit = atoi(v->value);
		} else if (!strcasecmp(v->name, "outgoinglimit")) {
			gOutgoingLimit = atoi(v->value);
		} else if (!strcasecmp(v->name, "gatekeeper")) {
			if (!strcasecmp(v->value, "DISABLE")) {
				gRasGkMode = RasNoGatekeeper;
			} else if (!strcasecmp(v->value, "DISCOVER")) {
				gRasGkMode = RasDiscoverGatekeeper;
			} else {
				gRasGkMode = RasUseSpecificGatekeeper;
            			strncpy(gGatekeeper, v->value, sizeof(gGatekeeper)-1);
			}
		} else if (!strcasecmp(v->name, "logfile")) {
         strncpy(gLogFile, v->value, sizeof(gLogFile)-1);
		} else if (!strcasecmp(v->name, "context")) {
         strncpy(gContext, v->value, sizeof(gContext)-1);
         ast_verbose(VERBOSE_PREFIX_3 "  == Setting default context to %s\n", 
                                                      gContext);
		} else if (!strcasecmp(v->name, "rtptimeout")) {
			gRTPTimeout = atoi(v->value);
			if (gRTPTimeout <= 0)
				gRTPTimeout = 60;
		} else if (!strcasecmp(v->name, "tos")) {
			if (sscanf(v->value, "%30i", &format) == 1)
				gTOS = format & 0xff;
			else if (!strcasecmp(v->value, "lowdelay"))
				gTOS = IPTOS_LOWDELAY;
			else if (!strcasecmp(v->value, "throughput"))
				gTOS = IPTOS_THROUGHPUT;
			else if (!strcasecmp(v->value, "reliability"))
				gTOS = IPTOS_RELIABILITY;
			else if (!strcasecmp(v->value, "mincost"))
				gTOS = IPTOS_MINCOST;
			else if (!strcasecmp(v->value, "none"))
				gTOS = 0;
			else
				ast_log(LOG_WARNING, "Invalid tos value at line %d, should be "
											"'lowdelay', 'throughput', 'reliability', "
											"'mincost', or 'none'\n", v->lineno);
		} else if (!strcasecmp(v->name, "amaflags")) {
			gAMAFLAGS = ast_cdr_amaflags2int(v->value);
		} else if (!strcasecmp(v->name, "accountcode")) {
         ast_copy_string(gAccountcode, v->value, sizeof(gAccountcode));
		} else if (!strcasecmp(v->name, "disallow")) {
			ast_parse_allow_disallow(&gPrefs, &gCapability, v->value, 0);
		} else if (!strcasecmp(v->name, "allow")) {
			const char* tcodecs = v->value;
			if (!strcasecmp(v->value, "all")) {
				tcodecs = "ulaw,alaw,g729,g723,gsm";
			}
			ast_parse_allow_disallow(&gPrefs, &gCapability, tcodecs, 1);
		} else if (!strcasecmp(v->name, "dtmfmode")) {
			if (!strcasecmp(v->value, "inband"))
				gDTMFMode = H323_DTMF_INBAND;
			else if (!strcasecmp(v->value, "rfc2833"))
				gDTMFMode = H323_DTMF_RFC2833;
			else if (!strcasecmp(v->value, "cisco"))
				gDTMFMode = H323_DTMF_CISCO;
			else if (!strcasecmp(v->value, "q931keypad"))
				gDTMFMode = H323_DTMF_Q931;
			else if (!strcasecmp(v->value, "h245alphanumeric"))
				gDTMFMode = H323_DTMF_H245ALPHANUMERIC;
			else if (!strcasecmp(v->value, "h245signal"))
				gDTMFMode = H323_DTMF_H245SIGNAL;
			else {
            ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", 
                                                                    v->value);
				gDTMFMode = H323_DTMF_RFC2833;
			}
		} else if (!strcasecmp(v->name, "relaxdtmf")) {
			gDTMFMode |= ast_true(v->value) ? H323_DTMF_INBANDRELAX : 0;
		} else if (!strcasecmp(v->name, "dtmfcodec") && atoi(v->value)) {
			gDTMFCodec = atoi(v->value);
	 	} else if (!strcasecmp(v->name, "t38support")) {
			if (!strcasecmp(v->value, "disabled"))
				gT38Support = T38_DISABLED;
			if (!strcasecmp(v->value, "no"))
				gT38Support = T38_DISABLED;
			else if (!strcasecmp(v->value, "faxgw"))
				gT38Support = T38_FAXGW;
			else if (!strcasecmp(v->value, "yes"))
				gT38Support = T38_ENABLED;
		} else if (!strcasecmp(v->name, "tracelevel")) {
			gTRCLVL = atoi(v->value);
			ooH323EpSetTraceLevel(gTRCLVL);
		}
		v = v->next;
	}
	
	for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
		if (strcasecmp(cat, "general")) {
			int friend_type = 0;
			utype = ast_variable_retrieve(cfg, cat, "type");
			if (utype) {
				friend_type = strcasecmp(utype, "friend");
				if (!strcmp(utype, "user") || 0 == friend_type) {
					user = build_user(cat, ast_variable_browse(cfg, cat));
					if (user) {
						ast_mutex_lock(&userl.lock);
						user->next = userl.users;
						userl.users = user;
						ast_mutex_unlock(&userl.lock);
					} else {
						ast_log(LOG_WARNING, "Failed to build user %s\n", cat);
					}
				}
				if (!strcasecmp(utype, "peer") || 0 == friend_type) {
					peer = build_peer(cat, ast_variable_browse(cfg, cat), friend_type);
					if (peer) {
						ast_mutex_lock(&peerl.lock);
						peer->next = peerl.peers;
						peerl.peers = peer;
						ast_mutex_unlock(&peerl.lock);
					} else {
						ast_log(LOG_WARNING, "Failed to build peer %s\n", cat);
					}
				}
			}
		}
	}
	ast_config_destroy(cfg);


	/* Determine ip address if neccessary */
	if (ast_strlen_zero(gIP)) {
		ooGetLocalIPAddress(gIP);
		if (!strcmp(gIP, "127.0.0.1")) {
			ast_log(LOG_NOTICE, "Failed to determine local ip address. Please "
									 "specify it in ooh323.conf. OOH323 Disabled\n");
			return 1;
		}
	}

	if (gH323Debug)
		ast_verbose("+++   reload_config\n");

	return 0;

}


static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	char ip_port[30];
	struct ooh323_peer *prev = NULL, *peer = NULL;
	
	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 show peer";
		e->usage =
			"Usage: ooh323 show peer <name>\n"
			"		 List details of specific OOH323 peer.\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 4)
		return CLI_SHOWUSAGE;

 
	ast_mutex_lock(&peerl.lock);
	peer = peerl.peers;
	while (peer) {
		ast_mutex_lock(&peer->lock);
      		if(!strcmp(peer->name, a->argv[3]))
			break;
		else {
			prev = peer;
			peer = peer->next;
			ast_mutex_unlock(&prev->lock);
		}
	}

	if (peer) {
      sprintf(ip_port, "%s:%d", peer->ip, peer->port);
      ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name);
      ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", peer->faststart?"yes":"no",
					peer->h245tunneling?"yes":"no");
      ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "(");
      print_codec_to_cli(a->fd, &peer->prefs);
      ast_cli(a->fd, ")\n");
      ast_cli(a->fd, "%-15.15s", "DTMF Mode: ");
		if (peer->dtmfmode & H323_DTMF_CISCO) {
         ast_cli(a->fd, "%s\n", "cisco");
	 ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec);
		} else if (peer->dtmfmode & H323_DTMF_RFC2833) {
         ast_cli(a->fd, "%s\n", "rfc2833");
	 ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", peer->dtmfcodec);
		} else if (peer->dtmfmode & H323_DTMF_Q931)
         ast_cli(a->fd, "%s\n", "q931keypad");
		else if (peer->dtmfmode & H323_DTMF_H245ALPHANUMERIC)
         ast_cli(a->fd, "%s\n", "h245alphanumeric");
		else if (peer->dtmfmode & H323_DTMF_H245SIGNAL)
         ast_cli(a->fd, "%s\n", "h245signal");
		else if (peer->dtmfmode & H323_DTMF_INBAND && peer->dtmfmode & H323_DTMF_INBANDRELAX)
         ast_cli(a->fd, "%s\n", "inband-relaxed");
		else if (peer->dtmfmode & H323_DTMF_INBAND)
         ast_cli(a->fd, "%s\n", "inband");
		else
         ast_cli(a->fd, "%s\n", "unknown");

	ast_cli(a->fd,"%-15s", "T.38 Mode: ");
	if (peer->t38support == T38_DISABLED)
		ast_cli(a->fd, "%s\n", "disabled");
	else if (peer->t38support == T38_FAXGW)
		ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");

	ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode);
	ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", 
		ast_cdr_flags2str(peer->amaflags));
	ast_cli(a->fd, "%-15.15s%s\n", "IP:Port: ", ip_port);
	ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit);
	ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout);
	if (peer->rtpmaskstr[0])
		ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", peer->rtpmaskstr);
	if (peer->rtdrcount && peer->rtdrinterval) 
		ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", peer->rtdrcount, peer->rtdrinterval);
	ast_mutex_unlock(&peer->lock);
	} else {
	ast_cli(a->fd, "Peer %s not found\n", a->argv[3]);
	ast_cli(a->fd, "\n");
	}
	ast_mutex_unlock(&peerl.lock);

	return CLI_SUCCESS;
}

static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	struct ooh323_peer *prev = NULL, *peer = NULL;
   char formats[FORMAT_STRING_SIZE];
   char ip_port[30];
#define FORMAT  "%-15.15s  %-15.15s  %-23.23s  %-s\n"

	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 show peers";
		e->usage =
			"Usage: ooh323 show peers\n"
			"		 Lists all known OOH323 peers.\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 3)
		return CLI_SHOWUSAGE;

   ast_cli(a->fd, FORMAT, "Name", "Accountcode", "ip:port", "Formats");

	ast_mutex_lock(&peerl.lock);
	peer = peerl.peers;
	while (peer) {
		ast_mutex_lock(&peer->lock);
		snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port);
     ast_cli(a->fd, FORMAT, peer->name, 
					peer->accountcode,
					ip_port,
                 ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,peer->capability));
		prev = peer;
		peer = peer->next;
		ast_mutex_unlock(&prev->lock);

	}
	ast_mutex_unlock(&peerl.lock);
#undef FORMAT
	return CLI_SUCCESS;
}

/*! \brief Print codec list from preference to CLI/manager */
static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
{
	int x, codec;

	for (x = 0; x < 32; x++) {
		codec = ast_codec_pref_index(pref, x);
		if (!codec)
			break;
		ast_cli(fd, "%s", ast_getformatname(codec));
		ast_cli(fd, ":%d", pref->framing[x]);
		if (x < 31 && ast_codec_pref_index(pref, x + 1))
			ast_cli(fd, ",");
	}
	if (!x)
		ast_cli(fd, "none");
}

static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	struct ooh323_user *prev = NULL, *user = NULL;

	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 show user";
		e->usage =
			"Usage: ooh323 show user <name>\n"
			"		 List details of specific OOH323 user.\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 4)
		return CLI_SHOWUSAGE;


	ast_mutex_lock(&userl.lock);
	user = userl.users;
	while (user) {
		ast_mutex_lock(&user->lock);
      if(!strcmp(user->name, a->argv[3])) {
			break;
      } else {
			prev = user;
			user = user->next;
			ast_mutex_unlock(&prev->lock);
		}
	}

	if (user) {
      ast_cli(a->fd, "%-15.15s%s\n", "Name: ", user->name);
      ast_cli(a->fd, "%s:%s,%s\n", "FastStart/H.245 Tunneling", user->faststart?"yes":"no",
					user->h245tunneling?"yes":"no");
      ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "(");
      print_codec_to_cli(a->fd, &user->prefs);
      ast_cli(a->fd, ")\n");
      ast_cli(a->fd, "%-15.15s", "DTMF Mode: ");
		if (user->dtmfmode & H323_DTMF_CISCO) {
         ast_cli(a->fd, "%s\n", "cisco");
	 ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec);
		} else if (user->dtmfmode & H323_DTMF_RFC2833) {
         ast_cli(a->fd, "%s\n", "rfc2833");
	 ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", user->dtmfcodec);
		} else if (user->dtmfmode & H323_DTMF_Q931)
         ast_cli(a->fd, "%s\n", "q931keypad");
		else if (user->dtmfmode & H323_DTMF_H245ALPHANUMERIC)
         ast_cli(a->fd, "%s\n", "h245alphanumeric");
		else if (user->dtmfmode & H323_DTMF_H245SIGNAL)
         ast_cli(a->fd, "%s\n", "h245signal");
		else if (user->dtmfmode & H323_DTMF_INBAND && user->dtmfmode & H323_DTMF_INBANDRELAX)
         ast_cli(a->fd, "%s\n", "inband-relaxed");
		else if (user->dtmfmode & H323_DTMF_INBAND)
         ast_cli(a->fd, "%s\n", "inband");
		else
         ast_cli(a->fd, "%s\n", "unknown");

   	ast_cli(a->fd,"%-15s", "T.38 Mode: ");
	if (user->t38support == T38_DISABLED)
		ast_cli(a->fd, "%s\n", "disabled");
	else if (user->t38support == T38_FAXGW)
		ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");

      ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode);
      ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", 
                                            ast_cdr_flags2str(user->amaflags));
      ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context);
      ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit);
      ast_cli(a->fd, "%-15.15s%d\n", "InUse: ", user->inUse);
      ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout);
	if (user->rtpmaskstr[0])
		ast_cli(a->fd, "%-15.15s%s\n", "rtpmask: ", user->rtpmaskstr);
		ast_mutex_unlock(&user->lock);
	if (user->rtdrcount && user->rtdrinterval) 
		ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", user->rtdrcount, user->rtdrinterval);
	} else {
     ast_cli(a->fd, "User %s not found\n", a->argv[3]);
     ast_cli(a->fd, "\n");
	}
	ast_mutex_unlock(&userl.lock);

	return CLI_SUCCESS;
}

static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	struct ooh323_user *prev = NULL, *user = NULL;
   char formats[FORMAT_STRING_SIZE];
#define FORMAT1  "%-15.15s  %-15.15s  %-15.15s  %-s\n"

	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 show users";
		e->usage =
			"Usage: ooh323 show users \n"
			"		 Lists all known OOH323 users.\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 3)
		return CLI_SHOWUSAGE;


   ast_cli(a->fd, FORMAT1, "Username", "Accountcode", "Context", "Formats");

	ast_mutex_lock(&userl.lock);
	user = userl.users;
   while(user)
   {
		ast_mutex_lock(&user->lock);
     		ast_cli(a->fd, FORMAT1, user->name, 
					user->accountcode, user->context,
					ast_getformatname_multiple(formats, FORMAT_STRING_SIZE, user->capability));
		prev = user;
		user = user->next;
		ast_mutex_unlock(&prev->lock);

	}
	ast_mutex_unlock(&userl.lock);
#undef FORMAT1
   return RESULT_SUCCESS;

}

static char *handle_cli_ooh323_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 set debug [off]";
		e->usage =
			"Usage: ooh323 set debug [off]\n"
			"		 Enables/Disables debugging of OOH323 channel driver\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc < 3 || a->argc > 4)
		return CLI_SHOWUSAGE;
	if (a->argc == 4 && strcasecmp(a->argv[3], "off"))
		return CLI_SHOWUSAGE;

	gH323Debug = (a->argc == 4) ? FALSE : TRUE;
	ast_cli(a->fd, "OOH323 Debugging %s\n", gH323Debug ? "Enabled" : "Disabled");

	return CLI_SUCCESS;
}

#if 0
static int ooh323_show_channels(int fd, int argc, char *argv[])
{
	return RESULT_SUCCESS;
}
#endif

static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	char value[FORMAT_STRING_SIZE];
	ooAliases *pAlias = NULL, *pAliasNext = NULL;;

	switch (cmd) {
	case CLI_INIT:
		e->command = "ooh323 show config";
		e->usage =
			"Usage: ooh323 show config\n"
			"		 Shows global configuration of H.323 channel driver\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 3)
		return CLI_SHOWUSAGE;



   ast_cli(a->fd, "\nObjective Open H.323 Channel Driver's Config:\n");
	snprintf(value, sizeof(value), "%s:%d", gIP, gPort);
   ast_cli(a->fd, "%-20s%s\n", "IP:Port: ", value);
   ast_cli(a->fd, "%-20s%d-%d\n", "H.225 port range: ", 
      ooconfig.mTCPPortStart, ooconfig.mTCPPortEnd);
   ast_cli(a->fd, "%-20s%s\n", "FastStart", gFastStart?"yes":"no");
   ast_cli(a->fd, "%-20s%s\n", "Tunneling", gTunneling?"yes":"no");
   ast_cli(a->fd, "%-20s%s\n", "CallerId", gCallerID);
   ast_cli(a->fd, "%-20s%s\n", "MediaWaitForConnect", 
      gMediaWaitForConnect?"yes":"no");

#if (0)
		extern OOH323EndPoint gH323ep;
   ast_cli(a->fd, "%-20s%s\n", "FASTSTART", 
			(OO_TESTFLAG(gH323ep.flags, OO_M_FASTSTART) != 0) ? "yes" : "no");
   ast_cli(a->fd, "%-20s%s\n", "TUNNELING", 
			(OO_TESTFLAG(gH323ep.flags, OO_M_TUNNELING) != 0) ? "yes" : "no");
   ast_cli(a->fd, "%-20s%s\n", "MEDIAWAITFORCONN",
			(OO_TESTFLAG(gH323ep.flags, OO_M_MEDIAWAITFORCONN) != 0) ? "yes" : "no");
#endif

	if (gRasGkMode == RasNoGatekeeper)
		snprintf(value, sizeof(value), "%s", "No Gatekeeper");
	else if (gRasGkMode == RasDiscoverGatekeeper)
		snprintf(value, sizeof(value), "%s", "Discover");
	else
		snprintf(value, sizeof(value), "%s", gGatekeeper);

   ast_cli(a->fd,  "%-20s%s\n", "Gatekeeper:", value);

   ast_cli(a->fd,  "%-20s%s\n", "H.323 LogFile:", gLogFile);   

   ast_cli(a->fd,  "%-20s%s\n", "Context:", gContext);
   
   ast_cli(a->fd,  "%-20s%s\n", "Capability:", 
           ast_getformatname_multiple(value,FORMAT_STRING_SIZE,gCapability));

   ast_cli(a->fd, "%-20s", "DTMF Mode: ");
	if (gDTMFMode & H323_DTMF_CISCO) {
      ast_cli(a->fd, "%s\n", "cisco");
      ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec);
	} else if (gDTMFMode & H323_DTMF_RFC2833) {
      ast_cli(a->fd, "%s\n", "rfc2833");
      ast_cli(a->fd, "%-15.15s%d\n", "DTMF Codec: ", gDTMFCodec);
	} else if (gDTMFMode & H323_DTMF_Q931)
      ast_cli(a->fd, "%s\n", "q931keypad");
	else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC)
      ast_cli(a->fd, "%s\n", "h245alphanumeric");
	else if (gDTMFMode & H323_DTMF_H245SIGNAL)
      ast_cli(a->fd, "%s\n", "h245signal");
		else if (gDTMFMode & H323_DTMF_INBAND && gDTMFMode & H323_DTMF_INBANDRELAX)
         ast_cli(a->fd, "%s\n", "inband-relaxed");
		else if (gDTMFMode & H323_DTMF_INBAND)
         ast_cli(a->fd, "%s\n", "inband");
	else
		ast_cli(a->fd, "%s\n", "unknown");

   	ast_cli(a->fd,"%-20s", "T.38 Mode: ");
	if (gT38Support == T38_DISABLED)
		ast_cli(a->fd, "%s\n", "disabled");
	else if (gT38Support == T38_FAXGW)
		ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");

	if (gRTDRCount && gRTDRInterval)
		ast_cli(a->fd, "%-15.15s%d,%d\n", "RoundTrip: ", gRTDRCount, gRTDRInterval);

   ast_cli(a->fd, "%-20s%ld\n", "Call counter: ", callnumber);
   ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode);

   ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS));

	pAlias = gAliasList;
   if(pAlias) {
     ast_cli(a->fd, "%-20s\n", "Aliases: ");
   }
	while (pAlias) {
		pAliasNext = pAlias->next;
		if (pAliasNext) {
         ast_cli(a->fd,"\t%-30s\t%-30s\n",pAlias->value, pAliasNext->value);
			pAlias = pAliasNext->next;
      }
      else{
         ast_cli(a->fd,"\t%-30s\n",pAlias->value);
			pAlias = pAlias->next;
		}
	}
	return CLI_SUCCESS;
}

static struct ast_cli_entry cli_ooh323[] = {
	AST_CLI_DEFINE(handle_cli_ooh323_set_debug,	"Enable/Disable OOH323 debugging"),
	AST_CLI_DEFINE(handle_cli_ooh323_show_config, "Show details on global configuration of H.323 channel driver"),
	AST_CLI_DEFINE(handle_cli_ooh323_show_peer,	"Show details on specific OOH323 peer"),
	AST_CLI_DEFINE(handle_cli_ooh323_show_peers,  "Show defined OOH323 peers"),
	AST_CLI_DEFINE(handle_cli_ooh323_show_user,	"Show details on specific OOH323 user"),
	AST_CLI_DEFINE(handle_cli_ooh323_show_users,  "Show defined OOH323 users"),
        AST_CLI_DEFINE(handle_cli_ooh323_reload, "reload ooh323 config")
};


static int load_module(void)
{
	int res;
	struct ooAliases * pNewAlias = NULL;
	struct ooh323_peer *peer = NULL;
	OOH225MsgCallbacks h225Callbacks = {0, 0, 0, 0};

	OOH323CALLBACKS h323Callbacks = {
		.onNewCallCreated = onNewCallCreated,
		.onAlerting = onAlerting,
		.onProgress = onProgress,
		.onIncomingCall = NULL,
		.onOutgoingCall = onOutgoingCall,
		.onCallEstablished = onCallEstablished,
		.onCallCleared = onCallCleared,
		.openLogicalChannels = NULL,
		.onReceivedDTMF = ooh323_onReceivedDigit,
		.onModeChanged = onModeChanged
	};

	myself = ast_module_info->self;

	h225Callbacks.onReceivedSetup = &ooh323_onReceivedSetup;

	userl.users = NULL;
	ast_mutex_init(&userl.lock);
	peerl.peers = NULL;
	ast_mutex_init(&peerl.lock);
 
#if 0		
	ast_register_atexit(&ast_ooh323c_exit);
#endif

	if (!(sched = sched_context_create())) {
		ast_log(LOG_WARNING, "Unable to create schedule context\n");
	}
	if (!(io = io_context_create())) {
		ast_log(LOG_WARNING, "Unable to create I/O context\n");
	}


	if (!(res = reload_config(0))) {
		/* Make sure we can register our OOH323 channel type */
		if (ast_channel_register(&ooh323_tech)) {
			ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
			return 0;
		}
		ast_rtp_glue_register(&ooh323_rtp);
		ast_udptl_proto_register(&ooh323_udptl);
		ast_cli_register_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry));

		 /* fire up the H.323 Endpoint */		 
		if (OO_OK != ooH323EpInitialize(OO_CALLMODE_AUDIOCALL, gLogFile)) {
         ast_log(LOG_ERROR, "Failed to initialize OOH323 endpoint-"
                            "OOH323 Disabled\n");
			return 1;
		}

		if (gIsGateway)
			ooH323EpSetAsGateway();

      		ooH323EpSetVersionInfo(t35countrycode, t35extensions, manufacturer,
									 vendor, version);
		ooH323EpDisableAutoAnswer();
		ooH323EpSetH225MsgCallbacks(h225Callbacks);
      		ooH323EpSetTraceLevel(gTRCLVL);
		ooH323EpSetLocalAddress(gIP, gPort);
		ooH323EpSetCallerID(gCallerID);
 
      if(ooH323EpSetTCPPortRange(ooconfig.mTCPPortStart, 
                                 ooconfig.mTCPPortEnd) == OO_FAILED) {
         ast_log(LOG_ERROR, "h225portrange: Failed to set range\n");
      }

		/* Set aliases if any */
		for (pNewAlias = gAliasList; pNewAlias; pNewAlias = pNewAlias->next) {
			switch (pNewAlias->type) {
			case T_H225AliasAddress_h323_ID:
				ooH323EpAddAliasH323ID(pNewAlias->value);
				break;
			case T_H225AliasAddress_dialedDigits:	
				ooH323EpAddAliasDialedDigits(pNewAlias->value);
				break;
			case T_H225AliasAddress_email_ID:	
				ooH323EpAddAliasEmailID(pNewAlias->value);
				break;
         default:
            ;
			}
		}

		ast_mutex_lock(&peerl.lock);
		peer = peerl.peers;
		while (peer) {
         if(peer->h323id) ooH323EpAddAliasH323ID(peer->h323id);
         if(peer->email)  ooH323EpAddAliasEmailID(peer->email);
         if(peer->e164)   ooH323EpAddAliasDialedDigits(peer->e164);
         if(peer->url)    ooH323EpAddAliasURLID(peer->url);
			peer = peer->next;
		}
		ast_mutex_unlock(&peerl.lock);
	

		if (gMediaWaitForConnect)
			ooH323EpEnableMediaWaitForConnect();
		else 
			ooH323EpDisableMediaWaitForConnect();

		/* Fast start and tunneling options */
		if (gFastStart)
			ooH323EpEnableFastStart();
		else
			ooH323EpDisableFastStart();

		if (!gTunneling)
			ooH323EpDisableH245Tunneling();

		if (gBeMaster)
			ooH323EpTryBeMaster(1);

      		ooH323EpEnableManualRingback();

		/* Gatekeeper */
		if (gRasGkMode == RasUseSpecificGatekeeper)
			ooGkClientInit(gRasGkMode, gGatekeeper, 0);
		else if (gRasGkMode == RasDiscoverGatekeeper)
			ooGkClientInit(gRasGkMode, 0, 0);

		/* Register callbacks */
		ooH323EpSetH323Callbacks(h323Callbacks);

		/* Add endpoint capabilities */
		if (ooh323c_set_capability(&gPrefs, gCapability, gDTMFMode, gDTMFCodec) < 0) {
			ast_log(LOG_ERROR, "Capabilities failure for OOH323. OOH323 Disabled.\n");
			return 1;
		}
  
		/* Create H.323 listener */
		if (ooCreateH323Listener() != OO_OK) {
         ast_log(LOG_ERROR, "OOH323 Listener Creation failure. "
                            "OOH323 DISABLED\n");
		
			ooH323EpDestroy();
			return 1;
		}

		if (ooh323c_start_stack_thread() < 0) {
         ast_log(LOG_ERROR, "Failed to start OOH323 stack thread. "
                            "OOH323 DISABLED\n");
			ooH323EpDestroy();
			return 1;
		}
		/* And start the monitor for the first time */
		restart_monitor();
	}

	return 0;
}


static void *do_monitor(void *data)
{
	int res;
	int reloading;
	struct ooh323_pvt *h323 = NULL;
	time_t t;

	for (;;) {
		struct ooh323_pvt *h323_next;
		/* Check for a reload request */
		ast_mutex_lock(&h323_reload_lock);
		reloading = h323_reloading;
		h323_reloading = 0;
		ast_mutex_unlock(&h323_reload_lock);
		if (reloading) {
			ast_verb(1, "Reloading H.323\n");
			ooh323_do_reload();
		}

		/* Check for interfaces needing to be killed */
		ast_mutex_lock(&iflock);
		time(&t);
		h323 = iflist;
		while (h323) {
			h323_next = h323->next;

			/* TODO: Need to add rtptimeout keepalive support */

			if (h323->rtp && h323->rtptimeout && h323->lastrtptx &&
				h323->lastrtptx + h323->rtptimeout < t) {
				ast_rtp_instance_sendcng(h323->rtp, 0);
				h323->lastrtptx = time(NULL);
			}

			if (h323->rtp && h323->owner && h323->rtptimeout &&
				h323->lastrtprx &&
				h323->lastrtprx + h323->rtptimeout < t) {
				if (!ast_channel_trylock(h323->owner)) {
					ast_softhangup_nolock(h323->owner, AST_SOFTHANGUP_DEV);
					ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n", h323->owner->name, (long) (t - h323->lastrtprx));
					ast_channel_unlock(h323->owner);
				}
				
			}

			if (ast_test_flag(h323, H323_NEEDDESTROY)) {
				ooh323_destroy (h323);
         } /* else if (ast_test_flag(h323, H323_NEEDSTART) && h323->owner) {
	  ast_channel_lock(h323->owner);
          if (ast_pbx_start(h323->owner)) {
            ast_log(LOG_WARNING, "Unable to start PBX on %s\n", h323->owner->name);
            ast_channel_unlock(h323->owner);
            ast_hangup(h323->owner);
          }
          ast_channel_unlock(h323->owner);
	  ast_clear_flag(h323, H323_NEEDSTART);
	 } */
			h323 = h323_next;
		}
		ast_mutex_unlock(&iflock);
		pthread_testcancel();

		/* Wait for sched or io */
		res = ast_sched_wait(sched);
		if ((res < 0) || (res > 1000)) {
			res = 1000;
		}
		res = ast_io_wait(io, res);
		pthread_testcancel();
		ast_mutex_lock(&monlock);
		if (res >= 0) {
			ast_sched_runq(sched);
		}
		ast_mutex_unlock(&monlock);
	}
	/* Never reached */
	return NULL;
}

int restart_monitor(void)
{
	pthread_attr_t attr;

	/* If we're supposed to be stopped -- stay stopped */
	if (monitor_thread == AST_PTHREADT_STOP)
		return 0;
	if (ast_mutex_lock(&monlock)) {
		ast_log(LOG_WARNING, "Unable to lock monitor\n");
		return -1;
	}
	if (monitor_thread == pthread_self()) {
		ast_mutex_unlock(&monlock);
		ast_log(LOG_WARNING, "Cannot kill myself\n");
		return -1;
	}
	if (monitor_thread != AST_PTHREADT_NULL) {
		/* Wake up the thread */
		pthread_kill(monitor_thread, SIGURG);
	} else {
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
		/* Start a new monitor */
		if (ast_pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
			ast_mutex_unlock(&monlock);
			ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
			return -1;
		}
	}
	ast_mutex_unlock(&monlock);
	return 0;
}



int ooh323_destroy(struct ooh323_pvt *p)
{
	/* NOTE: Assumes iflock already acquired */
	struct ooh323_pvt *prev = NULL, *cur = NULL;
	struct ooh323_user *user = NULL;

	if (gH323Debug) {
		ast_verbose("---   ooh323_destroy \n");

		if (p)
			ast_verbose(" Destroying %s\n", p->username);
	}

	cur = iflist;
	while (cur) {
		if (cur == p) { break; }
		prev = cur;
		cur = cur->next;
	}

	if (cur) {
		ast_mutex_lock(&cur->lock);
		if (prev)
			prev->next = cur->next;
		else
			iflist = cur->next;

		if (cur->callToken) {
	 		if (gH323Debug) 
				ast_verbose(" Destroying %s\n", cur->callToken);
			ast_free(cur->callToken);
			cur->callToken = 0;
		}

		if (cur->username) {
			free(cur->username);
			cur->username = 0;
		}

		if (cur->host) {
			free(cur->host);
			cur->host = 0;
		}

		if (cur->callerid_name) {
			free(cur->callerid_name);
			cur->callerid_name = 0;
		}
		
		if (cur->callerid_num) {
			free(cur->callerid_num);
			cur->callerid_num = 0;
		}

		if (cur->rtp) {
			ast_rtp_instance_destroy(cur->rtp);
			cur->rtp = NULL;
		}

		if (cur->udptl) {
			ast_udptl_destroy(cur->udptl);
			cur->udptl = NULL;
		}
	
		/* Unlink us from the owner if we have one */
		if (cur->owner) {
         		while(ast_channel_trylock(cur->owner)) {
            			ast_debug(1,"Failed to grab lock, trying again\n");
				DEADLOCK_AVOIDANCE(&cur->lock);
         		}           
			ast_debug(1, "Detaching from %s\n", cur->owner->name);
			cur->owner->tech_pvt = NULL;
			ast_channel_unlock(cur->owner);
			cur->owner = NULL;
			ast_module_unref(myself);
		}
  
		if (cur->vad) {
			ast_dsp_free(cur->vad);
			cur->vad = NULL;
		}

/* decrement user/peer count */

      if(!ast_test_flag(cur, H323_OUTGOING)) {
	 if (cur->neighbor.user) {
	  user = find_user(p->callerid_name, cur->neighbor.user);
	  if(user && user->inUse > 0) {
	  	ast_mutex_lock(&user->lock);
	  	user->inUse--;
	  	ast_mutex_unlock(&user->lock);
	  }
	  free(cur->neighbor.user);
	 }
      } else {
/* outgoing limit decrement here !!! */
      }

		ast_mutex_unlock(&cur->lock);
		ast_mutex_destroy(&cur->lock);

		ast_free(cur);
	}

	if (gH323Debug)
		ast_verbose("+++   ooh323_destroy\n");

	return 0;
}

int delete_peers()
{
	struct ooh323_peer *cur = NULL, *prev = NULL;
	ast_mutex_lock(&peerl.lock);
	cur = peerl.peers;
	while (cur) {
		prev = cur;
		cur = cur->next;

		ast_mutex_destroy(&prev->lock);
      if(prev->h323id)   free(prev->h323id);
      if(prev->email)    free(prev->email);
      if(prev->url)      free(prev->url);
      if(prev->e164)     free(prev->e164);
      if(prev->rtpmask) {
		ast_mutex_lock(&prev->rtpmask->lock);
		prev->rtpmask->inuse--;
		ast_mutex_unlock(&prev->rtpmask->lock);
	 	if (prev->rtpmask->inuse == 0) {
	  		regfree(&prev->rtpmask->regex);
			ast_mutex_destroy(&prev->rtpmask->lock);
	  		free(prev->rtpmask);
      		}
      }
		free(prev);

		if (cur == peerl.peers) {
			break;
		}
	}
	peerl.peers = NULL;
	ast_mutex_unlock(&peerl.lock);
	return 0;
}

int delete_users()
{
	struct ooh323_user *cur = NULL, *prev = NULL;
	ast_mutex_lock(&userl.lock);
	cur = userl.users;
	while (cur) {
		prev = cur;
		cur = cur->next;
		ast_mutex_destroy(&prev->lock);

      		if(prev->rtpmask) {
			ast_mutex_lock(&prev->rtpmask->lock);
			prev->rtpmask->inuse--;
			ast_mutex_unlock(&prev->rtpmask->lock);
	 		if (prev->rtpmask->inuse == 0) {
	  			regfree(&prev->rtpmask->regex);
				ast_mutex_destroy(&prev->rtpmask->lock);
	  			free(prev->rtpmask);
      			}
      		}
		free(prev);
		if (cur == userl.users) {
			break;
		}
	}
	userl.users = NULL;
	ast_mutex_unlock(&userl.lock);
	return 0;
}
  
static int unload_module(void)
{
	struct ooh323_pvt *p;
	struct ooAliases *cur = NULL, *prev = NULL;

	if (gH323Debug) {
		ast_verbose("--- ooh323  unload_module \n");
	}
	/* First, take us out of the channel loop */
	ast_cli_unregister_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry));
	ast_rtp_glue_unregister(&ooh323_rtp);
	ast_udptl_proto_unregister(&ooh323_udptl);
	ast_channel_unregister(&ooh323_tech);
#if 0
	ast_unregister_atexit(&ast_ooh323c_exit);
#endif

	if (gH323Debug) {
		ast_verbose("  unload_module - hanging up all interfaces\n");
	}
	if (!ast_mutex_lock(&iflock)) {
		/* Hangup all interfaces if they have an owner */
		p = iflist;
		while (p) {
			if (p->owner) {
				ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
			}
			p = p->next;
		}
		iflist = NULL;
		ast_mutex_unlock(&iflock);
	} else {
		ast_log(LOG_WARNING, "Unable to lock the interface list\n");
		return -1;
	}


	if (gH323Debug) {
		ast_verbose("  unload_module - stopping monitor thread\n");
	}  
	if (monitor_thread != AST_PTHREADT_NULL) {
		if (!ast_mutex_lock(&monlock)) {
			if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
				pthread_cancel(monitor_thread);
				pthread_kill(monitor_thread, SIGURG);
				pthread_join(monitor_thread, NULL);
			}
			monitor_thread = AST_PTHREADT_STOP;
			ast_mutex_unlock(&monlock);
		} else {
			ast_log(LOG_WARNING, "Unable to lock the monitor\n");
			return -1;
		}
	}


	if (gH323Debug) {
		ast_verbose("   unload_module - stopping stack thread\n");
	}
	ooh323c_stop_stack_thread();


	if (gH323Debug) {
		ast_verbose("   unload_module - freeing up memory used by interfaces\n");
	}
	if (!ast_mutex_lock(&iflock)) {
		struct ooh323_pvt *pl;

		/* Destroy all the interfaces and free their memory */
		p = iflist;
		while (p) {
			pl = p;
			p = p->next;
			/* Free associated memory */
			ooh323_destroy(pl);
		}
		iflist = NULL;
		ast_mutex_unlock(&iflock);
	} else {
		ast_log(LOG_WARNING, "Unable to lock the interface list\n");
		return -1;
	}
 

	if (gH323Debug) {
		ast_verbose("  unload_module - deleting users\n");
	}
	delete_users();


	if (gH323Debug) {
		ast_verbose("  unload_module - deleting peers\n");
	}
	delete_peers();


	if (gH323Debug) {
		ast_verbose("  unload_module - Freeing up alias list\n");
	}
	cur = gAliasList;
	while (cur) {
	  prev = cur;
	  cur = cur->next;
	  free(prev->value);
	  free(prev);
	}
	gAliasList = NULL;


	if (gH323Debug) {
		ast_verbose("	unload_module- destroying OOH323 endpoint \n");
	}
	ooH323EpDestroy();

	if (gH323Debug) {
		ast_verbose("+++ ooh323  unload_module \n");	
	}

	return 0;
}



static enum ast_rtp_glue_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **rtp)
{
	struct ooh323_pvt *p = NULL;
	enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;

	if (!(p = (struct ooh323_pvt *) chan->tech_pvt))
		return AST_RTP_GLUE_RESULT_FORBID;

	if (!(p->rtp)) {
		return AST_RTP_GLUE_RESULT_FORBID;
	}

	*rtp = p->rtp ? ao2_ref(p->rtp, +1), p->rtp : NULL;

	res = AST_RTP_GLUE_RESULT_LOCAL;

	if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) {
		res = AST_RTP_GLUE_RESULT_FORBID;
	}

	return res;
}

static enum ast_rtp_glue_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **rtp)
{
	struct ooh323_pvt *p = NULL;
	enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;

	if (!(p = (struct ooh323_pvt *) chan->tech_pvt))
		return AST_RTP_GLUE_RESULT_FORBID;

	if (!(p->rtp)) {
		return AST_RTP_GLUE_RESULT_FORBID;
	}

	*rtp = p->vrtp ? ao2_ref(p->vrtp, +1), p->vrtp : NULL;
	res = AST_RTP_GLUE_RESULT_LOCAL;

	return res;
}


int ooh323_update_capPrefsOrderForCall
	(ooCallData *call, struct ast_codec_pref *prefs)
{
	int i = 0;
	int codec = ast_codec_pref_index(prefs, i);

	ooResetCapPrefs(call);
	while (codec) {
		ooAppendCapToCapPrefs(call, ooh323_convertAsteriskCapToH323Cap(codec));
		codec = ast_codec_pref_index(prefs, ++i);
	}

	return 0;
}


int ooh323_convertAsteriskCapToH323Cap(format_t cap)
{
	char formats[FORMAT_STRING_SIZE];
	switch (cap) {
	case AST_FORMAT_ULAW:
		return OO_G711ULAW64K;
	case AST_FORMAT_ALAW:
		return OO_G711ALAW64K;
	case AST_FORMAT_GSM:
		return OO_GSMFULLRATE;

#ifdef AST_FORMAT_AMRNB
	case AST_FORMAT_AMRNB:
		return OO_AMRNB;
#endif
#ifdef AST_FORMAT_SPEEX
	case AST_FORMAT_SPEEX:
		return OO_SPEEX;
#endif

	case AST_FORMAT_G729A:
		return OO_G729A;
	case AST_FORMAT_G726:
		return OO_G726;
	case AST_FORMAT_G726_AAL2:
		return OO_G726AAL2;
	case AST_FORMAT_G723_1:
		return OO_G7231;
	case AST_FORMAT_H263:
		return OO_H263VIDEO;
	default:
		ast_log(LOG_NOTICE, "Don't know how to deal with mode %s\n", 
                        ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,cap));
		return -1;
	}
}

static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp,
	 struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
{
	/* XXX Deal with Video */
	struct ooh323_pvt *p;
	struct sockaddr_in them;
	struct sockaddr_in us;
	struct ast_sockaddr tmp;

	if (gH323Debug)
		ast_verbose("---   ooh323_set_peer - %s\n", chan->name);

	if (!rtp) {
		return 0;
	}

	p = (struct ooh323_pvt *) chan->tech_pvt;
	if (!p) {
		ast_log(LOG_ERROR, "No Private Structure, this is bad\n");
		return -1;
	}
	ast_rtp_instance_get_remote_address(rtp, &tmp);
	ast_sockaddr_to_sin(&tmp, &them);
	ast_rtp_instance_get_local_address(rtp, &tmp);
	ast_sockaddr_to_sin(&tmp, &us);
	return 0;
}




int configure_local_rtp(struct ooh323_pvt *p, ooCallData *call)
{
	struct sockaddr_in us;
	struct ast_sockaddr tmp;
	ooMediaInfo mediaInfo;
	int x;
	format_t format = 0;

	if (gH323Debug)
		ast_verbose("---   configure_local_rtp\n");

	if (p->rtp) {
		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &p->prefs);
		if (p->dtmfmode & H323_DTMF_RFC2833 && p->dtmfcodec) {
			ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1);
			ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp),
				 p->rtp, p->dtmfcodec, "audio", "telephone-event", 0);
		}
		if (p->dtmfmode & H323_DTMF_CISCO && p->dtmfcodec) {
			ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, 1);
			ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp),
				 p->rtp, p->dtmfcodec, "audio", "cisco-telephone-event", 0);
		}
		/* figure out our local RTP port and tell the H.323 stack about it*/
		ast_rtp_instance_get_local_address(p->rtp, &tmp);
		ast_sockaddr_to_sin(&tmp, &us);

		if (p->rtptimeout) {
			ast_rtp_instance_set_timeout(p->rtp, p->rtptimeout);
		}
		ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
		
	}

	if (p->rtdrcount) {
		if (gH323Debug)
			ast_verbose("Setup RTDR info: %d, %d\n", p->rtdrinterval, p->rtdrcount);
		call->rtdrInterval = p->rtdrinterval;
		call->rtdrCount = p->rtdrcount;
	}


	ast_copy_string(mediaInfo.lMediaIP, ast_inet_ntoa(us.sin_addr), sizeof(mediaInfo.lMediaIP));
	mediaInfo.lMediaPort = ntohs(us.sin_port);
	mediaInfo.lMediaCntrlPort = mediaInfo.lMediaPort +1;
	for (x = 0; 0 != (format = ast_codec_pref_index(&p->prefs, x)); x++) {
		strcpy(mediaInfo.dir, "transmit");
		mediaInfo.cap = ooh323_convertAsteriskCapToH323Cap(format);
		ooAddMediaInfo(call, mediaInfo);
		strcpy(mediaInfo.dir, "receive");
		ooAddMediaInfo(call, mediaInfo);
		if (mediaInfo.cap == OO_G729A) {
			strcpy(mediaInfo.dir, "transmit");
			mediaInfo.cap = OO_G729;
			ooAddMediaInfo(call, mediaInfo);
			strcpy(mediaInfo.dir, "receive");
			ooAddMediaInfo(call, mediaInfo);

			strcpy(mediaInfo.dir, "transmit");
			mediaInfo.cap = OO_G729B;
			ooAddMediaInfo(call, mediaInfo);
			strcpy(mediaInfo.dir, "receive");
			ooAddMediaInfo(call, mediaInfo);
		}
	}

	if (p->udptl) {
		struct ast_sockaddr us_tmp;
		ast_sockaddr_from_sin(&us_tmp, &us);
		ast_udptl_get_us(p->udptl, &us_tmp);
		ast_sockaddr_to_sin(&us_tmp, &us);
	}
	ast_copy_string(mediaInfo.lMediaIP, ast_inet_ntoa(us.sin_addr), sizeof(mediaInfo.lMediaIP));
	mediaInfo.lMediaPort = ntohs(us.sin_port);
	mediaInfo.lMediaCntrlPort = mediaInfo.lMediaPort +1;
	mediaInfo.cap = OO_T38;
	strcpy(mediaInfo.dir, "transmit");
	ooAddMediaInfo(call, mediaInfo);
	strcpy(mediaInfo.dir, "receive");
	ooAddMediaInfo(call, mediaInfo);

	if (gH323Debug)
		ast_verbose("+++   configure_local_rtp\n");

	return 1;
}

void setup_rtp_connection(ooCallData *call, const char *remoteIp, 
								  int remotePort)
{
	struct ooh323_pvt *p = NULL;
	struct sockaddr_in them;
	struct ast_sockaddr tmp;

	if (gH323Debug)
		ast_verbose("---   setup_rtp_connection %s:%d\n", remoteIp, remotePort);

	/* Find the call or allocate a private structure if call not found */
	p = find_call(call); 

	if (!p) {
		ast_log(LOG_ERROR, "Something is wrong: rtp\n");
		return;
	}

	them.sin_family = AF_INET;
	them.sin_addr.s_addr = inet_addr(remoteIp); /* only works for IPv4 */
	them.sin_port = htons(remotePort);
	ast_sockaddr_from_sin(&tmp, &them);
	ast_rtp_instance_set_remote_address(p->rtp, &tmp);

	if (p->writeformat & AST_FORMAT_G726_AAL2) 
                ast_rtp_codecs_payloads_set_rtpmap_type(ast_rtp_instance_get_codecs(p->rtp), p->rtp, 2,
							"audio", "G726-32", AST_RTP_OPT_G726_NONSTANDARD);

	if(gH323Debug)
		ast_verbose("+++   setup_rtp_connection\n");

	return;
}

void close_rtp_connection(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;

   if(gH323Debug)
		ast_verbose("---   close_rtp_connection\n");

	p = find_call(call);
	if (!p) {
      ast_log(LOG_ERROR, "Couldn't find matching call to close rtp "
                         "connection\n");
		return;
	}
	ast_mutex_lock(&p->lock);
	if (p->rtp) {
		ast_rtp_instance_stop(p->rtp);
	}
	ast_mutex_unlock(&p->lock);

   if(gH323Debug)
		ast_verbose("+++   close_rtp_connection\n");

	return;
}

/*
 udptl handling functions
 */

static struct ast_udptl *ooh323_get_udptl_peer(struct ast_channel *chan)
{
	struct ooh323_pvt *p;
	struct ast_udptl *udptl = NULL;

	p = chan->tech_pvt;
	if (!p)
		return NULL;

	ast_mutex_lock(&p->lock);
	if (p->udptl)
		udptl = p->udptl;
	ast_mutex_unlock(&p->lock);
	return udptl;
}

static int ooh323_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
{
	struct ooh323_pvt *p;

	p = chan->tech_pvt;
	if (!p)
		return -1;
	ast_mutex_lock(&p->lock);
	if (udptl) {
		struct ast_sockaddr udptl_addr;
		ast_udptl_get_peer(udptl, &udptl_addr);
		ast_sockaddr_to_sin(&udptl_addr, &p->udptlredirip);
	} else
		memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));

	ast_mutex_unlock(&p->lock);
	return 0;
}

void setup_udptl_connection(ooCallData *call, const char *remoteIp, 
								  int remotePort)
{
	struct ooh323_pvt *p = NULL;
	struct sockaddr_in them;
	struct ast_sockaddr them_addr;

	if (gH323Debug)
		ast_verbose("---   setup_udptl_connection\n");

	/* Find the call or allocate a private structure if call not found */
	p = find_call(call); 

	if (!p) {
		ast_log(LOG_ERROR, "Something is wrong: rtp\n");
		return;
	}

	ast_mutex_lock(&p->lock);
	if (p->owner) {
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return;
		}
	} else {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return;
	}

	them.sin_family = AF_INET;
	them.sin_addr.s_addr = inet_addr(remoteIp); /* only works for IPv4 */
	them.sin_port = htons(remotePort);
	ast_sockaddr_from_sin(&them_addr, &them);
	ast_udptl_set_peer(p->udptl, &them_addr);
	ast_udptl_set_tag(p->udptl, "%s", p->owner->name);
	p->t38_tx_enable = 1;
	p->lastTxT38 = time(NULL);
	if (p->t38support == T38_ENABLED) {
		struct ast_control_t38_parameters parameters = { .request_response = 0 };
		parameters.request_response = AST_T38_NEGOTIATED;
		parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
		parameters.rate = AST_T38_RATE_14400;
		ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
	}
	if (gH323Debug)
		ast_debug(1, "Receiving UDPTL  %s:%d\n", ast_inet_ntoa(them.sin_addr), 
							 ntohs(them.sin_port));

	ast_channel_unlock(p->owner);
	ast_mutex_unlock(&p->lock);

	if(gH323Debug)
		ast_verbose("+++   setup_udptl_connection\n");

	return;
}

void close_udptl_connection(ooCallData *call)
{
	struct ooh323_pvt *p = NULL;

   	if(gH323Debug)
		ast_verbose("---   close_udptl_connection\n");

	p = find_call(call);
	if (!p) {
      		ast_log(LOG_ERROR, "Couldn't find matching call to close udptl "
                         "connection\n");
		return;
	}
	ast_mutex_lock(&p->lock);
	if (p->owner) {
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return;
		}
	} else {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return;
	}

	p->t38_tx_enable = 0;
	if (p->t38support == T38_ENABLED) {
		struct ast_control_t38_parameters parameters = { .request_response = 0 };
		parameters.request_response = AST_T38_TERMINATED;
		ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
	}

	ast_channel_unlock(p->owner);
	ast_mutex_unlock(&p->lock);

   	if(gH323Debug)
		ast_verbose("+++   close_udptl_connection\n");

	return;
}

/* end of udptl handling */

int update_our_aliases(ooCallData *call, struct ooh323_pvt *p)
{
	int updated = -1;
	ooAliases *psAlias = NULL;
	
	if (!call->ourAliases)
		return updated;
	for (psAlias = call->ourAliases; psAlias; psAlias = psAlias->next) {
		if (psAlias->type == T_H225AliasAddress_h323_ID) {
			ast_copy_string(p->callee_h323id, psAlias->value, sizeof(p->callee_h323id));
			updated = 1;
		}
		if (psAlias->type == T_H225AliasAddress_dialedDigits) {
         ast_copy_string(p->callee_dialedDigits, psAlias->value, 
                                        sizeof(p->callee_dialedDigits));
			updated = 1;
		}
		if (psAlias->type == T_H225AliasAddress_url_ID) {
			ast_copy_string(p->callee_url, psAlias->value, sizeof(p->callee_url));
			updated = 1;
		}
		if (psAlias->type == T_H225AliasAddress_email_ID) {
			ast_copy_string(p->callee_email, psAlias->value, sizeof(p->callee_email));
			updated = 1;
		}
	}
	return updated;
}

struct ast_frame *ooh323_rtp_read(struct ast_channel *ast, struct ooh323_pvt *p)
{
	/* Retrieve audio/etc from channel.  Assumes p->lock is already held. */
	struct ast_frame *f;
	static struct ast_frame null_frame = { AST_FRAME_NULL, };
	switch (ast->fdno) {
	case 0:
		f = ast_rtp_instance_read(p->rtp, 0);	/* RTP Audio */
		p->lastrtprx = time(NULL);
		break;
	case 1:
		f = ast_rtp_instance_read(p->rtp, 1);	/* RTCP Control Channel */
		break;
	case 2:
		f = ast_rtp_instance_read(p->vrtp, 0);	/* RTP Video */
		p->lastrtprx = time(NULL);
		break;
	case 3:
		f = ast_rtp_instance_read(p->vrtp, 1);	/* RTCP Control Channel for video */
		break;
	case 5:
		f = ast_udptl_read(p->udptl);		/* UDPTL t.38 data */
		if (gH323Debug) ast_debug(1, "Got UDPTL %d/%d len %d for %s\n",
				f->frametype, f->subclass.integer, f->datalen, ast->name);
		p->lastrtprx = time(NULL);
		break;

	default:
		f = &null_frame;
	}

	if (f && p->owner) {
		/* We already hold the channel lock */
		if (f->frametype == AST_FRAME_VOICE && !p->faxmode) {
			if (f->subclass.codec != p->owner->nativeformats) {
            			ast_debug(1, "Oooh, voice format changed to %s\n", ast_getformatname(f->subclass.codec));
				p->owner->nativeformats = f->subclass.codec;
				ast_set_read_format(p->owner, p->owner->readformat);
				ast_set_write_format(p->owner, p->owner->writeformat);
			}

			if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad &&
				(f->subclass.codec == AST_FORMAT_SLINEAR || f->subclass.codec == AST_FORMAT_ALAW ||
					f->subclass.codec == AST_FORMAT_ULAW)) {
				f = ast_dsp_process(p->owner, p->vad, f);
            			if (f && (f->frametype == AST_FRAME_DTMF)) 
               				ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass.integer);
			}
		}
	}
	return f;
}

void onModeChanged(ooCallData *call, int t38mode) {
        struct ooh323_pvt *p;

	p = find_call(call);
	if (!p) {
		ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken);
		return;
	}

	ast_mutex_lock(&p->lock);

	if (gH323Debug)
       		ast_debug(1, "change mode to %d for %s\n", t38mode, call->callToken);

	if (t38mode == p->faxmode) {
		if (gH323Debug)
			ast_debug(1, "mode for %s is already %d\n", call->callToken,
					t38mode);
		ast_mutex_unlock(&p->lock);
		return;
	}

	if (p->owner) {
		while (p->owner && ast_channel_trylock(p->owner)) {
			ast_debug(1,"Failed to grab lock, trying again\n");
			DEADLOCK_AVOIDANCE(&p->lock);
		}
		if (!p->owner) {
			ast_mutex_unlock(&p->lock);
			ast_log(LOG_ERROR, "Channel has no owner\n");
			return;
		}
	} else {
		ast_mutex_unlock(&p->lock);
		ast_log(LOG_ERROR, "Channel has no owner\n");
		return;
	}

	if (t38mode) {


		if (p->t38support == T38_ENABLED) {

/* AST_T38_CONTROL mode */

			struct ast_control_t38_parameters parameters = { .request_response = 0 };
			parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
			if (call->T38FarMaxDatagram) {
				ast_udptl_set_far_max_datagram(p->udptl, call->T38FarMaxDatagram);
			} else {
				ast_udptl_set_far_max_datagram(p->udptl, 144);
			}
			if (call->T38Version) {
				parameters.version = call->T38Version;
			}
			parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
			parameters.rate = AST_T38_RATE_14400;
			ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, 
							&parameters, sizeof(parameters));
			p->faxmode = 1;

		}
	} else {
		if (p->t38support == T38_ENABLED) {
			struct ast_control_t38_parameters parameters = { .request_response = 0 };
			parameters.request_response = AST_T38_REQUEST_TERMINATE;
			parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
			parameters.rate = AST_T38_RATE_14400;
			ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, 
							&parameters, sizeof(parameters));
		}
		p->faxmode = 0;
		p->t38_init = 0;
	}

	p->chmodepend = 0;
	ast_channel_unlock(p->owner);
	ast_mutex_unlock(&p->lock);
}



int ooh323_convert_hangupcause_asteriskToH323(int cause)
{
	switch (cause) {
	case AST_CAUSE_CALL_REJECTED:
		return OO_REASON_REMOTE_REJECTED;
	case AST_CAUSE_UNALLOCATED:
		return OO_REASON_NOUSER;
	case AST_CAUSE_BUSY:
		return OO_REASON_REMOTE_BUSY;
	case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL:
		return OO_REASON_NOCOMMON_CAPABILITIES;
	case AST_CAUSE_CONGESTION:
		return OO_REASON_REMOTE_BUSY;
	case AST_CAUSE_NO_ANSWER:
		return OO_REASON_REMOTE_NOANSWER;
	case AST_CAUSE_NORMAL:
		return OO_REASON_REMOTE_CLEARED;
	case AST_CAUSE_FAILURE:
	default:
		return OO_REASON_UNKNOWN;
	}

	return 0;


}

int ooh323_convert_hangupcause_h323ToAsterisk(int cause)
{
	switch (cause) {
	case OO_REASON_REMOTE_REJECTED:
		return AST_CAUSE_CALL_REJECTED;
	case OO_REASON_NOUSER: 
		return AST_CAUSE_UNALLOCATED;
	case OO_REASON_REMOTE_BUSY:
	case OO_REASON_LOCAL_BUSY:
		return AST_CAUSE_BUSY;
	case OO_REASON_NOCOMMON_CAPABILITIES:	/* No codecs approved */
		return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
	case OO_REASON_REMOTE_CONGESTED:
	case OO_REASON_LOCAL_CONGESTED:
		return AST_CAUSE_CONGESTION;
	case OO_REASON_REMOTE_NOANSWER:
		return AST_CAUSE_NO_ANSWER;
	case OO_REASON_UNKNOWN: 
	case OO_REASON_INVALIDMESSAGE:
	case OO_REASON_TRANSPORTFAILURE:
		return AST_CAUSE_FAILURE;
	case OO_REASON_REMOTE_CLEARED:
		return AST_CAUSE_NORMAL;
	default:
		return AST_CAUSE_NORMAL;
	}
	/* Never reached */
	return 0;
}

#if 0
void ast_ooh323c_exit()
{
	ooGkClientDestroy();
}
#endif

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Objective Systems H323 Channel");
