wanpipe/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/chan_woomera.c.svn-base

4180 lines
110 KiB
Plaintext

/*
* Asterisk -- A telephony toolkit for Linux.
*
* Woomera Channel Driver
*
* Copyright (C) 05-07 Nenad Corbic
* Anthony Minessale II
*
* Nenad Corbic <ncorbic@sangoma.com>
* Anthony Minessale II <anthmct@yahoo.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
* =============================================
* v1.16 Nenad Corbic <ncorbic@sangoma.com>
* Added support for Asterisk 1.4
* Updated support for Callweaver
*
* v1.15 Nenad Corbic <ncorbic@sangoma.com>
* Added PRI_CAUSE and Q931-Cause-Code
* in woomera protocol.
*
* v1.14 Nenad Corbic <ncorbic@sangoma.com>
* Updated for session support
*
* v1.13 Nenad Corbic <ncorbic@sangoma.com>
* Added CallWeaver Support
* |->(thanks to Andre Schwaller)
* Updated codec negotiation for
* mutliple profiles.
*
* v1.12 Nenad Corbic <ncorbic@sangoma.com>
* Updated DTMF locking
*
* v1.11 Nenad Corbic <ncorbic@sangoma.com>
* Updated multiple profiles
* Updated Dialect for OPAL Woomera
* Added call logging/debugging
*
* v1.10 Nenad Corbic <ncorbic@sangoma.com>
* Bug fix in incoming hangup
*
* v1.9 Nenad Corbic <ncorbic@sangoma.com>
* Fixed remote asterisk/woomera
* setup.
*
* v1.8 Nenad Corbic <ncorbic@sangoma.com>
* Added Woomera OPAL dialect.
* Code cleanup.
* Added cli call_status
*
* v1.7 Nenad Corbic <ncorbic@sangoma.com>
* Added smgdebug to enable smg debugging
* Added rdnis
*
* v1.6 Nenad Corbic <ncorbic@sangoma.com>
* Added incoming trunk group context
* The trunk number will be added to the
* profile context name.
* Added presentation feature.
*
* v1.5 Nenad Corbic <ncorbic@sangoma.com>
* Use only ALAW and MLAW not SLIN.
* This reduces the load quite a bit.
* Autodetect Format from HELLO Message.
* RxTx Gain supported in woomera.conf as well
* from CLI.
*/
#if defined(CALLWEAVER) && defined(HAVE_CONFIG_H)
#include "confdefs.h"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <math.h>
#include <netinet/tcp.h>
#ifndef CALLWEAVER
#include "asterisk.h"
#include "asterisk/sched.h"
#include "asterisk/astobj.h"
#include "asterisk/lock.h"
#include "asterisk/manager.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/logger.h"
#include "asterisk/frame.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/translate.h"
#include "asterisk/causes.h"
#include "asterisk/dsp.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.16 $")
#else
#include "callweaver.h"
#include "callweaver/sched.h"
#include "callweaver/astobj.h"
#include "callweaver/lock.h"
#include "callweaver/manager.h"
#include "callweaver/channel.h"
#include "callweaver/pbx.h"
#include "callweaver/cli.h"
#include "callweaver/logger.h"
#include "callweaver/frame.h"
#include "callweaver/config.h"
#include "callweaver/module.h"
#include "callweaver/lock.h"
#include "callweaver/translate.h"
#include "callweaver/causes.h"
#include "callweaver/dsp.h"
#include "callweaver.h"
CALLWEAVER_FILE_VERSION(__FILE__, "$Revision: 1.16 $")
// strings...
#define AST_FORMAT_SLINEAR OPBX_FORMAT_SLINEAR
#define AST_FORMAT_ULAW OPBX_FORMAT_ULAW
#define AST_FORMAT_ALAW OPBX_FORMAT_ALAW
#define ast_mutex_t opbx_mutex_t
#define ast_frame opbx_frame
#define ast_verbose opbx_verbose
#define AST_FRIENDLY_OFFSET OPBX_FRIENDLY_OFFSET
#define AST_MUTEX_DEFINE_STATIC OPBX_MUTEX_DEFINE_STATIC
#define AST_CONTROL_PROGRESS OPBX_CONTROL_PROGRESS
#define AST_CAUSE_REQUESTED_CHAN_UNAVAIL OPBX_CAUSE_REQUESTED_CHAN_UNAVAIL
#define AST_CAUSE_NORMAL_CIRCUIT_CONGESTION OPBX_CAUSE_NORMAL_CIRCUIT_CONGESTION
#define AST_CAUSE_USER_BUSY OPBX_CAUSE_USER_BUSY
#define AST_CAUSE_NO_ANSWER OPBX_CAUSE_NO_ANSWER
#define AST_CAUSE_NORMAL_CLEARING OPBX_CAUSE_NORMAL_CLEARING
#define AST_SOFTHANGUP_EXPLICIT OPBX_SOFTHANGUP_EXPLICIT
#define AST_SOFTHANGUP_DEV OPBX_SOFTHANGUP_DEV
#define AST_CAUSE_NORMAL_CLEARING OPBX_CAUSE_NORMAL_CLEARING
#define AST_FRAME_DTMF OPBX_FRAME_DTMF
#define AST_FRAME_CONTROL OPBX_FRAME_CONTROL
#define AST_CONTROL_ANSWER OPBX_CONTROL_ANSWER
#define AST_STATE_UP OPBX_STATE_UP
#define AST_STATE_RINGING OPBX_STATE_RINGING
#define AST_STATE_DOWN OPBX_STATE_DOWN
#define AST_FLAGS_ALL OPBX_FLAGS_ALL
#define AST_FRAME_VOICE OPBX_FRAME_VOICE
#define ASTERISK_GPL_KEY 0
#define ast_channel_tech opbx_channel_tech
#define ast_test_flag opbx_test_flag
#define ast_queue_frame opbx_queue_frame
#define ast_frdup opbx_frdup
#define ast_channel opbx_channel
#define ast_exists_extension opbx_exists_extension
#define ast_hostent opbx_hostent
#define ast_clear_flag opbx_clear_flag
#define ast_log opbx_log
#define ast_set_flag opbx_set_flag
#define ast_copy_string opbx_copy_string
#define ast_set_flag opbx_set_flag
#define ast_set2_flag opbx_set2_flag
#define ast_setstate opbx_setstate
#define ast_test_flag opbx_test_flag
#define ast_softhangup opbx_softhangup
#define ast_true opbx_true
#define ast_false opbx_false
#define ast_strlen_zero opbx_strlen_zero
#define ast_exists_extension opbx_exists_extension
#define ast_frame opbx_frame
#define ast_jb_conf opbx_jb_conf
#define ast_carefulwrite opbx_carefulwrite
#define ast_channel_unregister opbx_channel_unregister
#define ast_cli opbx_cli
#define ast_cli_register opbx_cli_register
#define ast_cli_unregister opbx_cli_unregister
#define ast_jb_read_conf opbx_jb_read_conf
#define ast_mutex_destroy opbx_mutex_destroy
#define ast_mutex_init opbx_mutex_init
#define ast_mutex_lock opbx_mutex_lock
#define ast_mutex_unlock opbx_mutex_unlock
#define ast_mutex_t opbx_mutex_t
#define ast_queue_control opbx_queue_control
#define ast_queue_frame opbx_queue_frame
#define ast_queue_hangup opbx_queue_hangup
#define ast_set_callerid opbx_set_callerid
#define ast_variable opbx_variable
#define ast_pthread_create opbx_pthread_create
#define ast_cli_entry opbx_cli_entry
#define ast_channel_register opbx_channel_register
#define ast_config_load opbx_config_load
#define ast_config_destroy opbx_config_destroy
#define ast_category_browse opbx_category_browse
#define ast_variable_browse opbx_variable_browse
#define ast_gethostbyname opbx_gethostbyname
#define ast_channel_alloc opbx_channel_alloc
#define ast_dsp_new opbx_dsp_new
#define ast_dsp opbx_dsp
#define ast_dsp_set_features opbx_dsp_set_features
#define ast_dsp_digitmode opbx_dsp_digitmode
#define ast_dsp_set_call_progress_zone opbx_dsp_set_call_progress_zone
#define ast_dsp_set_busy_count opbx_dsp_set_busy_count
#define ast_dsp_set_busy_pattern opbx_dsp_set_busy_pattern
#define ast_dsp_process opbx_dsp_process
#define ast_strdupa opbx_strdupa
#define ast_mutex_trylock opbx_mutex_trylock
#define ast_cause2str opbx_cause2str
#define ast_pbx_start opbx_pbx_start
#define ast_hangup opbx_hangup
#endif
#include "g711.h"
#include <errno.h>
//#define MEDIA_ANSWER "MEDIA"
// THE ONE ABOVE OR THE 2 BELOW BUT NOT BOTH
#define MEDIA_ANSWER "ACCEPT"
#define USE_ANSWER 1
extern int option_verbose;
#define WOOMERA_VERSION "v1.16"
static int tech_count = 0;
static const char desc[] = "Woomera Channel Driver";
static const char type[] = "WOOMERA";
static const char tdesc[] = "Woomera Channel Driver";
static char configfile[] = "woomera.conf";
static char smgversion_init=0;
static char smgversion[100] = "N/A";
#ifdef AST_JB
#include "asterisk/abstract_jb.h"
/* Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = ""
};
static struct ast_jb_conf global_jbconf;
#endif /* AST_JB */
#define WOOMERA_SLINEAR 0
#define WOOMERA_ULAW 1
#define WOOMERA_ALAW 2
#define WOOMERA_STRLEN 256
#define WOOMERA_ARRAY_LEN 50
#define WOOMERA_MIN_PORT 9900
#define WOOMERA_MAX_PORT 10799
#define WOOMERA_BODYLEN 2048
#define WOOMERA_LINE_SEPARATOR "\r\n"
#define WOOMERA_RECORD_SEPARATOR "\r\n\r\n"
#define WOOMERA_DEBUG_PREFIX "**[WOOMERA]** "
#define WOOMERA_DEBUG_LINE "--------------------------------------------------------------------------------"
#define WOOMERA_HARD_TIMEOUT -1000
#define WOOMERA_QLEN 10
/* this macro is not in all versions of asterisk */
#ifdef OLDERAST
#define ASTOBJ_CONTAINER_UNLINK(container,obj) \
({ \
typeof((container)->head) found = NULL; \
typeof((container)->head) prev = NULL; \
ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
if (iterator== obj) { \
found = iterator; \
found->next[0] = NULL; \
ASTOBJ_CONTAINER_WRLOCK(container); \
if (prev) \
prev->next[0] = next; \
else \
(container)->head = next; \
ASTOBJ_CONTAINER_UNLOCK(container); \
} \
prev = iterator; \
} while (0)); \
found; \
})
#endif
#define FRAME_LEN 480
#if 0
static int WFORMAT = AST_FORMAT_ALAW; //AST_FORMAT_SLINEAR;
#else
static int WFORMAT = AST_FORMAT_SLINEAR;
#endif
typedef enum {
WFLAG_EXISTS = (1 << 0),
WFLAG_EVENT = (1 << 1),
WFLAG_CONTENT = (1 << 2),
} WFLAGS;
typedef enum {
WCFLAG_NOWAIT = (1 << 0)
} WCFLAGS;
typedef enum {
PFLAG_INBOUND = (1 << 0),
PFLAG_OUTBOUND = (1 << 1),
PFLAG_DYNAMIC = (1 << 2),
PFLAG_DISABLED = (1 << 3)
} PFLAGS;
typedef enum {
TFLAG_MEDIA = (1 << 0),
TFLAG_INBOUND = (1 << 1),
TFLAG_OUTBOUND = (1 << 2),
TFLAG_INCOMING = (1 << 3),
TFLAG_PARSE_INCOMING = (1 << 4),
TFLAG_ACTIVATE = (1 << 5),
TFLAG_DTMF = (1 << 6),
TFLAG_DESTROY = (1 << 7),
TFLAG_ABORT = (1 << 8),
TFLAG_PBX = (1 << 9),
TFLAG_ANSWER = (1 << 10),
TFLAG_INTHREAD = (1 << 11),
TFLAG_TECHHANGUP = (1 << 12),
TFLAG_DESTROYED = (1 << 13),
TFLAG_UP = (1 << 14)
} TFLAGS;
static int usecnt = 0;
struct woomera_message {
char callid[WOOMERA_STRLEN];
int mval;
char command[WOOMERA_STRLEN];
char command_args[WOOMERA_STRLEN];
char names[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN];
char values[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN];
char body[WOOMERA_BODYLEN];
char cause[WOOMERA_STRLEN];
unsigned int flags;
int last;
unsigned int queue_id;
struct woomera_message *next;
};
static struct {
int next_woomera_port;
int debug;
int panic;
int more_threads;
ast_mutex_t woomera_port_lock;
} globals;
struct woomera_event_queue {
struct woomera_message *head;
ast_mutex_t lock;
};
struct woomera_profile {
ASTOBJ_COMPONENTS(struct woomera_profile);
ast_mutex_t iolock;
ast_mutex_t call_count_lock;
char woomera_host[WOOMERA_STRLEN];
int max_calls;
int call_count;
int woomera_port;
char audio_ip[WOOMERA_STRLEN];
char context[WOOMERA_STRLEN];
pthread_t thread;
unsigned int flags;
int thread_running;
int dtmf_enable;
int faxdetect;
int woomera_socket;
struct woomera_event_queue event_queue;
int jb_enable;
int progress_enable;
int coding;
float rxgain_val;
float txgain_val;
unsigned char rxgain[256];
unsigned char txgain[256];
int call_out;
int call_in;
int call_ok;
int call_end;
int call_abort;
};
struct private_object {
ASTOBJ_COMPONENTS(struct private_object);
ast_mutex_t iolock;
struct ast_channel *owner;
struct sockaddr_in udpread;
struct sockaddr_in udpwrite;
int command_channel;
int udp_socket;
unsigned int flags;
struct ast_frame frame;
short fdata[FRAME_LEN + AST_FRIENDLY_OFFSET];
struct woomera_message call_info;
struct woomera_profile *profile;
char dest[WOOMERA_STRLEN];
char proto[WOOMERA_STRLEN];
int port;
struct timeval started;
int timeout;
char dtmfbuf[WOOMERA_STRLEN];
char cid_name[WOOMERA_STRLEN];
char cid_num[WOOMERA_STRLEN];
char *cid_rdnis;
int cid_pres;
char ds[WOOMERA_STRLEN];
struct ast_dsp *dsp;
int ast_dsp;
int dsp_features;
int faxdetect;
int faxhandled;
int call_count;
char callid[WOOMERA_STRLEN];
pthread_t thread;
unsigned int callno;
int refcnt;
struct woomera_event_queue event_queue;
unsigned int coding;
int pri_cause;
#ifdef AST_JB
struct ast_jb_conf jbconf;
#endif /* AST_JB */
};
typedef struct private_object private_object;
typedef struct woomera_message woomera_message;
typedef struct woomera_profile woomera_profile;
typedef struct woomera_event_queue woomera_event_queue;
static void my_ast_softhangup(struct ast_channel *chan, private_object *tech_pvt, int cause)
{
#if 1
ast_queue_hangup(chan);
//ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
#else
struct ast_frame f = { AST_FRAME_NULL };
if (chan) {
chan->_softhangup |= cause;
ast_queue_frame(chan, &f);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
#endif
#if 0
if (tech_pvt->dsp) {
tech_pvt->dsp_features &= ~DSP_FEATURE_DTMF_DETECT;
ast_dsp_set_features(tech_pvt->dsp, tech_pvt->dsp_features);
tech_pvt->ast_dsp=0;
}
#endif
}
//static struct sched_context *sched;
static struct private_object_container {
ASTOBJ_CONTAINER_COMPONENTS(private_object);
} private_object_list;
static struct woomera_profile_container {
ASTOBJ_CONTAINER_COMPONENTS(woomera_profile);
} woomera_profile_list;
static woomera_profile default_profile;
/* some locks you will use for use count and for exclusive access to the main linked-list of private objects */
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
AST_MUTEX_DEFINE_STATIC(lock);
/* local prototypes */
static void woomera_close_socket(int *socket);
static void global_set_flag(int flags);
static void woomera_printf(woomera_profile *profile, int fd, char *fmt, ...);
static char *woomera_message_header(woomera_message *wmsg, char *key);
static int woomera_enqueue_event(woomera_event_queue *event_queue, woomera_message *wmsg);
static int woomera_dequeue_event(woomera_event_queue *event_queue, woomera_message *wmsg);
static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout, woomera_profile *profile, woomera_event_queue *event_queue);
static int woomera_message_parse_wait(private_object *tech_pvt,woomera_message *wmsg);
static int waitfor_socket(int fd, int timeout);
static int woomera_profile_thread_running(woomera_profile *profile, int set, int new);
static int woomera_locate_socket(woomera_profile *profile, int *woomera_socket);
static void *woomera_thread_run(void *obj);
static void launch_woomera_thread(woomera_profile *profile);
static void destroy_woomera_profile(woomera_profile *profile);
static woomera_profile *clone_woomera_profile(woomera_profile *new_profile, woomera_profile *default_profile);
static woomera_profile *create_woomera_profile(woomera_profile *default_profile);
static int config_woomera(void);
static int create_udp_socket(char *ip, int port, struct sockaddr_in *sockaddr, int client);
static int connect_woomera(int *new_socket, woomera_profile *profile, int flags);
static int init_woomera(void);
static int woomera_cli(int fd, int argc, char *argv[]);
static void tech_destroy(private_object *tech_pvt, struct ast_channel *owner);
static struct ast_channel *woomera_new(const char *type, int format, void *data, int *cause, woomera_profile *profile);
static int launch_tech_thread(private_object *tech_pvt);
static int tech_create_read_socket(private_object *tech_pvt);
static int tech_activate(private_object *tech_pvt);
static int tech_init(private_object *tech_pvt, woomera_profile *profile, int flags);
static void *tech_monitor_thread(void *obj);
static void tech_monitor_in_one_thread(void);
static struct ast_channel * tech_get_owner( private_object *tech_pvt);
int usecount(void);
#if 0
static char *key(void);
static char *description(void);
#endif
int load_module(void);
int unload_module(void);
int reload(void);
/********************CHANNEL METHOD PROTOTYPES*******************
* You may or may not need all of these methods, remove any unnecessary functions/protos/mappings as needed.
*
*/
static struct ast_channel *tech_requester(const char *type, int format, void *data, int *cause);
static int tech_send_digit(struct ast_channel *self, char digit);
#ifdef AST14
static int tech_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
#endif
static int tech_call(struct ast_channel *self, char *dest, int timeout);
static int tech_hangup(struct ast_channel *self);
static int tech_answer(struct ast_channel *self);
static struct ast_frame *tech_read(struct ast_channel *self);
static struct ast_frame *tech_exception(struct ast_channel *self);
static int tech_write(struct ast_channel *self, struct ast_frame *frame);
#if 0
static int tech_indicate(struct ast_channel *self, int condition);
#endif
static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int tech_send_html(struct ast_channel *self, int subclass, const char *data, int datalen);
static int tech_send_text(struct ast_channel *self, const char *text);
static int tech_send_image(struct ast_channel *self, struct ast_frame *frame);
static int tech_setoption(struct ast_channel *self, int option, void *data, int datalen);
static int tech_queryoption(struct ast_channel *self, int option, void *data, int *datalen);
//static enum ast_bridge_result tech_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
static int tech_transfer(struct ast_channel *self, const char *newdest);
static int tech_write_video(struct ast_channel *self, struct ast_frame *frame);
//static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct ast_channel *bridge);
static int woomera_event_incoming (private_object *tech_pvt);
static int woomera_event_media (private_object *tech_pvt, woomera_message *wmsg);
/********************************************************************************
* Constant structure for mapping local methods to the core interface.
* This structure only needs to contain the methods the channel requires to operate
* Not every channel needs all of them defined.
*/
static const struct ast_channel_tech technology = {
.type = type,
.description = tdesc,
.capabilities = (AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW),
.requester = tech_requester,
#ifdef AST14
.send_digit_begin = tech_send_digit,
.send_digit_end = tech_digit_end,
#else
.send_digit = tech_send_digit,
#endif
.call = tech_call,
//.bridge = tech_bridge,
.hangup = tech_hangup,
.answer = tech_answer,
.transfer = tech_transfer,
.write_video = tech_write_video,
.read = tech_read,
.write = tech_write,
.exception = tech_exception,
//.indicate = tech_indicate,
.fixup = tech_fixup,
.send_html = tech_send_html,
.send_text = tech_send_text,
.send_image = tech_send_image,
.setoption = tech_setoption,
.queryoption = tech_queryoption,
//.bridged_channel = tech_bridged_channel,
.transfer = tech_transfer,
};
static void woomera_close_socket(int *socket)
{
if (*socket > -1) {
close(*socket);
}
*socket = -1;
}
static int woomera_message_reply_ok(woomera_message *wmsg)
{
if (!(wmsg->mval >= 200 && wmsg->mval <= 299)) {
return -1;
}
return 0;
}
static void global_set_flag(int flags)
{
private_object *tech_pvt;
ASTOBJ_CONTAINER_TRAVERSE(&private_object_list, 1, do {
ASTOBJ_RDLOCK(iterator);
tech_pvt = iterator;
ast_set_flag(tech_pvt, flags);
ASTOBJ_UNLOCK(iterator);
} while(0));
}
static void woomera_send_progress(private_object *tech_pvt)
{
struct ast_channel *owner = tech_get_owner(tech_pvt);
if (tech_pvt->profile->progress_enable && owner){
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Sending Progress %s\n",tech_pvt->callid);
}
ast_queue_control(owner,
AST_CONTROL_PROGRESS);
}
}
static uint32_t string_to_release(char *code)
{
if (code) {
if (!strcasecmp(code, "CHANUNAVAIL")) {
return AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
}
if (!strcasecmp(code, "INVALID")) {
return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
}
if (!strcasecmp(code, "ERROR")) {
return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
}
if (!strcasecmp(code, "CONGESTION")) {
return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
}
if (!strcasecmp(code, "BUSY")) {
return AST_CAUSE_USER_BUSY;
}
if (!strcasecmp(code, "NOANSWER")) {
return AST_CAUSE_NO_ANSWER;
}
if (!strcasecmp(code, "ANSWER")) {
return AST_CAUSE_NORMAL_CLEARING;
}
if (!strcasecmp(code, "CANCEL")) {
return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
}
if (!strcasecmp(code, "UNKNOWN")) {
return AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
}
}
return AST_CAUSE_NORMAL_CLEARING;
}
static void woomera_printf(woomera_profile *profile, int fd, char *fmt, ...)
{
char *stuff;
int res = 0;
if (fd < 0) {
if (globals.debug > 4) {
ast_log(LOG_ERROR, "Not gonna write to fd %d\n", fd);
}
return;
}
va_list ap;
va_start(ap, fmt);
#ifdef SOLARIS
stuff = (char *)malloc(10240);
vsnprintf(stuff, 10240, fmt, ap);
#else
res = vasprintf(&stuff, fmt, ap);
#endif
va_end(ap);
if (res == -1) {
ast_log(LOG_ERROR, "Out of memory\n");
} else {
if (profile && globals.debug) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "Send Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, stuff);
}
}
ast_carefulwrite(fd, stuff, strlen(stuff), 100);
free(stuff);
}
}
static char *woomera_message_header(woomera_message *wmsg, char *key)
{
int x = 0;
char *value = NULL;
#if 0
if (!strcasecmp(wmsg->command,"HANGUP")) {
ast_log(LOG_NOTICE, "Message Header for HANGUP\n");
for (x = 0 ; x < wmsg->last ; x++) {
ast_log(LOG_NOTICE, "Name=%s Value=%s\n",
wmsg->names[x],wmsg->values[x]);
if (!strcasecmp(wmsg->names[x], key)) {
value = wmsg->values[x];
break;
}
}
}
#endif
for (x = 0 ; x < wmsg->last ; x++) {
if (!strcasecmp(wmsg->names[x], key)) {
value = wmsg->values[x];
break;
}
}
return value;
}
static int woomera_enqueue_event(woomera_event_queue *event_queue, woomera_message *wmsg)
{
woomera_message *new, *mptr;
if ((new = malloc(sizeof(woomera_message)))) {
ast_mutex_lock(&event_queue->lock);
memcpy(new, wmsg, sizeof(woomera_message));
new->next = NULL;
if (!event_queue->head) {
event_queue->head = new;
} else {
for (mptr = event_queue->head; mptr && mptr->next ; mptr = mptr->next);
mptr->next = new;
}
ast_mutex_unlock(&event_queue->lock);
return 1;
} else {
ast_log(LOG_ERROR, "Memory Allocation Error!\n");
}
return 0;
}
static int woomera_dequeue_event(woomera_event_queue *event_queue, woomera_message *wmsg)
{
woomera_message *mptr = NULL;
ast_mutex_lock(&event_queue->lock);
if (event_queue->head) {
mptr = event_queue->head;
event_queue->head = mptr->next;
}
if (mptr) {
memcpy(wmsg, mptr, sizeof(woomera_message));
}
ast_mutex_unlock(&event_queue->lock);
if (mptr){
free(mptr);
return 1;
} else {
memset(wmsg, 0, sizeof(woomera_message));
}
return 0;
}
static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout,
woomera_profile *profile, woomera_event_queue *event_queue)
{
char *cur, *cr, *next = NULL, *eor = NULL;
char buf[2048];
int res = 0, bytes = 0, sanity = 0;
struct timeval started, ended;
int elapsed, loops = 0;
int failto = 0;
memset(wmsg, 0, sizeof(woomera_message));
if (fd < 0) {
return -1;
}
gettimeofday(&started, NULL);
memset(buf, 0, sizeof(buf));
if (timeout < 0) {
timeout = abs(timeout);
failto = 1;
} else if(timeout == 0) {
timeout = -1;
}
while (!(eor = strstr(buf, WOOMERA_RECORD_SEPARATOR))) {
if (!profile->thread_running) {
return -1;
}
if (globals.panic > 2) {
return -1;
}
/* Keep things moving.
Stupid Sockets -Homer Simpson */
woomera_printf(NULL, fd, "%s", WOOMERA_RECORD_SEPARATOR);
if((res = waitfor_socket(fd, (timeout > 0 ? timeout : 100)) > 0)) {
res = recv(fd, buf, sizeof(buf), MSG_PEEK);
if (res == 0) {
sanity++;
} else if (res < 0) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "{%s} error during packet retry #%d\n", profile->name, loops);
}
return res;
} else if (loops && globals.debug) {
//ast_verbose(WOOMERA_DEBUG_PREFIX "{%s} Didnt get complete packet retry #%d\n", profile->name, loops);
woomera_printf(NULL, fd, "%s", WOOMERA_RECORD_SEPARATOR);
usleep(100);
}
if (res > 0) {
sanity=0;
}
}
gettimeofday(&ended, NULL);
elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000));
if (res < 0) {
return res;
}
if (sanity > 1000) {
if (globals.debug > 2) {
ast_log(LOG_ERROR, "{%s} Failed Sanity Check! [errors] chfd=%d\n",
profile->name,fd);
}
return -100;
}
if (timeout > 0 && (elapsed > timeout)) {
return failto ? -1 : 0;
}
loops++;
}
*eor = '\0';
bytes = strlen(buf) + 4;
memset(buf, 0, sizeof(buf));
res = read(fd, buf, bytes);
next = buf;
if (globals.debug) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "Receive Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, buf);
}
}
while((cur = next)) {
if ((cr = strstr(cur, WOOMERA_LINE_SEPARATOR))) {
*cr = '\0';
next = cr + (sizeof(WOOMERA_LINE_SEPARATOR) - 1);
if (!strcmp(next, WOOMERA_RECORD_SEPARATOR)) {
break;
}
}
if (ast_strlen_zero(cur)) {
break;
}
if (!wmsg->last) {
ast_set_flag(wmsg, WFLAG_EXISTS);
if (!strncasecmp(cur, "EVENT", 5)) {
cur += 6;
ast_set_flag(wmsg, WFLAG_EVENT);
if (cur && (cr = strchr(cur, ' '))) {
char *id;
*cr = '\0';
cr++;
id = cr;
if (cr && (cr = strchr(cr, ' '))) {
*cr = '\0';
cr++;
strncpy(wmsg->command_args, cr, WOOMERA_STRLEN);
}
if(id) {
ast_copy_string(wmsg->callid, id, sizeof(wmsg->callid));
}
}
} else {
if (cur && (cur = strchr(cur, ' '))) {
*cur = '\0';
cur++;
wmsg->mval = atoi(buf);
} else {
ast_log(LOG_WARNING, "Malformed Message!\n");
break;
}
}
if (cur) {
strncpy(wmsg->command, cur, WOOMERA_STRLEN);
} else {
ast_log(LOG_WARNING, "Malformed Message!\n");
break;
}
} else {
char *name, *val;
name = cur;
if ((val = strchr(name, ':'))) {
*val = '\0';
val++;
while (*val == ' ') {
*val = '\0';
val++;
}
strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN);
}
strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN);
if (name && val && !strcasecmp(name, "content-type")) {
ast_set_flag(wmsg, WFLAG_CONTENT);
bytes = atoi(val);
}
if (name && val && !strcasecmp(name, "content-length")) {
ast_set_flag(wmsg, WFLAG_CONTENT);
bytes = atoi(val);
}
}
wmsg->last++;
}
wmsg->last--;
if (bytes && ast_test_flag(wmsg, WFLAG_CONTENT)) {
read(fd, wmsg->body, (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes);
if (globals.debug) {
if (option_verbose > 2) {
ast_verbose("%s\n", wmsg->body);
}
}
}
if (event_queue && ast_test_flag(wmsg, WFLAG_EVENT)) {
if (globals.debug) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "Queue Event: {%s} [%s]\n", profile->name, wmsg->command);
}
}
/* we don't want events we want a reply so we will stash them for later */
woomera_enqueue_event(event_queue, wmsg);
/* call ourself recursively to find the reply. we'll keep doing this as long we get events.
* wmsg will be overwritten but it's ok we just queued it.
*/
return woomera_message_parse(fd, wmsg, timeout, profile, event_queue);
} else if (wmsg->mval > 99 && wmsg->mval < 200) {
/* reply in the 100's are nice but we need to wait for another reply
call ourself recursively to find the reply > 199 and forget this reply.
*/
return woomera_message_parse(fd, wmsg, timeout, profile, event_queue);
} else {
return ast_test_flag(wmsg, WFLAG_EXISTS);
}
}
static int woomera_message_parse_wait(private_object *tech_pvt, woomera_message *wmsg)
{
int err=0;
for (;;) {
if (ast_test_flag(tech_pvt, TFLAG_ABORT)){
return -1;
}
err=woomera_message_parse(tech_pvt->command_channel,
wmsg,
100,
tech_pvt->profile,
&tech_pvt->event_queue);
if (err == 0) {
/* This is a timeout */
continue;
}
break;
}
return err;
}
static int tech_create_read_socket(private_object *tech_pvt)
{
int retry=0;
retry_udp:
ast_mutex_lock(&globals.woomera_port_lock);
globals.next_woomera_port++;
if (globals.next_woomera_port >= WOOMERA_MAX_PORT) {
globals.next_woomera_port = WOOMERA_MIN_PORT;
}
tech_pvt->port = globals.next_woomera_port;
ast_mutex_unlock(&globals.woomera_port_lock);
if ((tech_pvt->udp_socket = create_udp_socket(tech_pvt->profile->audio_ip, tech_pvt->port, &tech_pvt->udpread, 0)) > -1) {
struct ast_channel *owner = tech_get_owner(tech_pvt);
if (owner) {
owner->fds[0] = tech_pvt->udp_socket;
} else {
ast_log(LOG_ERROR, "Tech_pvt has no OWNER! %i\n",__LINE__);
}
} else {
retry++;
if (retry <= 1) {
goto retry_udp;
}
if (globals.debug > 2) {
ast_log(LOG_ERROR,
"Error CREATING READ udp socket %s/%i %s (%p) %s %s\n",
tech_pvt->profile->audio_ip,tech_pvt->port, strerror(errno),tech_pvt,tech_pvt->callid,
ast_test_flag(tech_pvt, TFLAG_OUTBOUND) ? "OUT":"IN");
}
}
return tech_pvt->udp_socket;
}
#define WOOMERA_MAX_CALLS 600
static struct private_object *tech_pvt_idx[WOOMERA_MAX_CALLS];
static ast_mutex_t tech_pvt_idx_lock[WOOMERA_MAX_CALLS];
static int tech_activate(private_object *tech_pvt)
{
int retry_activate_call=0;
woomera_message wmsg;
char *callid;
int err=0;
memset(&wmsg,0,sizeof(wmsg));
retry_activate_again:
if (!tech_pvt) {
ast_log(LOG_ERROR, "Critical Error: Where's my tech_pvt?\n");
return -1;
}
if((connect_woomera(&tech_pvt->command_channel, tech_pvt->profile, 0)) > -1) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE,
"Connected to woomera! chfd=%i port=%i dir=%s callid=%s Count=%i tpvt=%p\n",
tech_pvt->command_channel,
tech_pvt->port,
ast_test_flag(tech_pvt, TFLAG_OUTBOUND)?"OUT":"IN",
ast_test_flag(tech_pvt, TFLAG_OUTBOUND)?"N/A" :
tech_pvt->callid,
tech_pvt->call_count,
tech_pvt);
}
} else {
if (retry_activate_call <= 3) {
retry_activate_call++;
goto retry_activate_again;
}
if (globals.debug > 1 && option_verbose > 1) {
ast_log(LOG_ERROR, "Error: %s call connect to TCP/Woomera Server! tpvt=%p: %s\n",
ast_test_flag(tech_pvt, TFLAG_OUTBOUND)?"Out":"In",
tech_pvt,strerror(errno));
}
goto tech_activate_failed;
}
retry_activate_call=0;
if (ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
if (strlen(tech_pvt->proto) > 1) {
woomera_printf(tech_pvt->profile,
tech_pvt->command_channel,
"CALL %s:%s%s"
"Raw-Audio: %s:%d%s"
"Local-Name: %s!%s%s"
"Local-Number:%s%s"
"Presentation:%d%s"
"Screening:%d%s"
"RDNIS:%s%s",
tech_pvt->proto,
tech_pvt->dest,
WOOMERA_LINE_SEPARATOR,
tech_pvt->profile->audio_ip,
tech_pvt->port,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_name,
tech_pvt->cid_num,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_num,
WOOMERA_LINE_SEPARATOR,
(tech_pvt->cid_pres>>5)&0x7,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_pres&0xF,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_rdnis?tech_pvt->cid_rdnis:"",
WOOMERA_RECORD_SEPARATOR
);
} else {
woomera_printf(tech_pvt->profile,
tech_pvt->command_channel,
"CALL %s%s"
"Raw-Audio: %s:%d%s"
"Local-Name: %s!%s%s"
"Local-Number:%s%s"
"Presentation:%d%s"
"Screening:%d%s"
"RDNIS:%s%s",
tech_pvt->dest,
WOOMERA_LINE_SEPARATOR,
tech_pvt->profile->audio_ip,
tech_pvt->port,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_name,
tech_pvt->cid_num,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_num,
WOOMERA_LINE_SEPARATOR,
(tech_pvt->cid_pres>>5)&0x7,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_pres&0xF,
WOOMERA_LINE_SEPARATOR,
tech_pvt->cid_rdnis?tech_pvt->cid_rdnis:"",
WOOMERA_RECORD_SEPARATOR
);
}
err=woomera_message_parse_wait(tech_pvt,&wmsg);
if (err < 0 || woomera_message_reply_ok(&wmsg) != 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
}
callid = woomera_message_header(&wmsg, "Unique-Call-Id");
if (callid) {
ast_copy_string(tech_pvt->callid,callid,sizeof(wmsg.callid));
}
} else {
ast_set_flag(tech_pvt, TFLAG_PARSE_INCOMING);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "%s:%d Incoming Call %s tpvt=%p\n",
__FUNCTION__,__LINE__,
tech_pvt->callid,tech_pvt);
}
woomera_printf(tech_pvt->profile,
tech_pvt->command_channel,
"PROCEED %s%s"
"Unique-Call-Id: %s%s",
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
err=woomera_message_parse_wait(tech_pvt,&wmsg);
if (err < 0 || woomera_message_reply_ok(&wmsg) != 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
/* Do not hangup on main because
* socket connection has been
* established */
}
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH ACTIVATE OK tech_pvt=%p\n",tech_pvt);
}
return 0;
tech_activate_failed:
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH ACTIVATE FAILED tech_pvt=%p\n",tech_pvt);
}
woomera_close_socket(&tech_pvt->command_channel);
ast_set_flag(tech_pvt, TFLAG_ABORT);
/* At this point we cannot estabilsh a woomer
* socket to the smg. The SMG still doesnt know
* about the incoming call that is now pending.
* We must send a message to SMG to hangup the call */
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Error: %s Call %s tpvt=%p Failed!\n",
ast_test_flag(tech_pvt, TFLAG_OUTBOUND) ? "OUT":"IN",
tech_pvt->callid,tech_pvt);
}
return -1;
}
static int tech_init(private_object *tech_pvt, woomera_profile *profile, int flags)
{
struct ast_channel *self = tech_get_owner(tech_pvt);
gettimeofday(&tech_pvt->started, NULL);
if (profile) {
tech_pvt->profile = profile;
} else {
ast_log(LOG_ERROR, "ERROR: No Tech profile on init!\n");
ast_set_flag(tech_pvt, TFLAG_ABORT);
return -1;
}
ast_set_flag(tech_pvt, flags);
if (tech_pvt->udp_socket < 0) {
int rc;
rc=tech_create_read_socket(tech_pvt);
if (rc < 0){
if (globals.debug > 2) {
ast_log(LOG_ERROR, "ERROR: Failed to create UDP Socket (%p)!\n",
tech_pvt);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
return -1;
}
}
ast_set_flag(tech_pvt, flags);
tech_pvt->coding = profile->coding;
self->nativeformats = tech_pvt->coding;
self->writeformat = self->rawwriteformat = self->readformat = tech_pvt->coding;
tech_pvt->frame.subclass = tech_pvt->coding;
if (profile->dtmf_enable) {
tech_pvt->dsp_features=0;
tech_pvt->dsp = ast_dsp_new();
if (tech_pvt->dsp) {
#if 0
i->dsp_features = features & ~DSP_PROGRESS_TALK;
/* We cannot do progress detection until receives PROGRESS message */
if (i->outgoing && (i->sig == SIG_PRI)) {
/* Remember requested DSP features, don't treat
talking as ANSWER */
features = 0;
}
#endif
tech_pvt->dsp_features |= DSP_FEATURE_DTMF_DETECT;
//tech_pvt->dsp_features |= DSP_FEATURE_BUSY_DETECT;
//tech_pvt->dsp_features |= DSP_FEATURE_CALL_PROGRESS;
//tech_pvt->dsp_features |= DSP_FEATURE_FAX_DETECT;
ast_dsp_set_features(tech_pvt->dsp, tech_pvt->dsp_features);
ast_dsp_digitmode(tech_pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
tech_pvt->ast_dsp=1;
#if 0
if (!ast_strlen_zero(progzone))
ast_dsp_set_call_progress_zone(tech_pvt->dsp, progzone);
if (i->busydetect && CANBUSYDETECT(i)) {
ast_dsp_set_busy_count(tech_pvt->dsp, i->busycount);
ast_dsp_set_busy_pattern(tech_pvt->dsp, i->busy_tonelength, ->busy_quietlength);
}
#endif
}
}
if (profile && profile->faxdetect) {
tech_pvt->faxdetect=1;
}
if (profile->jb_enable) {
#ifdef AST_JB
/* Assign default jb conf to the new zt_pvt */
memcpy(&tech_pvt->jbconf, &global_jbconf, sizeof(struct ast_jb_conf));
ast_jb_configure(chan, &tech_pvt->jbconf);
#else
ast_log(LOG_ERROR, "Asterisk Jitter Buffer Not Compiled!\n");
#endif
}
/* Asterisk being asterisk and all allows approx 1 nanosecond
* to try and establish a connetion here before it starts crying.
* Now asterisk, being unsure of it's self will not enforce a lock while we work
* and after even a 1 second delay it will give up on the lock and mess everything up
* This stems from the fact that asterisk will scan it's list of channels constantly for
* silly reasons like tab completion and cli output.
*
* Anyway, since we've already spent that nanosecond with the previous line of code
* tech_create_read_socket(tech_pvt); to setup a read socket
* which, by the way, asterisk insists we have before going any furthur.
* So, in short, we are between a rock and a hard place and asterisk wants us to open a socket here
* but it too impaitent to wait for us to make sure it's ready so in the case of outgoing calls
* we will defer the rest of the socket establishment process to the monitor thread. This is, of course, common
* knowledge since asterisk abounds in documentation right?, sorry to bother you with all this!
*/
if (globals.more_threads) {
int err;
ast_set_flag(tech_pvt, TFLAG_ACTIVATE);
/* we're gonna try "wasting" a thread to do a better realtime monitoring */
err=launch_tech_thread(tech_pvt);
if (err) {
ast_log(LOG_ERROR, "Error: Failed to Lauch the thread\n");
ast_clear_flag(tech_pvt, TFLAG_ACTIVATE);
ast_set_flag(tech_pvt, TFLAG_ABORT);
return -1;
}
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
if (ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
tech_pvt->profile->call_out++;
} else {
tech_pvt->profile->call_in++;
}
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
} else {
if (ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
ast_set_flag(tech_pvt, TFLAG_ACTIVATE);
} else {
tech_activate(tech_pvt);
}
}
ast_mutex_lock(&profile->call_count_lock);
profile->call_count++;
tech_pvt->call_count = profile->call_count;
ast_mutex_unlock(&profile->call_count_lock);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH INIT tech_pvt=%p c=%p (use=%i)\n",
tech_pvt,tech_pvt->owner,usecount());
}
return 0;
}
static void tech_destroy(private_object *tech_pvt, struct ast_channel *owner)
{
ASTOBJ_CONTAINER_UNLINK(&private_object_list, tech_pvt);
ast_set_flag(tech_pvt, TFLAG_DESTROY);
ast_set_flag(tech_pvt, TFLAG_ABORT);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Tech Destroy callid=%s tpvt=%p %s/%d\n",
tech_pvt->callid,
tech_pvt,
tech_pvt->profile ? tech_pvt->profile->audio_ip : "NA",
tech_pvt->port);
}
if (tech_pvt->profile && tech_pvt->command_channel > -1) {
if (globals.debug > 1 && option_verbose > 1) {
ast_log(LOG_NOTICE, "+++DESTROY sent HANGUP %s\n",
tech_pvt->callid);
}
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"hangup %s%scause: %s%sQ931-Cause-Code: %d%sUnique-Call-Id: %s%s",
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
tech_pvt->ds,
WOOMERA_LINE_SEPARATOR,
tech_pvt->pri_cause,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"bye%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
woomera_close_socket(&tech_pvt->command_channel);
}
woomera_close_socket(&tech_pvt->command_channel);
woomera_close_socket(&tech_pvt->udp_socket);
if (owner) {
struct ast_channel *chan=owner;
if (!ast_test_flag(tech_pvt, TFLAG_PBX) &&
!chan->pbx &&
!ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "DESTROY Destroying Owner %p: ast_hangup()\n",
tech_pvt);
}
chan->tech_pvt = NULL;
ast_hangup(chan);
} else {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "DESTROY Destroying Owner %p: softhangup\n",
tech_pvt);
}
/* softhangup needs tech_pvt pointer */
chan->tech_pvt = NULL;
my_ast_softhangup(chan, tech_pvt, AST_SOFTHANGUP_EXPLICIT);
}
tech_pvt->owner=NULL;
}else{
tech_pvt->owner=NULL;
}
if (tech_pvt->profile) {
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
tech_pvt->profile->call_count--;
if (tech_pvt->profile->call_count < 0) {
tech_pvt->profile->call_count=0;
}
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
}
/* Tech profile is allowed to be null in case the call
* is blocked from the call_count */
#if 0
ast_log(LOG_NOTICE, "---- Call END %p %s ----------------------------\n",
tech_pvt,tech_pvt->callid);
#endif
tech_count--;
if (tech_pvt->dsp){
tech_pvt->dsp_features &= ~DSP_FEATURE_DTMF_DETECT;
ast_dsp_set_features(tech_pvt->dsp, tech_pvt->dsp_features);
tech_pvt->ast_dsp=0;
free(tech_pvt->dsp);
tech_pvt->dsp=NULL;
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "DESTROY Exit tech_pvt=%p (use=%i)\n",
tech_pvt,usecount());
}
ast_mutex_destroy(&tech_pvt->iolock);
ast_mutex_destroy(&tech_pvt->event_queue.lock);
if (tech_pvt->cid_rdnis) {
free(tech_pvt->cid_rdnis);
tech_pvt->cid_rdnis=NULL;
}
free(tech_pvt);
ast_mutex_lock(&usecnt_lock);
usecnt--;
if (usecnt < 0) {
usecnt = 0;
}
ast_mutex_unlock(&usecnt_lock);
}
#if 0
static int waitfor_socket(int fd, int timeout)
{
struct pollfd pfds[1];
int res;
int errflags = (POLLERR | POLLHUP | POLLNVAL);
if (fd < 0) {
return -1;
}
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = POLLIN | errflags;
res = poll(pfds, 1, timeout);
if (res > 0) {
if ((pfds[0].revents & errflags)) {
res = -1;
} else if ((pfds[0].revents & POLLIN)) {
res = 1;
} else {
ast_log(LOG_ERROR, "System Error: Poll Event Error no event!\n");
res = -1;
}
}
return res;
}
#else
static int waitfor_socket(int fd, int timeout)
{
struct pollfd pfds[1];
int res;
if (fd < 0) {
return -1;
}
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = POLLIN | POLLERR;
res = poll(pfds, 1, timeout);
if (res > 0) {
if ((pfds[0].revents & POLLERR)) {
res = -1;
} else if((pfds[0].revents & POLLIN)) {
res = 1;
} else {
res = -1;
}
}
return res;
}
#endif
static void *tech_monitor_thread(void *obj)
{
private_object *tech_pvt;
woomera_message wmsg;
char tcallid[WOOMERA_STRLEN];
int aborted=0;
int res = 0;
tech_pvt = obj;
memset(tcallid,0,sizeof(tcallid));
memset(&wmsg,0,sizeof(wmsg));
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "IN THREAD %s rxgain=%f txtain=%f\n",
tech_pvt->callid,
tech_pvt->profile->rxgain_val,
tech_pvt->profile->txgain_val);
}
for(;;) {
if (globals.panic) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
}
if (!tech_pvt->owner) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Thread lost Owner Chan %s %p\n",
tech_pvt->callid,
tech_pvt);
}
}
/* finish the deferred crap asterisk won't allow us to do live */
if (ast_test_flag(tech_pvt, TFLAG_ABORT)) {
int timeout_cnt=0;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ABORT GOT HANGUP CmdCh=%i %s %s/%i\n",
tech_pvt->command_channel, tech_pvt->callid,
tech_pvt->profile ? tech_pvt->profile->audio_ip : "N/A",
tech_pvt->port);
}
aborted|=1;
if (tech_pvt->profile && tech_pvt->command_channel > -1) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ABORT sent HANGUP on %s %p\n",
tech_pvt->callid,
tech_pvt);
}
aborted|=2;
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"hangup %s%scause: %s%sQ931-Cause-Code: %d%sUnique-Call-Id: %s%s",
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
tech_pvt->ds,
WOOMERA_LINE_SEPARATOR,
tech_pvt->pri_cause,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"bye%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
woomera_close_socket(&tech_pvt->command_channel);
}
if (tech_pvt->udp_socket > -1) {
woomera_close_socket(&tech_pvt->udp_socket);
}
if (!ast_test_flag(tech_pvt, TFLAG_TECHHANGUP) &&
ast_test_flag(tech_pvt, TFLAG_PBX)) {
struct ast_channel *owner = tech_get_owner(tech_pvt);
if (owner) {
aborted|=4;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ABORT calling hangup on %s t=%p c=%p UP=%d\n",
tech_pvt->callid,
tech_pvt,
owner,
ast_test_flag(tech_pvt, TFLAG_UP));
}
while(tech_pvt->owner &&
ast_mutex_trylock(&tech_pvt->owner->lock)) {
usleep(1);
}
if ((owner=tech_pvt->owner)) {
tech_pvt->owner=NULL;
/* Issue a softhangup */
ast_softhangup(owner, AST_SOFTHANGUP_DEV);
ast_mutex_unlock(&owner->lock);
}
}
}
/* Wait for tech_hangup to set this, so there is on
* race condition with asterisk */
//ast_set_flag(tech_pvt, TFLAG_DESTROY);
if (ast_test_flag(tech_pvt, TFLAG_PBX)) {
while (!ast_test_flag(tech_pvt, TFLAG_DESTROY)) {
timeout_cnt++;
if (timeout_cnt > 4000) { //10sec timeout
/* Five second timeout */
ast_log(LOG_ERROR, "ERROR: Wait on destroy timedout! %s tech_pvt=%p\n",
tech_pvt->callid, tech_pvt);
ast_set_flag(tech_pvt, TFLAG_DESTROY);
break;
}
usleep(5000);
sched_yield();
}
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
tech_pvt->profile->call_end++;
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
} else {
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
tech_pvt->profile->call_abort++;
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "NOTE: Skipping Wait on destroy timedout! %s tech_pvt=%p\n",
tech_pvt->callid, tech_pvt);
}
ast_set_flag(tech_pvt, TFLAG_DESTROY);
}
aborted|=8;
tech_destroy(tech_pvt,tech_get_owner(tech_pvt));
tech_pvt = NULL;
break;
}
if (ast_test_flag(tech_pvt, TFLAG_TECHHANGUP) || !tech_pvt->owner) {
ast_set_flag(tech_pvt, TFLAG_DESTROY);
ast_set_flag(tech_pvt, TFLAG_ABORT);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Thread got HANGUP or no owner %s %p tpvt=%p\n",
tech_pvt->callid,tech_pvt,tech_pvt->owner);
}
goto tech_thread_continue;
}
if (ast_test_flag(tech_pvt, TFLAG_ACTIVATE)) {
struct ast_channel *owner;
int err;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ACTIVATE %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_clear_flag(tech_pvt, TFLAG_ACTIVATE);
err=tech_activate(tech_pvt);
if (err < 0 || ast_test_flag(tech_pvt, TFLAG_ABORT)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ACTIVATE ABORT Ch=%d\n",
tech_pvt->command_channel);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
}
owner = tech_get_owner(tech_pvt);
if (owner) {
owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ACTIVATE DONE %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
}
if (ast_test_flag(tech_pvt, TFLAG_PARSE_INCOMING)) {
int err;
ast_clear_flag(tech_pvt, TFLAG_PARSE_INCOMING);
ast_set_flag(tech_pvt, TFLAG_INCOMING);
err=woomera_event_incoming (tech_pvt);
if (err != 0) {
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"hangup %s%s"
"cause: INVALID_CALL_REFERENCE%s"
"Q931-Cause-Code: 81%s"
"Unique-Call-Id: %s%s",
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
WOOMERA_LINE_SEPARATOR,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
/* Wait for Ack */
woomera_message_parse_wait(tech_pvt,&wmsg);
woomera_close_socket(&tech_pvt->command_channel);
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
} else {
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"%s %s%s"
"Raw-Audio: %s:%d%s"
"Request-Audio: Raw%s"
"Unique-Call-Id: %s%s",
MEDIA_ANSWER,
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
tech_pvt->profile->audio_ip,
tech_pvt->port,
WOOMERA_LINE_SEPARATOR,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
}
/* Wait for Ack */
if (woomera_message_parse_wait(tech_pvt,&wmsg) < 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
ast_log(LOG_NOTICE, "MEDIA ANSWER ABORT Ch=%d\n",
tech_pvt->command_channel);
goto tech_thread_continue;
}
/* Confirm that the Ack is OK otherwise
* hangup */
if (woomera_message_reply_ok(&wmsg) != 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
}
/* It is possible for ACCEPT to have media info
* This is how Early Media is started */
err=woomera_event_media (tech_pvt, &wmsg);
if (err < 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
}
}
if (ast_test_flag(tech_pvt, TFLAG_ANSWER)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ANSWER %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_clear_flag(tech_pvt, TFLAG_ANSWER);
if (ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
ast_log(LOG_ERROR,"Error: ANSWER on OUTBOUND Call! (skipped) %s\n",
tech_pvt->callid);
} else {
#ifdef USE_ANSWER
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"ANSWER %s%s"
"Unique-Call-Id: %s%s",
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_RECORD_SEPARATOR);
if(woomera_message_parse_wait(tech_pvt,&wmsg) < 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
ast_log(LOG_NOTICE, "ANSWER ABORT Ch=%d\n",
tech_pvt->command_channel);
goto tech_thread_continue;
continue;
}
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
tech_pvt->profile->call_ok++;
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
#endif
}
}
if (ast_test_flag(tech_pvt, TFLAG_DTMF)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "DTMF %s tpvt=%p %s\n",
tech_pvt->callid,tech_pvt,tech_pvt->dtmfbuf);
}
//DIALECT
ast_mutex_lock(&tech_pvt->iolock);
#if 0
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"DTMF %s %s%s",
tech_pvt->callid,
tech_pvt->dtmfbuf,
WOOMERA_LINE_SEPARATOR);
#else
woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
"DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%s%s",
WOOMERA_LINE_SEPARATOR,
tech_pvt->callid,
WOOMERA_LINE_SEPARATOR,
strlen(tech_pvt->dtmfbuf),
WOOMERA_LINE_SEPARATOR,
WOOMERA_LINE_SEPARATOR,
tech_pvt->dtmfbuf,
WOOMERA_RECORD_SEPARATOR);
#endif
ast_clear_flag(tech_pvt, TFLAG_DTMF);
memset(tech_pvt->dtmfbuf, 0, sizeof(tech_pvt->dtmfbuf));
ast_mutex_unlock(&tech_pvt->iolock);
if (woomera_message_parse_wait(tech_pvt,&wmsg) < 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
ast_log(LOG_NOTICE, "DTMF ABORT Ch=%d\n",
tech_pvt->command_channel);
goto tech_thread_continue;
continue;
}
}
if(tech_pvt->timeout) {
struct timeval now;
int elapsed;
gettimeofday(&now, NULL);
elapsed = (((now.tv_sec * 1000) + now.tv_usec / 1000) -
((tech_pvt->started.tv_sec * 1000) + tech_pvt->started.tv_usec / 1000));
if (elapsed > tech_pvt->timeout) {
/* call timed out! */
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "CALL TIMED OUT %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
}
}
if (globals.debug > 2) {
if (tcallid[0] == 0) {
strncpy(tcallid,tech_pvt->callid,sizeof(tcallid)-1);
}
}
if (tech_pvt->command_channel < 0) {
if (!globals.more_threads) {
goto tech_thread_continue;
continue;
} else {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "No Command Channel %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
continue;
}
}
/* Check for events */
if((res = woomera_dequeue_event(&tech_pvt->event_queue, &wmsg)) ||
(res = woomera_message_parse(tech_pvt->command_channel,
&wmsg,
100,
tech_pvt->profile,
NULL
))) {
if (globals.debug > 2) {
if (!strcasecmp(wmsg.command, "INCOMING") ) {
/* Do not print it here */
}else{
ast_log(LOG_NOTICE, "WOOMERA EVENT %s : callid=%s tech_callid=%s tpvt=%p\n",
wmsg.command,wmsg.callid,tech_pvt->callid,tech_pvt);
}
}
if (res < 0) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "INVALID MESSAGE PARSE COMMAND %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
continue;
} else if (ast_test_flag(tech_pvt, TFLAG_ABORT)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "GOT ABORT WHILE WAITING FOR EVENT %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
goto tech_thread_continue;
continue;
} else if (!strcasecmp(wmsg.command, "HANGUP")) {
char *cause;
char *q931cause;
struct ast_channel *owner;
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "Hangup [%s]\n", tech_pvt->callid);
}
cause = woomera_message_header(&wmsg, "Cause");
q931cause = woomera_message_header(&wmsg, "Q931-Cause-Code");
owner = tech_get_owner(tech_pvt);
if (cause && owner) {
owner->hangupcause = string_to_release(cause);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "EVENT HANGUP with OWNER with Cause %s %s tpvt=%p\n",
cause,tech_pvt->callid,tech_pvt);
}
}else{
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "EVENT HANGUP with Cause %s %s tpvt=%p\n",
cause,tech_pvt->callid,tech_pvt);
}
}
if (q931cause && atoi(q931cause) && owner) {
owner->hangupcause = atoi(q931cause);
}
woomera_close_socket(&tech_pvt->command_channel);
ast_set_flag(tech_pvt, TFLAG_ABORT);
goto tech_thread_continue;
continue;
} else if (!strcasecmp(wmsg.command, "DTMF")) {
struct ast_frame dtmf_frame = {AST_FRAME_DTMF};
int x = 0;
for (x = 0; x < strlen(wmsg.command_args); x++) {
struct ast_channel *owner = tech_get_owner(tech_pvt);
dtmf_frame.subclass = wmsg.command_args[x];
if (owner) {
ast_queue_frame(owner, ast_frdup(&dtmf_frame));
}
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "SEND DTMF [%c] to %s\n", dtmf_frame.subclass,tech_pvt->callid);
}
}
}
} else if (!strcasecmp(wmsg.command, "PROCEED")) {
/* This packet has lots of info so well keep it */
tech_pvt->call_info = wmsg;
} else if (ast_test_flag(tech_pvt, TFLAG_PARSE_INCOMING) &&
!strcasecmp(wmsg.command, "INCOMING")) {
/* The INCOMING EVENT should never come here becuase
* there should be only a single LISTEN via main thread */
ast_log(LOG_ERROR, "INVALID WOOMERA EVENT %s :"
"callid=%s tech_callid=%s tpvt=%p\n",
wmsg.command,wmsg.callid,
tech_pvt->callid,tech_pvt);
} else if (!strcasecmp(wmsg.command, "CONNECT")) {
struct ast_frame answer_frame = {AST_FRAME_CONTROL, AST_CONTROL_ANSWER};
struct ast_channel *owner = tech_get_owner(tech_pvt);
if (owner) {
ast_setstate(owner, AST_STATE_UP);
ast_queue_frame(owner, &answer_frame);
ast_set_flag(tech_pvt, TFLAG_UP);
ast_mutex_lock(&tech_pvt->profile->call_count_lock);
tech_pvt->profile->call_ok++;
ast_mutex_unlock(&tech_pvt->profile->call_count_lock);
}else{
ast_set_flag(tech_pvt, TFLAG_ABORT);
}
} else if (!strcasecmp(wmsg.command, "MEDIA")) {
int err;
err=woomera_event_media (tech_pvt, &wmsg);
if (err != 0) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
}
} else {
if (!strcasecmp(wmsg.command, "INCOMING") ) {
/* Do not print it here */
}else{
ast_log(LOG_ERROR, "Woomera Invalid CMD %s %s\n",
wmsg.command,tech_pvt->callid);
}
}
}
if (globals.debug > 4) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "CHECK {%s} (%d) %s\n",
tech_pvt->profile->name,
res,tech_pvt->callid);
}
}
tech_thread_continue:
if (!globals.more_threads) {
ast_log(LOG_NOTICE, "EXITING THREAD on more threads %s\n",
tcallid);
break;
}
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "OUT THREAD %s 0x%X\n",tcallid,aborted);
}
return NULL;
}
static int woomera_profile_thread_running(woomera_profile *profile, int set, int new)
{
int running = 0;
ast_mutex_lock(&profile->iolock);
if (set) {
profile->thread_running = new;
}
running = profile->thread_running;
ast_mutex_unlock(&profile->iolock);
return running;
}
static int woomera_locate_socket(woomera_profile *profile, int *woomera_socket)
{
woomera_message wmsg;
memset(&wmsg,0,sizeof(wmsg));
for (;;) {
while (connect_woomera(woomera_socket, profile, 0) < 0) {
if(!woomera_profile_thread_running(profile, 0, 0)) {
break;
}
if (globals.panic > 2) {
break;
}
ast_log(LOG_WARNING,
"Woomera {%s} Cannot Reconnect! retry in 5 seconds...\n",
profile->name);
sleep(5);
}
if (*woomera_socket > -1) {
if (ast_test_flag(profile, PFLAG_INBOUND)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Woomera Master Socket \n");
}
woomera_printf(profile, *woomera_socket, "LISTEN MASTER%s", WOOMERA_RECORD_SEPARATOR);
if (woomera_message_parse(*woomera_socket,
&wmsg,
WOOMERA_HARD_TIMEOUT,
profile,
&profile->event_queue
) < 0) {
ast_log(LOG_ERROR, "{%s} %s:%d HELP! Woomera is broken!\n",
profile->name,__FUNCTION__,__LINE__);
if (*woomera_socket > -1) {
woomera_close_socket(woomera_socket);
}
continue;
}
}
}
usleep(100);
break;
}
return *woomera_socket;
}
static void tech_monitor_in_one_thread(void)
{
private_object *tech_pvt;
ASTOBJ_CONTAINER_TRAVERSE(&private_object_list, 1, do {
ASTOBJ_RDLOCK(iterator);
tech_pvt = iterator;
tech_monitor_thread(tech_pvt);
ASTOBJ_UNLOCK(iterator);
} while(0));
}
static void *woomera_thread_run(void *obj)
{
int woomera_socket = -1, res = 0, res2=0;
woomera_message wmsg;
woomera_profile *profile;
memset(&wmsg,0,sizeof(wmsg));
profile = obj;
ast_log(LOG_NOTICE, "Started Woomera Thread {%s}.\n", profile->name);
profile->thread_running = 1;
while(woomera_profile_thread_running(profile, 0, 0)) {
/* listen on socket and handle events */
if (globals.panic > 2) {
break;
}
if (globals.panic == 2) {
ast_log(LOG_NOTICE, "Woomera is disabled!\n");
sleep(5);
continue;
}
if (woomera_socket < 0) {
if (woomera_locate_socket(profile, &woomera_socket)) {
globals.panic = 0;
}
if (!woomera_profile_thread_running(profile, 0, 0)) {
break;
}
profile->woomera_socket=woomera_socket;
ast_log(LOG_NOTICE, "Woomera Thread Up {%s} %s/%d\n",
profile->name, profile->woomera_host, profile->woomera_port);
}
if (globals.panic) {
if (globals.panic != 2) {
ast_log(LOG_ERROR, "Help I'm in a state of panic!\n");
}
if (woomera_socket > -1) {
woomera_close_socket(&woomera_socket);
}
continue;
}
if (!globals.more_threads) {
if (woomera_socket > -1) {
tech_monitor_in_one_thread();
}
}
if ((res = woomera_dequeue_event(&profile->event_queue, &wmsg) ||
(res2 = woomera_message_parse(woomera_socket,
&wmsg,
/* if we are not stingy with threads we can block forever */
globals.more_threads ? 0 : 500,
profile,
NULL
)))) {
if (res2 < 0) {
ast_log(LOG_ERROR, "{%s} HELP! I lost my connection to woomera!\n", profile->name);
if (woomera_socket > -1) {
woomera_close_socket(&woomera_socket);
}
global_set_flag(TFLAG_ABORT);
if (globals.panic > 2) {
break;
}
continue;
if (woomera_socket > -1) {
if (ast_test_flag(profile, PFLAG_INBOUND)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "%s:%d Incoming Call \n",__FUNCTION__,__LINE__);
}
woomera_printf(profile, woomera_socket, "LISTEN%s", WOOMERA_RECORD_SEPARATOR);
if(woomera_message_parse(woomera_socket,
&wmsg,
WOOMERA_HARD_TIMEOUT,
profile,
&profile->event_queue
) < 0) {
ast_log(LOG_ERROR, "{%s} %s:%d HELP! Woomera is broken!\n", profile->name,__FUNCTION__,__LINE__);
woomera_close_socket(&woomera_socket);
}
}
if (woomera_socket > -1) {
ast_log(LOG_NOTICE, "Woomera Thread Up {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);
}
}
continue;
}
if (!strcasecmp(wmsg.command, "INCOMING")) {
int err=1;
int cause = 0;
struct ast_channel *inchan;
char *name = "Woomera";
if (!(name = woomera_message_header(&wmsg, "Remote-Address"))) {
name = woomera_message_header(&wmsg, "Channel-Name");
}
if (!name) {
name=wmsg.callid;
}
if (!name) {
name="smg";
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "NEW INBOUND CALL %s!\n",wmsg.callid);
}
if ((inchan = woomera_new(type, profile->coding, name, &cause, profile))) {
private_object *tech_pvt;
char *callid;
tech_pvt = inchan->tech_pvt;
/* Save the call id */
tech_pvt->call_info = wmsg;
memcpy(tech_pvt->callid,wmsg.callid,sizeof(tech_pvt->callid));
callid = woomera_message_header(&wmsg, "Unique-Call-Id");
if (callid) {
ast_copy_string(tech_pvt->callid,
callid,sizeof(wmsg.callid));
}
err=tech_init(tech_pvt, profile, TFLAG_INBOUND);
if (err) {
if(globals.debug > 2) {
ast_log(LOG_ERROR, "Error: Inbound Call Failed %s %p\n",
wmsg.callid,
tech_pvt);
}
tech_destroy(tech_pvt,inchan);
}
} else {
ast_log(LOG_ERROR, "Cannot Create new Inbound Channel!\n");
}
/* It is the job of the server to timeout on this call
if the call is not started */
}
}
if(globals.debug > 4) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "Main Thread {%s} Select Return %d\n", profile->name, res);
}
}
usleep(100);
}
if (woomera_socket > -1) {
woomera_printf(profile, woomera_socket, "BYE%s", WOOMERA_RECORD_SEPARATOR);
if(woomera_message_parse(woomera_socket,
&wmsg,
WOOMERA_HARD_TIMEOUT,
profile,
&profile->event_queue
) < 0) {
}
woomera_close_socket(&woomera_socket);
}
ast_set_flag(profile, PFLAG_DISABLED);
ast_log(LOG_NOTICE, "Ended Woomera Thread {%s}.\n", profile->name);
woomera_profile_thread_running(profile, 1, -1);
return NULL;
}
static void launch_woomera_thread(woomera_profile *profile)
{
pthread_attr_t attr;
int result = 0;
result = pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
result = ast_pthread_create(&profile->thread, &attr, woomera_thread_run, profile);
result = pthread_attr_destroy(&attr);
}
static int launch_tech_thread(private_object *tech_pvt)
{
pthread_attr_t attr;
int result = 0;
if (globals.debug > 2) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++LAUCN TECH THREAD\n");
}
}
result = pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
ast_set_flag(tech_pvt, TFLAG_INTHREAD);
result = ast_pthread_create(&tech_pvt->thread, &attr, tech_monitor_thread, tech_pvt);
if (result) {
ast_clear_flag(tech_pvt, TFLAG_INTHREAD);
ast_log(LOG_ERROR, "Error: Failed to launch tech thread \n");
}
pthread_attr_destroy(&attr);
return result;
}
static void woomera_config_gain(woomera_profile *profile, float gain_val, int rx)
{
int j;
int k;
float linear_gain = pow(10.0, gain_val / 20.0);
unsigned char *gain;
if (gain_val == 0) {
goto woomera_config_gain_skip;
}
if (rx) {
gain = profile->rxgain;
} else {
gain = profile->txgain;
}
switch (profile->coding) {
case AST_FORMAT_ALAW:
for (j = 0; j < 256; j++) {
if (gain_val) {
k = (int) (((float) alaw_to_linear(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
gain[j] = linear_to_alaw(k);
} else {
gain[j] = j;
}
}
break;
case AST_FORMAT_ULAW:
for (j = 0; j < 256; j++) {
if (gain_val) {
k = (int) (((float) ulaw_to_linear(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
gain[j] = linear_to_ulaw(k);
} else {
gain[j] = j;
}
}
break;
}
woomera_config_gain_skip:
if (rx) {
profile->rxgain_val=gain_val;
} else {
profile->txgain_val=gain_val;
}
}
static void destroy_woomera_profile(woomera_profile *profile)
{
if (profile && ast_test_flag(profile, PFLAG_DYNAMIC)) {
ast_mutex_destroy(&profile->iolock);
ast_mutex_destroy(&profile->call_count_lock);
ast_mutex_destroy(&profile->event_queue.lock);
free(profile);
}
}
static woomera_profile *clone_woomera_profile(woomera_profile *new_profile, woomera_profile *default_profile)
{
return memcpy(new_profile, default_profile, sizeof(woomera_profile));
}
static woomera_profile *create_woomera_profile(woomera_profile *default_profile)
{
woomera_profile *profile;
if((profile = malloc(sizeof(woomera_profile)))) {
clone_woomera_profile(profile, default_profile);
ast_mutex_init(&profile->iolock);
ast_mutex_init(&profile->call_count_lock);
ast_mutex_init(&profile->event_queue.lock);
ast_set_flag(profile, PFLAG_DYNAMIC);
}
return profile;
}
static int config_woomera(void)
{
struct ast_config *cfg;
char *entry;
struct ast_variable *v;
woomera_profile *profile;
int count = 0;
memset(&default_profile, 0, sizeof(default_profile));
default_profile.coding=0;
if ((cfg = ast_config_load(configfile))) {
for (entry = ast_category_browse(cfg, NULL); entry != NULL; entry = ast_category_browse(cfg, entry)) {
if (!strcmp(entry, "settings")) {
for (v = ast_variable_browse(cfg, entry); v ; v = v->next) {
if (!strcmp(v->name, "debug")) {
globals.debug = atoi(v->value);
} else if (!strcmp(v->name, "more_threads")) {
globals.more_threads = ast_true(v->value);
}
}
#ifdef AST_JB
struct ast_variable *vjb;
/* Copy the default jb config over global_jbconf */
memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
/* Traverse all variables to handle jb conf */
vjb = ast_variable_browse(cfg, "settings");;
while(vjb)
{
ast_jb_read_conf(&global_jbconf, vjb->name, vjb->value);
vjb = vjb->next;
}
#endif /* AST_JB */
} else {
int new = 0;
float gain;
count++;
if (!strcmp(entry, "default")) {
profile = &default_profile;
} else {
if((profile = ASTOBJ_CONTAINER_FIND(&woomera_profile_list, entry))) {
clone_woomera_profile(profile, &default_profile);
} else {
if((profile = create_woomera_profile(&default_profile))) {
new = 1;
} else {
ast_log(LOG_ERROR, "Memory Error!\n");
}
}
}
strncpy(profile->name, entry, sizeof(profile->name) - 1);
profile->coding=AST_FORMAT_SLINEAR;
/*default is inbound and outbound enabled */
ast_set_flag(profile, PFLAG_INBOUND | PFLAG_OUTBOUND);
for (v = ast_variable_browse(cfg, entry); v ; v = v->next) {
if (!strcmp(v->name, "audio_ip")) {
strncpy(profile->audio_ip, v->value, sizeof(profile->audio_ip) - 1);
} else if (!strcmp(v->name, "host")) {
strncpy(profile->woomera_host, v->value, sizeof(profile->woomera_host) - 1);
} else if (!strcmp(v->name, "max_calls")) {
int max = atoi(v->value);
if (max > 0) {
profile->max_calls = max;
}
} else if (!strcmp(v->name, "port")) {
profile->woomera_port = atoi(v->value);
} else if (!strcmp(v->name, "disabled")) {
ast_set2_flag(profile, ast_true(v->value), PFLAG_DISABLED);
} else if (!strcmp(v->name, "inbound")) {
if (ast_false(v->value)) {
ast_clear_flag(profile, PFLAG_INBOUND);
}
} else if (!strcmp(v->name, "outbound")) {
if (ast_false(v->value)) {
ast_clear_flag(profile, PFLAG_OUTBOUND);
}
} else if (!strcmp(v->name, "context")) {
strncpy(profile->context, v->value, sizeof(profile->context) - 1);
} else if (!strcmp(v->name, "dtmf_enable")) {
profile->dtmf_enable = atoi(v->value);
} else if (!strcmp(v->name, "jb_enable")) {
profile->jb_enable = atoi(v->value);
} else if (!strcmp(v->name, "progress_enable")) {
profile->progress_enable = atoi(v->value);
} else if (!strcmp(v->name, "coding")) {
if (strcmp(v->value, "alaw") == 0) {
profile->coding=AST_FORMAT_ALAW;
}
if (strcmp(v->value, "ulaw") == 0) {
profile->coding=AST_FORMAT_ULAW;
}
} else if (!strcmp(v->name, "rxgain") && profile->coding) {
if (sscanf(v->value, "%f", &gain) != 1) {
ast_log(LOG_WARNING, "Invalid rxgain: %s\n", v->value);
} else {
woomera_config_gain(profile,gain,1);
}
} else if (!strcmp(v->name, "txgain") && profile->coding) {
if (sscanf(v->value, "%f", &gain) != 1) {
ast_log(LOG_WARNING, "Invalid txgain: %s\n", v->value);
} else {
woomera_config_gain(profile,gain,0);
}
}
}
ASTOBJ_CONTAINER_LINK(&woomera_profile_list, profile);
}
}
ast_config_destroy(cfg);
} else {
return 0;
}
return count;
}
static int create_udp_socket(char *ip, int port, struct sockaddr_in *sockaddr, int client)
{
int rc, sd = -1;
struct sockaddr_in servAddr, *addr, cliAddr;
struct hostent hps, *hp;
struct hostent *result;
char buf[512];
int err=0;
memset(&hps,0,sizeof(hps));
hp=&hps;
if(sockaddr) {
addr = sockaddr;
} else {
addr = &servAddr;
}
if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) > -1) {
gethostbyname_r(ip, hp, buf, sizeof(buf), &result, &err);
if (result) {
addr->sin_family = hp->h_addrtype;
memcpy((char *) &addr->sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
addr->sin_port = htons(port);
if (globals.debug > 4) {
ast_log(LOG_NOTICE,"MEDIA UdpRead IP=%s/%d len=%i %d.%d.%d.%d\n",
ip,port,
hp->h_length,
hp->h_addr_list[0][0],
hp->h_addr_list[0][1],
hp->h_addr_list[0][2],
hp->h_addr_list[0][3]);
}
if (client) {
cliAddr.sin_family = AF_INET;
cliAddr.sin_addr.s_addr = htonl(INADDR_ANY);
cliAddr.sin_port = htons(0);
rc = bind(sd, (struct sockaddr *) &cliAddr, sizeof(cliAddr));
} else {
rc = bind(sd, (struct sockaddr *) addr, sizeof(cliAddr));
}
if (rc < 0) {
if (globals.debug > 2) {
ast_log(LOG_ERROR,
"Error opening udp socket %s/%i %s\n",
ip,port, strerror(errno));
}
woomera_close_socket(&sd);
} else if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Socket Binded %s to %s/%d\n",
client ? "client" : "server", ip, port);
}
} else {
if (globals.debug > 2) {
ast_log(LOG_ERROR,
"Error opening udp: gethostbyname failed %s/%i %s\n",
ip,port, strerror(errno));
}
woomera_close_socket(&sd);
}
}
return sd;
}
static int connect_woomera(int *new_socket, woomera_profile *profile, int flags)
{
struct sockaddr_in localAddr, remoteAddr;
struct hostent *hp;
struct ast_hostent ahp;
int res = 0;
*new_socket=-1;
if ((hp = ast_gethostbyname(profile->woomera_host, &ahp))) {
remoteAddr.sin_family = hp->h_addrtype;
memcpy((char *) &remoteAddr.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
remoteAddr.sin_port = htons(profile->woomera_port);
do {
/* create socket */
*new_socket = socket(AF_INET, SOCK_STREAM, 0);
if (*new_socket < 0) {
ast_log(LOG_ERROR, "cannot open socket to %s/%d\n", profile->woomera_host, profile->woomera_port);
res = 0;
break;
}
/* bind any port number */
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(0);
res = bind(*new_socket, (struct sockaddr *) &localAddr, sizeof(localAddr));
if (res < 0) {
ast_log(LOG_ERROR, "cannot bind to %s/%d\n", profile->woomera_host, profile->woomera_port);
woomera_close_socket(new_socket);
res = 0;
break;
}
/* connect to server */
res = connect(*new_socket, (struct sockaddr *) &remoteAddr, sizeof(remoteAddr));
if (res < 0) {
ast_log(LOG_ERROR, "cannot connect to {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);
res = 0;
woomera_close_socket(new_socket);
break;
}
res = 1;
} while(0);
} else {
if (globals.debug > 2) {
ast_log(LOG_ERROR, "gethost failed connect to {%s} %s/%d\n",
profile->name, profile->woomera_host, profile->woomera_port);
}
res = 0;
}
if (res > 0) {
int flag = 1;
woomera_message wmsg;
memset(&wmsg,0,sizeof(wmsg));
/* disable nagle's algorythm */
res = setsockopt(*new_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
if (!(flags & WCFLAG_NOWAIT)) {
/* kickstart the session waiting for a HELLO */
woomera_printf(NULL, *new_socket, "%s", WOOMERA_RECORD_SEPARATOR);
if ((res = woomera_message_parse(*new_socket,
&wmsg,
WOOMERA_HARD_TIMEOUT,
profile,
NULL
)) < 0) {
ast_log(LOG_ERROR, "{%s} Timed out waiting for a hello from woomera!\n", profile->name);
woomera_close_socket(new_socket);
}
if (res > 0 && strcasecmp(wmsg.command, "HELLO")) {
ast_log(LOG_ERROR, "{%s} unexpected reply [%s] while waiting for a hello from woomera!\n", profile->name, wmsg.command);
woomera_close_socket(new_socket);
}else{
char *audio_format;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Woomera Got HELLO on connect! %s SMG Version %s\n", profile->name,woomera_message_header(&wmsg, "Version"));
}
if (!smgversion_init && woomera_message_header(&wmsg, "Version")) {
smgversion_init=1;
strncpy(smgversion,
woomera_message_header(&wmsg, "Version"),
sizeof(smgversion)-1);
}
audio_format = woomera_message_header(&wmsg, "Raw-Format");
if (!audio_format) {
audio_format = woomera_message_header(&wmsg, "Raw-Audio-Format");
}
if (audio_format) {
profile->coding=AST_FORMAT_SLINEAR;
if (strncasecmp(audio_format,"PCM-16",20) == 0){
profile->coding=AST_FORMAT_SLINEAR;
} else if (strncasecmp(audio_format,"ULAW",15) == 0) {
profile->coding=AST_FORMAT_ULAW;
} else if (strncasecmp(audio_format,"ALAW",15) == 0) {
profile->coding=AST_FORMAT_ALAW;
} else {
ast_log(LOG_ERROR, "Error: Invalid Raw-Format %s\n",
audio_format);
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "Setting RAW Format to %s %i (p%i:u%i:a%i)\n",
audio_format, profile->coding,
AST_FORMAT_SLINEAR,AST_FORMAT_ULAW,AST_FORMAT_ALAW);
}
}
}
}
} else {
if (globals.debug > 2) {
ast_log(LOG_ERROR, "Woomera {%s} connection failed: %s/%d\n",
profile->name, profile->woomera_host, profile->woomera_port);
ast_log(LOG_ERROR, "Woomera {%s} connection failed: %s\n",
profile->name, strerror(errno));
}
woomera_close_socket(new_socket);
}
return *new_socket;
}
static int init_woomera(void)
{
woomera_profile *profile;
ast_mutex_lock(&lock);
if (!config_woomera()) {
ast_mutex_unlock(&lock);
return 0;
}
ASTOBJ_CONTAINER_TRAVERSE(&woomera_profile_list, 1, do {
ASTOBJ_RDLOCK(iterator);
profile = iterator;
if (!ast_test_flag(profile, PFLAG_DISABLED)) {
launch_woomera_thread(profile);
}
ASTOBJ_UNLOCK(iterator);
} while(0));
ast_mutex_unlock(&lock);
return 1;
}
static struct ast_channel *woomera_new(const char *type, int format,
void *data, int *cause,
woomera_profile *parent_profile)
{
private_object *tech_pvt;
struct ast_channel *chan = NULL;
char name[100];
snprintf(name, sizeof(name), "%s/%s-%04x", "WOOMERA", (char *)data, rand() & 0xffff);
if (!(tech_pvt = malloc(sizeof(private_object)))) {
ast_log(LOG_ERROR, "Memory Error!\n");
return NULL;
}
memset(tech_pvt, 0, sizeof(private_object));
#ifdef AST14
chan = ast_channel_alloc(0, AST_STATE_DOWN, "", "", "", "", "", 0, "%s", name);
#else
chan = ast_channel_alloc(1);
#endif
if (chan) {
chan->nativeformats = WFORMAT;
#ifndef AST14
chan->type = type;
snprintf(chan->name, sizeof(chan->name), "%s/%s-%04x", chan->type, (char *)data, rand() & 0xffff);
#endif
chan->writeformat = chan->rawwriteformat = chan->readformat = WFORMAT;
chan->_state = AST_STATE_DOWN;
chan->_softhangup = 0;
tech_count++;
tech_pvt->coding=WFORMAT;
ast_mutex_init(&tech_pvt->iolock);
ast_mutex_init(&tech_pvt->event_queue.lock);
tech_pvt->command_channel = -1;
chan->tech_pvt = tech_pvt;
chan->tech = &technology;
tech_pvt->udp_socket = -1;
ast_clear_flag(chan, AST_FLAGS_ALL);
memset(&tech_pvt->frame, 0, sizeof(tech_pvt->frame));
tech_pvt->frame.frametype = AST_FRAME_VOICE;
tech_pvt->frame.subclass = WFORMAT;
tech_pvt->frame.offset = AST_FRIENDLY_OFFSET;
tech_pvt->owner = chan;
chan->nativeformats = tech_pvt->coding;
chan->writeformat = chan->rawwriteformat = chan->readformat = tech_pvt->coding;
tech_pvt->frame.subclass = tech_pvt->coding;
tech_pvt->pri_cause=AST_CAUSE_NORMAL_CLEARING;
ASTOBJ_CONTAINER_LINK(&private_object_list, tech_pvt);
ast_mutex_lock(&usecnt_lock);
usecnt++;
ast_mutex_unlock(&usecnt_lock);
} else {
if (option_verbose > 1) {
ast_log(LOG_ERROR, "Can't allocate a channel\n");
}
}
return chan;
}
/********************CHANNEL METHOD LIBRARY********************
* This is the actual functions described by the prototypes above.
*
*/
/*--- tech_requester: parse 'data' a url-like destination string, allocate a channel and a private structure
* and return the newly-setup channel.
*/
static struct ast_channel *tech_requester(const char *type, int format, void *data, int *cause)
{
struct ast_channel *chan = NULL;
if (globals.panic) {
return NULL;
}
if ((chan = woomera_new(type, format, data, cause, NULL))) {
private_object *tech_pvt;
tech_pvt = chan->tech_pvt;
if (tech_pvt->owner) {
tech_pvt->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
}
ast_set_flag(tech_pvt, TFLAG_PBX); /* so we know we dont have to free the channel ourselves */
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++REQ %s\n", chan->name);
}
}
} else {
if (option_verbose > 1) {
ast_log(LOG_ERROR, "Can't allocate a channel\n");
}
}
return chan;
}
#ifdef AST14
static int tech_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{
return 0;
}
#endif
/*--- tech_senddigit: Send a DTMF character */
static int tech_send_digit(struct ast_channel *self, char digit)
{
private_object *tech_pvt = self->tech_pvt;
int res = 0;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++DIGIT %s '%c'\n",self->name, digit);
}
}
/* we don't have time to make sure the dtmf command is successful cos asterisk again
is much too impaitent... so we will cache the digits so the monitor thread can send
it for us when it has time to actually wait.
*/
ast_mutex_lock(&tech_pvt->iolock);
snprintf(tech_pvt->dtmfbuf + strlen(tech_pvt->dtmfbuf), sizeof(tech_pvt->dtmfbuf), "%c", digit);
ast_set_flag(tech_pvt, TFLAG_DTMF);
ast_mutex_unlock(&tech_pvt->iolock);
return res;
}
/*--- tech_call: Initiate a call on my channel
* 'dest' has been passed telling you where to call
* but you may already have that information from the requester method
* not sure why it sends it twice, maybe it changed since then *shrug*
* You also have timeout (in ms) so you can tell how long the caller
* is willing to wait for the call to be complete.
*/
static int tech_call(struct ast_channel *self, char *dest, int timeout)
{
private_object *tech_pvt = self->tech_pvt;
char *workspace;
self->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
if (globals.panic) {
goto tech_call_failed;
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH CALL %s (%s <%s>) pres=0x%02X dest=%s\n",
self->name, self->cid.cid_name,
self->cid.cid_num,
self->cid.cid_pres,
dest);
}
if (self->cid.cid_name) {
strncpy(tech_pvt->cid_name, self->cid.cid_name, sizeof(tech_pvt->cid_name)-1);
}
if (self->cid.cid_num) {
strncpy(tech_pvt->cid_num, self->cid.cid_num, sizeof(tech_pvt->cid_num)-1);
}
tech_pvt->cid_pres = self->cid.cid_pres;
if (self->cid.cid_rdnis) {
tech_pvt->cid_rdnis=strdup(self->cid.cid_rdnis);
}
if ((workspace = ast_strdupa(dest))) {
char *addr, *profile_name, *proto=NULL;
woomera_profile *profile;
int err;
#if 0
int isprofile = 0;
if ((addr = strchr(workspace, ':'))) {
char *tst;
proto = workspace;
if ((tst = strchr(proto, '/'))){
proto=tst+1;
}
*addr = '\0';
addr++;
} else {
proto = NULL;
addr = workspace;
}
if ((profile_name = strchr(addr, ':'))) {
*profile_name = '\0';
profile_name++;
isprofile = 1;
} else {
profile_name = "default";
}
#else
profile_name = "default";
proto = NULL;
if ((addr = strchr(workspace, ':'))) {
profile_name = workspace;
proto=profile_name;
*addr = '\0';
addr++;
} else {
addr = workspace;
}
#endif
if (! (profile = ASTOBJ_CONTAINER_FIND(&woomera_profile_list, profile_name))) {
profile = ASTOBJ_CONTAINER_FIND(&woomera_profile_list, "default");
}
if (!profile) {
ast_log(LOG_ERROR, "Unable to find profile! Call Aborted!\n");
goto tech_call_failed;
}
if (!ast_test_flag(profile, PFLAG_OUTBOUND)) {
ast_log(LOG_ERROR, "This profile is not allowed to make outbound calls! Call Aborted!\n");
goto tech_call_failed;
}
if (profile->max_calls) {
ast_mutex_lock(&profile->call_count_lock);
if (profile->call_count >= profile->max_calls) {
ast_mutex_unlock(&profile->call_count_lock);
if (globals.debug > 1 && option_verbose > 1) {
ast_log(LOG_ERROR, "This profile is at call limit of %d\n",
profile->max_calls);
}
goto tech_call_failed;
}
ast_mutex_unlock(&profile->call_count_lock);
}
snprintf(tech_pvt->dest, sizeof(tech_pvt->dest), "%s", addr ? addr : "");
snprintf(tech_pvt->proto, sizeof(tech_pvt->proto), "%s", proto ? proto : "");
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH CALL: proto=%s addr=%s profile=%s Coding=%i\n",
proto,addr,profile_name,profile->coding);
}
tech_pvt->timeout = timeout;
err=tech_init(tech_pvt, profile, TFLAG_OUTBOUND);
if (err) {
if (globals.debug > 2) {
ast_log(LOG_ERROR, "Error: Outbound Call Failed \n");
}
goto tech_call_failed;
}
woomera_send_progress(tech_pvt);
}
self->hangupcause = AST_CAUSE_NORMAL_CLEARING;
return 0;
tech_call_failed:
if (globals.debug > 1 && option_verbose > 1) {
ast_log(LOG_ERROR, "Error: Outbound Call Failed %p \n",tech_pvt);
}
self->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
my_ast_softhangup(self, tech_pvt, AST_SOFTHANGUP_EXPLICIT);
return -1;
}
/*--- tech_hangup: end a call on my channel
* Now is your chance to tear down and free the private object
* from the channel it's about to be freed so you must do so now
* or the object is lost. Well I guess you could tag it for reuse
* or for destruction and let a monitor thread deal with it too.
* during the load_module routine you have every right to start up
* your own fancy schmancy bunch of threads or whatever else
* you want to do.
*/
static int tech_hangup(struct ast_channel *self)
{
const char *ds;
private_object *tech_pvt = self->tech_pvt;
int res = 0;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH HANGUP [%s] tech_pvt=%p c=%p\n",self->name,tech_pvt,self);
}
if (tech_pvt) {
ast_mutex_lock(&tech_pvt->iolock);
ast_set_flag(tech_pvt, TFLAG_TECHHANGUP);
tech_pvt->owner=NULL;
ast_mutex_unlock(&tech_pvt->iolock);
if (!self ||
(!(ds = pbx_builtin_getvar_helper(self, "DIALSTATUS")) &&
!(ds = ast_cause2str(self->hangupcause)))) {
ds = "NOEXIST";
}
ast_copy_string(tech_pvt->ds, ds, sizeof(tech_pvt->ds));
ds=pbx_builtin_getvar_helper(self, "PRI_CAUSE");
if (ds && atoi(ds)) {
tech_pvt->pri_cause=atoi(ds);
} else if (self->hangupcause) {
tech_pvt->pri_cause=self->hangupcause;
} else {
tech_pvt->pri_cause=AST_CAUSE_NORMAL_CLEARING;
}
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH HANGUP [%s] Cause=%i HangCause=%i ds=%s\n",
self->name,tech_pvt->pri_cause, self->hangupcause, ds?ds:"N/A");
}
if (tech_pvt->dsp) {
tech_pvt->dsp_features &= ~DSP_FEATURE_DTMF_DETECT;
ast_dsp_set_features(tech_pvt->dsp, tech_pvt->dsp_features);
tech_pvt->ast_dsp=0;
}
if (ast_test_flag(tech_pvt, TFLAG_INTHREAD)) {
ast_set_flag(tech_pvt, TFLAG_ABORT);
ast_set_flag(tech_pvt, TFLAG_DESTROY);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH HANGUP IN THREAD! tpvt=%p\n",
tech_pvt);
}
} else {
if (!ast_test_flag(tech_pvt, TFLAG_DESTROY)) {
tech_destroy(tech_pvt,NULL);
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH HANGUP NOT IN THREAD! tpvt=%p\n",
tech_pvt);
}
}else{
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "TECH HANGUP NOT IN THREAD ALREDAY HUNGUP! tpvt=%p\n",
tech_pvt);
}
}
}
} else {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "ERROR: NO TECH ON TECH HANGUP!\n");
}
}
self->tech_pvt = NULL;
return res;
}
/*--- tech_answer: answer a call on my channel
* if being 'answered' means anything special to your channel
* now is your chance to do it!
*/
static int tech_answer(struct ast_channel *self)
{
private_object *tech_pvt;
int res = 0;
tech_pvt = self->tech_pvt;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++ANSWER %s\n",self->name);
}
}
if (!ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
/* Only answer the inbound calls */
ast_set_flag(tech_pvt, TFLAG_ANSWER);
} else {
ast_log(LOG_ERROR, "Warning: AST trying to Answer OUTBOUND Call!\n");
}
ast_set_flag(tech_pvt, TFLAG_UP);
ast_setstate(self, AST_STATE_UP);
return res;
}
#if 0
static int woomera_tx2ast_frm(private_object *tech_pvt, char * buf, int len )
{
struct ast_frame frame;
frame.frametype = AST_FRAME_VOICE;
frame.subclass = tech_pvt->coding;
frame.datalen = len;
frame.samples = len;
frame.mallocd =0 ;
frame.offset= 0 ;
frame.src = NULL;
frame.data = buf ;
if (tech_pvt->faxdetect || tech_pvt->ast_dsp) {
struct ast_frame *f;
struct ast_channel *owner = tech_get_owner(tech_pvt);
if (!owner) {
return 0;
}
f = ast_dsp_process(owner, tech_pvt->dsp, &frame);
if (f && (f->frametype == AST_FRAME_DTMF)) {
ast_log(LOG_DEBUG, "Detected inband DTMF digit: %c\n", f->subclass);
#if 0
if (f->subclass == 'f' && tech_pvt->faxdetect) {
/* Fax tone -- Handle and return NULL */
struct ast_channel *ast = tech_pvt->owner;
if (!tech_pvt->faxhandled) {
tech_pvt->faxhandled++;
if (strcmp(ast->exten, "fax")) {
if (ast_exists_extension(ast, ast_strlen_zero(ast->macrocontext)? ast->context : ast->macrocontext, "fax", 1, AST_CID_P(ast))) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name);
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
pbx_builtin_setvar_helper(ast,"FAXEXTEN",ast->exten);
if (ast_async_goto(ast, ast->context, "fax", 1))
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, ast->context);
} else
ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n",ast->context, ast->exten);
} else
ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
} else
ast_log(LOG_DEBUG, "Fax already handled\n");
frame.frametype = AST_FRAME_NULL;
frame.subclass = 0;
f = &frame;
} else
#endif
if (tech_pvt->ast_dsp && owner) {
struct ast_frame fr;
memset(&fr, 0 , sizeof(fr));
fr.frametype = AST_FRAME_DTMF;
fr.subclass = f->subclass ;
fr.src=NULL;
fr.data = NULL ;
fr.datalen = 0;
fr.samples = 0 ;
fr.mallocd =0 ;
fr.offset= 0 ;
ast_log(LOG_EVENT, "Received DTMF Sending 2 AST %c\n",
fr.subclass);
ast_queue_frame(owner, &fr);
}
}
}
return 0;
}
#endif
/*--- tech_read: Read an audio frame from my channel.
* You need to read data from your channel and convert/transfer the
* data into a newly allocated struct ast_frame object
*/
static struct ast_frame *tech_read(struct ast_channel *self)
{
private_object *tech_pvt = self->tech_pvt;
int res = 0;
struct ast_frame *f;
if (!tech_pvt || globals.panic || ast_test_flag(tech_pvt, TFLAG_ABORT)) {
return NULL;
}
tech_read_again:
res = waitfor_socket(tech_pvt->udp_socket, 1000);
if (res < 1) {
return NULL;
}else if (res == 0) {
goto tech_read_again;
}
res = read(tech_pvt->udp_socket, tech_pvt->fdata + AST_FRIENDLY_OFFSET, FRAME_LEN);
if (res < 1) {
return NULL;
}
tech_pvt->frame.frametype = AST_FRAME_VOICE;
tech_pvt->frame.subclass = tech_pvt->coding;
tech_pvt->frame.offset = AST_FRIENDLY_OFFSET;
tech_pvt->frame.datalen = res;
tech_pvt->frame.samples = res;
tech_pvt->frame.data = tech_pvt->fdata + AST_FRIENDLY_OFFSET;
f=&tech_pvt->frame;
if (tech_pvt->profile->rxgain_val) {
int i;
unsigned char *data=tech_pvt->frame.data;
for (i=0;i<tech_pvt->frame.datalen;i++) {
data[i]=tech_pvt->profile->rxgain[data[i]];
}
}
if (tech_pvt->owner && (tech_pvt->faxdetect || tech_pvt->ast_dsp)) {
f = ast_dsp_process(tech_pvt->owner, tech_pvt->dsp, &tech_pvt->frame);
if (f && f->frametype == AST_FRAME_DTMF){
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "%s: Detected inband DTMF digit: %c\n",
self->name,
f->subclass);
}
}
//woomera_tx2ast_frm(tech_pvt, tech_pvt->frame.data, tech_pvt->frame.datalen);
}
if (globals.debug > 4) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++READ %s %d coding %d\n",self->name, res,
tech_pvt->coding);
}
}
return f;
}
/*--- tech_write: Write an audio frame to my channel
* Yep, this is the opposite of tech_read, you need to examine
* a frame and transfer the data to your technology's audio stream.
* You do not have any responsibility to destroy this frame and you should
* consider it to be read-only.
*/
static int tech_write(struct ast_channel *self, struct ast_frame *frame)
{
private_object *tech_pvt = self->tech_pvt;
int res = 0, i = 0;
if (!tech_pvt || globals.panic || ast_test_flag(tech_pvt, TFLAG_ABORT)) {
return -1;
}
if(ast_test_flag(tech_pvt, TFLAG_MEDIA) && frame->datalen) {
if (frame->frametype == AST_FRAME_VOICE) {
if (tech_pvt->profile->txgain_val) {
unsigned char *data=frame->data;
for (i=0;i<frame->datalen;i++) {
data[i]=tech_pvt->profile->txgain[data[i]];
}
}
i = sendto(tech_pvt->udp_socket, frame->data, frame->datalen, 0,
(struct sockaddr *) &tech_pvt->udpwrite, sizeof(tech_pvt->udpwrite));
if (i < 0) {
return -1;
}
if (globals.debug > 4) {
if (option_verbose > 4) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++WRITE %s %d\n",self->name, i);
}
}
} else {
ast_log(LOG_WARNING, "Invalid frame type %d sent\n", frame->frametype);
}
}
return res;
}
/*--- tech_write_video: Write a video frame to my channel ---*/
static int tech_write_video(struct ast_channel *self, struct ast_frame *frame)
{
private_object *tech_pvt;
int res = 0;
tech_pvt = self->tech_pvt;
return res;
}
/*--- tech_exception: Read an exception audio frame from my channel ---*/
static struct ast_frame *tech_exception(struct ast_channel *self)
{
private_object *tech_pvt;
struct ast_frame *new_frame = NULL;
tech_pvt = self->tech_pvt;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++EXCEPT %s\n",self->name);
}
}
return new_frame;
}
/*--- tech_indicate: Indicaate a condition to my channel ---*/
#if 0
static int tech_indicate(struct ast_channel *self, int condition)
{
private_object *tech_pvt;
int res = 0;
tech_pvt = self->tech_pvt;
if (globals.debug > 1) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++INDICATE %s %d\n",self->name, condition);
}
return res;
}
#endif
/*--- tech_fixup: add any finishing touches to my channel if it is masqueraded---*/
static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
int res = 0;
private_object *tech_pvt;
if ((tech_pvt = oldchan->tech_pvt)) {
ast_mutex_lock(&tech_pvt->iolock);
tech_pvt->owner = newchan;
ast_mutex_unlock(&tech_pvt->iolock);
}
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++FIXUP %s\n",oldchan->name);
}
}
return res;
}
/*--- tech_send_html: Send html data on my channel ---*/
static int tech_send_html(struct ast_channel *self, int subclass, const char *data, int datalen)
{
private_object *tech_pvt;
int res = 0;
tech_pvt = self->tech_pvt;
return res;
}
/*--- tech_send_text: Send plain text data on my channel ---*/
static int tech_send_text(struct ast_channel *self, const char *text)
{
int res = 0;
return res;
}
/*--- tech_send_image: Send image data on my channel ---*/
static int tech_send_image(struct ast_channel *self, struct ast_frame *frame)
{
int res = 0;
return res;
}
/*--- tech_setoption: set options on my channel ---*/
static int tech_setoption(struct ast_channel *self, int option, void *data, int datalen)
{
int res = 0;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++SETOPT %s\n",self->name);
}
}
return res;
}
/*--- tech_queryoption: get options from my channel ---*/
static int tech_queryoption(struct ast_channel *self, int option, void *data, int *datalen)
{
int res = 0;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++GETOPT %s\n",self->name);
}
}
return res;
}
/*--- tech_bridged_channel: return a pointer to a channel that may be bridged to our channel. ---*/
#if 0
static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct ast_channel *bridge)
{
struct ast_channel *chan = NULL;
if (globals.debug > 1 && option_verbose > 1) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++BRIDGED %s\n",self->name);
}
return chan;
}
#endif
/*--- tech_transfer: Technology-specific code executed to peform a transfer. ---*/
static int tech_transfer(struct ast_channel *self, const char *newdest)
{
int res = -1;
if (globals.debug > 1 && option_verbose > 1) {
if (option_verbose > 2) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++TRANSFER %s\n",self->name);
}
}
return res;
}
/*--- tech_bridge: Technology-specific code executed to natively bridge 2 of our channels ---*/
#if 0
static enum ast_bridge_result tech_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
{
int res = -1;
if (globals.debug > 1) {
ast_verbose(WOOMERA_DEBUG_PREFIX "+++BRIDGE %s\n",c0->name);
}
return res;
}
#endif
static int woomera_cli(int fd, int argc, char *argv[])
{
struct woomera_profile *profile;
char *profile_name="default";
if (argc > 2) {
profile_name=argv[1];
profile = ASTOBJ_CONTAINER_FIND(&woomera_profile_list, profile_name);
if (!profile) {
if (strcmp(profile_name,"version") == 0) {
ast_cli(fd, "Woomera version %s : SMG Version %s \n",
WOOMERA_VERSION,smgversion);
return 0;
}
ast_cli(fd, "Woomera: Invalid profile name %s\n", profile_name);
return 0;
}
if (!strcmp(argv[2], "debug")) {
if (argc > 3) {
globals.debug = atoi(argv[3]);
}
ast_cli(fd, "Woomera debug=%d\n", globals.debug);
} else if (!strcmp(argv[2], "coding")) {
switch (profile->coding) {
case AST_FORMAT_ALAW:
ast_cli(fd, " Woomera {%s} coding=ALAW\n",profile_name);
break;
case AST_FORMAT_ULAW:
ast_cli(fd, " Woomera {%s} coding=ULAW\n",profile_name);
break;
case AST_FORMAT_SLINEAR:
ast_cli(fd, " Woomera {%s} coding=PMC-16\n",profile_name);
break;
default:
ast_cli(fd, " Woomera {%s} invalid coding=%d {internal error}",
profile_name,profile->coding);
break;
}
} else if (!strcmp(argv[2], "call_status")) {
ast_cli(fd, "Woomera {%s} calls=%d tcalls=%d (out=%d in=%d ok=%d end=%d abort=%d)\n",
profile->name,
profile->call_count,
profile->call_out+profile->call_in,
profile->call_out,
profile->call_in,
profile->call_ok,
profile->call_end,
profile->call_abort);
} else if (!strcmp(argv[2], "version")) {
ast_cli(fd, "Woomera version %s : SMG Version %s \n",
WOOMERA_VERSION,smgversion);
} else if (!strcmp(argv[2], "panic")) {
if (argc > 3) {
globals.panic = atoi(argv[3]);
}
ast_cli(fd, "Woomera panic=%d \n", globals.panic);
} else if (!strcmp(argv[2], "rxgain")) {
float gain;
if (argc > 3) {
if (sscanf(argv[3], "%f", &gain) != 1) {
ast_cli(fd, "Woomera Invalid rxgain: %s\n",argv[3]);
} else {
woomera_config_gain(profile,gain,1);
}
}
ast_cli(fd, "Woomera {%s} rxgain: %f\n",profile_name,profile->rxgain_val);
} else if (!strcmp(argv[2], "txgain")) {
float gain;
if (argc > 3) {
if (sscanf(argv[3], "%f", &gain) != 1) {
ast_cli(fd, "Woomera Invalid txgain: %s\n",argv[3]);
} else {
woomera_config_gain(profile,gain,0);
}
}
ast_cli(fd, "Woomera {%s} txgain: %f\n",profile_name,profile->txgain_val);
} else if (!strcmp(argv[2], "threads")) {
ast_cli(fd, "chan_woomera is using %s threads!\n",
globals.more_threads ? "more" : "less");
} else if (!strcmp(argv[2], "smgdebug")) {
if (argc > 2) {
int smgdebug;
if (sscanf(argv[3], "%d", &smgdebug) != 1) {
ast_cli(fd, "Woomera Invalid smgdebug level: %s\n",argv[3]);
} else {
woomera_printf(NULL, profile->woomera_socket , "debug %d%s",
smgdebug,
WOOMERA_RECORD_SEPARATOR);
}
}
} else if (!strcmp(argv[2], "abort")) {
global_set_flag(TFLAG_ABORT);
}
} else {
ast_cli(fd, "Usage: woomera <profile> <cmd> <option>\n");
}
return 0;
}
static struct ast_cli_entry cli_woomera = { { "woomera", NULL }, woomera_cli, "Woomera", "Woomera" };
/******************************* CORE INTERFACE ********************************************
* These are module-specific interface functions that are common to every module
* To be used to initilize/de-initilize, reload and track the use count of a loadable module.
*/
static void urg_handler(int num)
{
signal(num, urg_handler);
return;
}
int usecount(void)
{
int res;
ast_mutex_lock(&usecnt_lock);
res = usecnt;
ast_mutex_unlock(&usecnt_lock);
return res;
}
#if 0
static char *key(void)
{
return ASTERISK_GPL_KEY;
}
/* returns a descriptive string to the system console */
char *description(void)
{
return (char *) desc;
}
#endif
static struct ast_channel * tech_get_owner( private_object *tech_pvt)
{
struct ast_channel *owner=NULL;
ast_mutex_lock(&tech_pvt->iolock);
if (!ast_test_flag(tech_pvt, TFLAG_TECHHANGUP) && tech_pvt->owner) {
owner=tech_pvt->owner;
}
ast_mutex_unlock(&tech_pvt->iolock);
return owner;
}
static int woomera_event_media (private_object *tech_pvt, woomera_message *wmsg)
{
char *raw_audio_header;
char ip[25];
char *ptr;
int port = 0;
struct hostent hps, *hp;
struct hostent *result;
struct ast_channel *owner;
char buf[512];
int err=0;
memset(&hps,0,sizeof(hps));
hp=&hps;
raw_audio_header = woomera_message_header(wmsg, "Raw-Audio");
if (raw_audio_header == NULL) {
return 1;
}
strncpy(ip, raw_audio_header, sizeof(ip) - 1);
if ((ptr=strchr(ip, '/'))) {
*ptr = '\0';
ptr++;
port = atoi(ptr);
} else if ((ptr=strchr(ip, ':'))) {
*ptr = '\0';
ptr++;
port = atoi(ptr);
}
#if 0
audio_codec = woomera_message_header(wmsg, "Receive-Audio-Codec");
if (audio_codec) {
if (!strcasecmp(audio_codec,"G.711-uLaw-64k")) {
tech_pvt->coding=AST_FORMAT_ULAW;
ast_log(LOG_NOTICE, "MEDIA GOT ULAW\n");
} else if (!strcasecmp(audio_codec,"G.711-aLaw-64k")) {
tech_pvt->coding=AST_FORMAT_ALAW;
ast_log(LOG_NOTICE, "MEDIA GOT ALAW\n");
}
}
#endif
/* Sanity Check */
owner = tech_get_owner(tech_pvt);
if (!owner) {
return -1;
}
/* If we are already in MEDIA mode then
* ignore this message */
if (ast_test_flag(tech_pvt, TFLAG_MEDIA)) {
return 0;
}
gethostbyname_r(ip, hp, buf, sizeof(buf), &result, &err);
if (result) {
tech_pvt->udpwrite.sin_family = hp->h_addrtype;
memcpy((char *) &tech_pvt->udpwrite.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
tech_pvt->udpwrite.sin_port = htons(port);
if (globals.debug > 4) {
ast_log(LOG_NOTICE,"MEDIA EVENT UdpWrite IP=%s/%d len=%i %d.%d.%d.%d\n",
ip,port,
hp->h_length,
hp->h_addr_list[0][0],
hp->h_addr_list[0][1],
hp->h_addr_list[0][2],
hp->h_addr_list[0][3]);
}
ast_set_flag(tech_pvt, TFLAG_MEDIA);
tech_pvt->timeout = 0;
owner = tech_get_owner(tech_pvt);
if (!owner) {
return -1;
}
if (ast_test_flag(tech_pvt, TFLAG_INBOUND)) {
if (ast_pbx_start(owner)) {
ast_log(LOG_ERROR, "Unable to start PBX on %s \n",
tech_pvt->callid);
ast_set_flag(tech_pvt, TFLAG_ABORT);
ast_clear_flag(tech_pvt, TFLAG_PBX);
//ast_hangup();
/* Let destroy hangup */
return -1;
} else {
ast_setstate(owner, AST_STATE_RINGING);
ast_set_flag(tech_pvt, TFLAG_PBX);
owner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
woomera_send_progress(tech_pvt);
return 0;
}
} else {
/* Do nothing for the outbound call
* The PBX flag was already set! */
return 0;
}
} else {
if (globals.debug) {
ast_log(LOG_ERROR, "{%s} Cannot resolve %s\n", tech_pvt->profile->name, ip);
}
return -1;
}
return -1;
}
static int woomera_event_incoming (private_object *tech_pvt)
{
char *exten;
char *cid_name;
char *cid_num;
char *cid_rdnis;
char *tg_string="1";
char *pres_string;
char *screen_string;
int validext;
int presentation=0;
woomera_message wmsg;
struct ast_channel *owner;
wmsg = tech_pvt->call_info;
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "WOOMERA EVENT %s : callid=%s tech_callid=%s tpvt=%p\n",
wmsg.command,wmsg.callid,tech_pvt->callid,tech_pvt);
}
if (ast_strlen_zero(tech_pvt->profile->context)) {
if (globals.debug > 2) {
ast_log(LOG_NOTICE, "No Context Found %s tpvt=%p\n",
tech_pvt->callid,tech_pvt);
}
ast_log(LOG_WARNING, "No context configured for inbound calls aborting call!\n");
ast_set_flag(tech_pvt, TFLAG_ABORT);
return -1;
}
exten = woomera_message_header(&wmsg, "Local-Number");
if (! exten || ast_strlen_zero(exten)) {
exten = "s";
}
tg_string = woomera_message_header(&wmsg, "Trunk-Group");
if (!tg_string || ast_strlen_zero(tg_string)) {
tg_string="1";
}
pres_string = woomera_message_header(&wmsg, "Presentation");
if (pres_string && !ast_strlen_zero(pres_string)) {
presentation |= ( atoi(pres_string) << 5) & 0xF0;
}
screen_string = woomera_message_header(&wmsg, "Screening");
if (screen_string && !ast_strlen_zero(screen_string)) {
presentation |= atoi(screen_string) & 0x0F;
}
cid_name = ast_strdupa(woomera_message_header(&wmsg, "Remote-Name"));
if ((cid_num = strchr(cid_name, '!'))) {
*cid_num = '\0';
cid_num++;
} else {
cid_num = woomera_message_header(&wmsg, "Remote-Number");
}
cid_rdnis = woomera_message_header(&wmsg, "RDNIS");
owner = tech_get_owner(tech_pvt);
if (owner) {
snprintf(owner->context, sizeof(owner->context) - 1,
"%s%s",
tech_pvt->profile->context,
tg_string);
strncpy(owner->exten, exten, sizeof(owner->exten) - 1);
ast_set_callerid(owner, cid_num, cid_name, cid_num);
owner->cid.cid_pres=presentation;
if (cid_rdnis) {
owner->cid.cid_rdnis=strdup(cid_rdnis);
}
validext = ast_exists_extension(owner,
owner->context,
owner->exten,
1,
owner->cid.cid_num);
if (globals.debug > 2){
ast_log(LOG_NOTICE, "Incoming Call exten %s@%s called %s astpres = 0x%0X!\n",
exten,
owner->context,
tech_pvt->callid,
owner->cid.cid_pres);
}
} else {
validext=0;
}
if (validext == 0) {
if (globals.debug > 1){
ast_log(LOG_ERROR, "Error: Invalid exten %s@%s%s called %s!\n",
exten,
tech_pvt->profile->context,
tg_string,
tech_pvt->callid);
}
return -1;
}
return 0;
}
/*==============================================================================
Module Load Section
=============================================================================*/
int load_module(void)
{
int x;
if (ast_channel_register(&technology)) {
ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
return -1;
}
memset(&globals, 0, sizeof(globals));
globals.next_woomera_port = WOOMERA_MIN_PORT;
/* Use more threads for better timing this adds a dedicated monitor thread to
every channel you can disable it with more_threads => no in [settings] */
globals.more_threads = 1;
ast_mutex_init(&globals.woomera_port_lock);
ast_mutex_init(&default_profile.iolock);
ast_mutex_init(&default_profile.call_count_lock);
ast_mutex_init(&default_profile.event_queue.lock);
memset(tech_pvt_idx, 0, sizeof(tech_pvt_idx));
for (x=0;x<WOOMERA_MAX_CALLS;x++){
ast_mutex_init(&tech_pvt_idx_lock[x]);
}
if (!init_woomera()) {
return -1;
}
signal(SIGURG, urg_handler);
ASTOBJ_CONTAINER_INIT(&private_object_list);
/*
sched = sched_context_create();
if (!sched) {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
}
*/
ast_cli_register(&cli_woomera);
return 0;
}
int reload(void)
{
return 0;
}
int unload_module(void)
{
time_t then, now;
woomera_profile *profile = NULL;
int x;
globals.panic=1;
ast_log(LOG_NOTICE, "WOOMERA Unload %i\n",
usecount());
sleep(1);
ASTOBJ_CONTAINER_TRAVERSE(&woomera_profile_list, 1, do {
ASTOBJ_RDLOCK(iterator);
profile = iterator;
time(&then);
if (!ast_test_flag(profile, PFLAG_DISABLED)) {
ast_log(LOG_NOTICE, "Shutting Down Thread. {%s}\n", profile->name);
woomera_profile_thread_running(profile, 1, 0);
while (!woomera_profile_thread_running(profile, 0, 0)) {
time(&now);
if (now - then > 30) {
ast_log(LOG_WARNING, "Timed out waiting for thread to exit\n");
break;
}
usleep(100);
}
}
ASTOBJ_UNLOCK(iterator);
} while(0));
ast_mutex_destroy(&default_profile.iolock);
ast_mutex_destroy(&default_profile.call_count_lock);
ast_mutex_destroy(&default_profile.event_queue.lock);
ast_mutex_destroy(&globals.woomera_port_lock);
for (x=0;x<WOOMERA_MAX_CALLS;x++){
ast_mutex_destroy(&tech_pvt_idx_lock[x]);
}
ast_cli_unregister(&cli_woomera);
ASTOBJ_CONTAINER_DESTROY(&private_object_list);
ASTOBJ_CONTAINER_DESTROYALL(&woomera_profile_list, destroy_woomera_profile);
//sched_context_destroy(sched);
ast_channel_unregister(&technology);
return 0;
}
#ifndef AST14
char *key()
{
return ASTERISK_GPL_KEY;
}
/* returns a descriptive string to the system console */
char *description()
{
return (char *) desc;
}
#else
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Woomera Protocol (WOOMERA)",
.load = load_module,
.unload = unload_module,
.reload = reload,
);
#endif
;