Skip to content
Snippets Groups Projects
chan_ooh323.c 119 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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>
     ***/
    
    
    #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;
    
    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,
    
    /* 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 */
    
    	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;
    
    	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;
    
    	struct ooh323_user *next;
    };
    
    /* Profile of valid asterisk peers */
    struct ooh323_peer{
    	ast_mutex_t lock;
    	char        name[256];
    	unsigned    outgoinglimit;
    	unsigned    outUse;
    
    	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;
    
    	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);
    
    
    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 char gAccountcode[80] = DEFAULT_H323ACCNT;
    static int  gAMAFLAGS;
    static char gContext[AST_MAX_EXTENSION] = DEFAULT_CONTEXT;
    
    static int  gIncomingLimit = 1024;
    static int  gOutgoingLimit = 1024;
    
    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) 
    
    	int fmt = 0;
    
    		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);
    
       	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);
       	ast_mutex_lock(&ooh323c_cn_lock);
       	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);
    
    		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);
    
    			} 
    	 	}
    
    		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;
    
    Mark Michelson's avatar
    Mark Michelson committed
    	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);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ouraddr.sin_family = AF_INET;
    
    	ouraddr.sin_addr = ipAddr;
    
    	ast_sockaddr_from_sin(&tmp, &ouraddr);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	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);
    
    	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);
    	pvt->faxmode = 0;
    	pvt->t38support = gT38Support;
    	pvt->rtptimeout = gRTPTimeout;
    
    	pvt->rtdrinterval = gRTDRInterval;
    	pvt->rtdrcount = gRTDRCount;
    
    
    	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 oldformat;
    	int port = 0;
    
    	if (gH323Debug)
    		ast_verbose("---   ooh323_request - data %s format %s\n", (char*)data,  
    
    										ast_getformatname_multiple(formats,FORMAT_STRING_SIZE,format));
    
    
    	oldformat = 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");
    		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->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;
    		}
    
    		p->dtmfcodec = gDTMFCodec;
    		p->t38support = gT38Support;
    		p->rtptimeout = gRTPTimeout;
    
    		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);
    
    	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;
    	
    	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);
    	}
    	ast_mutex_unlock(&p->lock);
    	if (gH323Debug)
    		ast_verbose("+++   ooh323_digit_begin\n");
    
    	return 0;
    }
    
    static int ooh323_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
    {
    	struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt;
    
    	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);
    
    
    	ast_mutex_unlock(&p->lock);
    	if (gH323Debug)
    		ast_verbose("+++   ooh323_digit_end\n");
    
    	return 0;
    }
    
    
    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 "
    
    		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);
    
    		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;