/* 
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthmct@yahoo.com>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * 
 * Anthony Minessale II <anthmct@yahoo.com>
 *
 *
 * mod_woomerachan.c -- Woomera Endpoint Module
 *
 */
#include <switch.h>

#define WOOMERA_STRLEN 256
#define WOOMERA_ARRAY_LEN 50
#define WOOMERA_MIN_PORT 9900
#define WOOMERA_MAX_PORT 9999
#define WOOMERA_BODYLEN 2048
#define WOOMERA_LINE_SEPERATOR "\r\n"
#define WOOMERA_RECORD_SEPERATOR "\r\n\r\n"
#define WOOMERA_DEBUG_PREFIX "**[DEBUG]** "
#define WOOMERA_DEBUG_LINE "--------------------------------------------------------------------------------" 
#define WOOMERA_HARD_TIMEOUT -10000
#define WOOMERA_QLEN 10
#define WOOMERA_RECONNECT_TIME 5000000
#define MEDIA_ANSWER "ANSWER"
// THE ONE ABOVE OR THE 2 BELOW BUT NOT BOTH
//#define MEDIA_ANSWER "ANSWER"
//#define USE_ANSWER 1

static const char modname[] = "mod_woomera";

static switch_memory_pool *module_pool;

#define STRLEN 15
#define FRAME_LEN 480
//static int WFORMAT = AST_FORMAT_SLINEAR;

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_SWITCH = (1 << 9),
	TFLAG_ANSWER = (1 << 10),
} TFLAGS;

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];
	unsigned int flags;
	int last;
	struct woomera_message *next;
};


static struct {
	int next_woomera_port;	
	int debug;
	int panic;
	int rtpmode;
} globals;

struct woomera_event_queue {
	struct woomera_message *head;
};

struct woomera_profile {
	char *name;
	switch_socket_t *woomera_socket;
	apr_thread_mutex_t *iolock;
	char woomera_host[WOOMERA_STRLEN];
	int woomera_port;
	char audio_ip[WOOMERA_STRLEN];
	char dialplan[WOOMERA_STRLEN];
//	pthread_t thread;
	unsigned int flags;
	int thread_running;
	struct woomera_event_queue event_queue;
};


struct private_object {
	char *name;
	switch_frame frame;
	switch_codec read_codec;
	switch_codec write_codec;
	switch_core_session *session;
	switch_pollfd_t read_poll;
	switch_pollfd_t write_poll;
	switch_pollfd_t command_poll;
	unsigned char databuf[2048];
	switch_mutex_t *iolock;
	switch_sockaddr_t *udpread;
	switch_sockaddr_t *udpwrite;
	switch_socket_t *command_channel;
	switch_socket_t *udp_socket;
	unsigned int flags;
	short fdata[FRAME_LEN];
	struct woomera_message call_info;
	struct woomera_profile *profile;
	char dest[WOOMERA_STRLEN];
	int port;
	switch_time_t started;
	int timeout;
	char dtmfbuf[WOOMERA_STRLEN];
	switch_caller_profile *caller_profile;
	struct woomera_event_queue event_queue;
};

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 woomera_profile default_profile;

static const switch_endpoint_interface woomerachan_endpoint_interface;

static switch_status woomerachan_on_init(switch_core_session *session);
static switch_status woomerachan_on_hangup(switch_core_session *session);
static switch_status woomerachan_on_ring(switch_core_session *session);
static switch_status woomerachan_on_loopback(switch_core_session *session);
static switch_status woomerachan_on_transmit(switch_core_session *session);
static switch_status woomerachan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
static switch_status woomerachan_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags, int stream_id);
static switch_status woomerachan_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags, int stream_id);
static switch_status woomerachan_kill_channel(switch_core_session *session, int sig);
static void tech_destroy(private_object *tech_pvt);
static void woomera_printf(woomera_profile *profile, switch_socket_t *socket, 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(switch_socket_t *fd, woomera_message *wmsg, int timeout, woomera_profile *profile, woomera_event_queue *event_queue);
static int connect_woomera(switch_socket_t **new_sock, woomera_profile *profile, int flags);
static int woomera_profile_thread_running(woomera_profile *profile, int set, int new);
static int woomera_locate_socket(woomera_profile *profile, switch_socket_t **woomera_socket);
static int tech_create_read_socket(private_object *tech_pvt);
static void *woomera_channel_thread_run(switch_thread *thread, void *obj);
static void *woomera_thread_run(void *obj);
static int tech_activate(private_object *tech_pvt);

/* 
   State methods they get called when the state changes to the specific state 
   returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
   so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
*/
static switch_status woomerachan_on_init(switch_core_session *session)
{
	switch_channel *channel;
	struct private_object *tech_pvt = NULL;
	int rate = 8000;

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt->frame.data = tech_pvt->databuf;

	if (switch_core_codec_init(&tech_pvt->read_codec, "L16", rate, 30, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s Cannot set read codec\n", switch_channel_get_name(channel));
		switch_channel_hangup(channel);
		return SWITCH_STATUS_FALSE;
	}

	if (switch_core_codec_init(&tech_pvt->write_codec, "L16", rate, 30, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s Cannot set read codec\n", switch_channel_get_name(channel));
		switch_channel_hangup(channel);
		return SWITCH_STATUS_FALSE;
	}
	tech_pvt->frame.rate = rate;
	tech_pvt->frame.codec = &tech_pvt->read_codec;
	switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
	switch_core_session_set_write_codec(session, &tech_pvt->write_codec);


	switch_set_flag(tech_pvt, TFLAG_ACTIVATE);

	switch_core_session_launch_thread(session, woomera_channel_thread_run, session);
	
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN INIT\n", switch_channel_get_name(channel));

	return SWITCH_STATUS_SUCCESS;
}

static switch_status woomerachan_on_ring(switch_core_session *session)
{
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN RING\n", switch_channel_get_name(channel));

	return SWITCH_STATUS_SUCCESS;
}

static switch_status woomerachan_on_execute(switch_core_session *session)
{

	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);
	
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN EXECUTE\n", switch_channel_get_name(channel));


	return SWITCH_STATUS_SUCCESS;
}

static switch_status woomerachan_on_hangup(switch_core_session *session)
{
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;
	
	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);


	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN HANGUP\n", switch_channel_get_name(channel));
	tech_destroy(tech_pvt);

	return SWITCH_STATUS_SUCCESS;
}

static void woomera_socket_close(switch_socket_t **socket)
{
	if (*socket) {
		switch_socket_close(*socket);
		*socket = NULL;
	}
}


static void udp_socket_close(struct private_object *tech_pvt) 
{
	if (tech_pvt->udp_socket) {
		apr_socket_shutdown(tech_pvt->udp_socket, APR_SHUTDOWN_READWRITE);
		woomera_socket_close(&tech_pvt->udp_socket);
	}
}


static switch_status woomerachan_kill_channel(switch_core_session *session, int sig)
{
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;
	
	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	if (!tech_pvt->udp_socket) {
		return SWITCH_STATUS_FALSE;
	}

	udp_socket_close(tech_pvt);

	switch_channel_hangup(channel);
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN KILL %d\n", switch_channel_get_name(channel), tech_pvt->udp_socket);

	
	return SWITCH_STATUS_SUCCESS;
}

static switch_status woomerachan_on_loopback(switch_core_session *session)
{
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WOOMERACHAN LOOPBACK\n");
	return SWITCH_STATUS_SUCCESS;
}

static switch_status woomerachan_on_transmit(switch_core_session *session)
{
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WOOMERACHAN TRANSMIT\n");
	return SWITCH_STATUS_SUCCESS;
}


/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
   that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
*/
static switch_status woomerachan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session) 
{
	if ((*new_session = switch_core_session_request(&woomerachan_endpoint_interface, NULL))) {
		struct private_object *tech_pvt;
		switch_channel *channel;
		
		switch_core_session_add_stream(*new_session, NULL);
		if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) {
			memset(tech_pvt, 0, sizeof(*tech_pvt));
			tech_pvt->profile = &default_profile;
			channel = switch_core_session_get_channel(*new_session);
			switch_core_session_set_private(*new_session, tech_pvt);
			tech_pvt->session = *new_session;
		} else {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
			switch_core_session_destroy(new_session);
			return SWITCH_STATUS_GENERR;
		}

		if (outbound_profile) {
			char name[128];
			switch_caller_profile *caller_profile;

			caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
			switch_channel_set_caller_profile(channel, caller_profile);
			tech_pvt->caller_profile = caller_profile;
			snprintf(name, sizeof(name), "Woomera/%s-%04x", caller_profile->destination_number, rand() & 0xffff);
			switch_channel_set_name(channel, name);
		} else {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Doh! no caller profile\n");
			switch_core_session_destroy(new_session);
			return SWITCH_STATUS_GENERR;
		}

		switch_channel_set_flag(channel, CF_OUTBOUND);
		switch_set_flag(tech_pvt, TFLAG_OUTBOUND);
		switch_channel_set_state(channel, CS_INIT);
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_GENERR;

}

static switch_status woomerachan_waitfor_read(switch_core_session *session, int ms, int stream_id)
{
	struct private_object *tech_pvt = NULL;

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	return switch_socket_waitfor(&tech_pvt->read_poll, ms);
}

static switch_status woomerachan_waitfor_write(switch_core_session *session, int ms, int stream_id)
{
	struct private_object *tech_pvt = NULL;

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	return SWITCH_STATUS_SUCCESS;
	return switch_socket_waitfor(&tech_pvt->write_poll, ms);
}

static switch_status woomerachan_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags, int stream_id) 
{
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;
	switch_frame *pframe;
	switch_status status;

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	if (!tech_pvt->udp_socket) {
		return SWITCH_STATUS_GENERR;
	}
	/*
	if ((status = woomerachan_waitfor_read(session, -1)) != SWITCH_STATUS_SUCCESS) {
		return status;
	}1<
	*/
	pframe = &tech_pvt->frame;
	*frame = pframe;	
	
	pframe->datalen = sizeof(tech_pvt->databuf);
	if ((status = switch_socket_recvfrom (tech_pvt->udpread, tech_pvt->udp_socket, 0, tech_pvt->databuf, &pframe->datalen)) == SWITCH_STATUS_SUCCESS) {
		pframe->samples = (int)pframe->datalen / 2;
	}
	return status;
}

static switch_status woomerachan_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags, int stream_id)
{
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;
	switch_frame *pframe;

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	if (!tech_pvt->udp_socket) {
		return SWITCH_STATUS_GENERR;
	}

	pframe = &tech_pvt->frame;
	return switch_socket_sendto(tech_pvt->udp_socket, tech_pvt->udpwrite, 0, frame->data, &frame->datalen);
	
}

static const switch_event_handler_table woomerachan_event_handlers = {
	/*.on_init*/			woomerachan_on_init,
	/*.on_ring*/			woomerachan_on_ring,
	/*.on_execute*/			woomerachan_on_execute,
	/*.on_hangup*/			woomerachan_on_hangup,
	/*.on_loopback*/		woomerachan_on_loopback,
	/*.on_transmit*/		woomerachan_on_transmit
};

static const switch_io_routines woomerachan_io_routines = {
	/*.outgoing_channel*/	woomerachan_outgoing_channel,
	/*.answer_channel*/		NULL,
	/*.read_frame*/			woomerachan_read_frame,
	/*.write_frame*/		woomerachan_write_frame,
	/*.kill_channel*/		woomerachan_kill_channel,
	/*.waitfor_read*/		woomerachan_waitfor_read,
	/*.waitfor_write*/		woomerachan_waitfor_write
};

static const switch_endpoint_interface woomerachan_endpoint_interface = {
	/*.interface_name*/		"woomera",
	/*.io_routines*/		&woomerachan_io_routines,
	/*.event_handlers*/		&woomerachan_event_handlers,
	/*.private*/			NULL,
	/*.next*/				NULL
};

static const switch_loadable_module_interface woomerachan_module_interface = {
	/*.module_name*/			modname,
	/*.endpoint_interface*/		&woomerachan_endpoint_interface,
	/*.timer_interface*/		NULL,
	/*.dialplan_interface*/		NULL,
	/*.codec_interface*/		NULL,
	/*.application_interface*/	NULL
};


static void tech_destroy(private_object *tech_pvt) 
{
	woomera_message wmsg;
	
	if (globals.debug > 1) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "+++DESTROY\n");
	}


	woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "hangup %s%s", tech_pvt->call_info.callid, WOOMERA_RECORD_SEPERATOR);
	if(woomera_message_parse(tech_pvt->command_channel,
							 &wmsg,
							 WOOMERA_HARD_TIMEOUT,
							 tech_pvt->profile,
							 &tech_pvt->event_queue
							 ) < 0) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE,	 "{%s} Already Disconnected\n", tech_pvt->profile->name);
	}
	
	woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "bye%s", WOOMERA_RECORD_SEPERATOR);
	woomera_socket_close(&tech_pvt->command_channel);
	udp_socket_close(tech_pvt);
}


static void woomera_printf(woomera_profile *profile, switch_socket_t *socket, char *fmt, ...)
{
	char *stuff;
	size_t res = 0, len = 0;

	va_list ap;
	va_start(ap, fmt);
#ifndef vasprintf
	stuff = (char *)malloc(10240);
	vsnprintf(stuff, 10240, fmt, ap);
#else
	res = vasprintf(&stuff, fmt, ap);
#endif
	va_end(ap);
	if (res == -1) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Out of memory\n");
	} else {
		if (profile && globals.debug) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Send Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, stuff);
		}
		len = strlen(stuff);
		switch_socket_send(socket, stuff, &len);
		
		free(stuff);
	}

}

static char *woomera_message_header(woomera_message *wmsg, char *key) 
{
	int x = 0;
	char *value = NULL;

	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)))) {
		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;
		}
		return 1;
	} else {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Memory Allocation Error!\n");
	}

	return 0;
}

static int woomera_dequeue_event(woomera_event_queue *event_queue, woomera_message *wmsg)
{
	woomera_message *mptr = NULL;
	
	if (event_queue->head) {
		mptr = event_queue->head;
		event_queue->head = mptr->next;
	}

	if (mptr) {
		memcpy(wmsg, mptr, sizeof(woomera_message));
		free(mptr);
		return 1;
	} else {
		memset(wmsg, 0, sizeof(woomera_message));
	}
	
	return 0;
}

static int woomera_message_parse(switch_socket_t *fd, woomera_message *wmsg, int timeout, woomera_profile *profile, woomera_event_queue *event_queue) 
{
	char *cur, *cr, *next = NULL, *eor = NULL;
	char buf[2048] = "", *ptr;
	int bytes = 0;
	int failto = 0;

	memset(wmsg, 0, sizeof(woomera_message));

	if (fd < 0 ) {
		return -1;
	}

	if (timeout < 0) {
		timeout = abs(timeout);
		failto = 1;
	} else if (timeout == 0) {
		timeout = -1;
	}

	ptr = buf;
	bytes = 0;
	while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) {
		size_t len = 1;

		if (!profile->thread_running) {
			return -1;
		}

		if (switch_socket_recv(fd, ptr, &len) != SWITCH_STATUS_SUCCESS) {
			return -1;
		}
		ptr++;
		bytes++;
	}
	//*eor = '\0';
	next = buf;

	if (globals.debug) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "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_SEPERATOR))) {
			*cr = '\0';
			next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1);
			if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) {
				break;
			}
		} 

		if (!cur || !cur[0]) {
			break;
		}

		if (!wmsg->last) {
			switch_set_flag(wmsg, WFLAG_EXISTS);
			if (!strncasecmp(cur, "EVENT", 5)) {
				cur += 6;
				switch_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) {
						strncpy(wmsg->callid, id, sizeof(wmsg->callid) - 1);
					}
				}
			} else {
				if (cur && (cur = strchr(cur, ' '))) {
					*cur = '\0';
					cur++;
					wmsg->mval = atoi(buf);
				} else {
					switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Malformed Message!\n");
					break;
				}
			}
			if (cur) {
				strncpy(wmsg->command, cur, WOOMERA_STRLEN);
			} else {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE, "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")) {
				switch_set_flag(wmsg, WFLAG_CONTENT);
				bytes = atoi(val);
			}

		}
		wmsg->last++;
	}

	wmsg->last--;

	if (bytes && switch_test_flag(wmsg, WFLAG_CONTENT)) {
		size_t len = (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes;
		switch_socket_recv(fd, wmsg->body, &len);

		if (globals.debug) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s\n", wmsg->body);
		}
	}

	if (event_queue && switch_test_flag(wmsg, WFLAG_EVENT)) {
		if (globals.debug) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "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 switch_test_flag(wmsg, WFLAG_EXISTS);
	}
}


static int connect_woomera(switch_socket_t **new_sock, woomera_profile *profile, int flags) 
{

	switch_sockaddr_t *sa;
	switch_status status;

	status = switch_sockaddr_info_get(&sa, profile->woomera_host, AF_INET, profile->woomera_port, 0, module_pool);
	if (status != SWITCH_STATUS_SUCCESS) {
		return -1;
	}
	status = switch_socket_create(new_sock, AF_INET, SOCK_STREAM, 0, module_pool);
	if (status != SWITCH_STATUS_SUCCESS) {
		return -1;
	}
	/*
	status = switch_socket_bind((*new_sock), sa);
	if (0 && status != SWITCH_STATUS_SUCCESS) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't Bind to %s:%d!\n", profile->woomera_host, profile->woomera_port);
		return -1;
	}
	*/
	status = switch_socket_connect((*new_sock), sa);
	if (status != SWITCH_STATUS_SUCCESS) {
		return -1;
	}

	return 1;
}

static int woomera_profile_thread_running(woomera_profile *profile, int set, int new) 
{
	int running = 0;

	switch_mutex_lock(profile->iolock);
	if (set) {
		profile->thread_running = new;
	}
	running = profile->thread_running;
	switch_mutex_unlock(profile->iolock);
	return running;
	
}

static int woomera_locate_socket(woomera_profile *profile, switch_socket_t **woomera_socket) 
{
	woomera_message wmsg;
	
	for (;;) {

		while (connect_woomera(woomera_socket, profile, 0) < 0) {
			if (!woomera_profile_thread_running(profile, 0, 0)) {
				break;
			}
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} Cannot Reconnect to Woomera! retry in 5 seconds\n", profile->name);
			switch_sleep(WOOMERA_RECONNECT_TIME);
		}

		if (*woomera_socket) {
			if (switch_test_flag(profile, PFLAG_INBOUND)) {
				woomera_printf(profile, *woomera_socket, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
				if (woomera_message_parse(*woomera_socket,
										  &wmsg,
										  WOOMERA_HARD_TIMEOUT,
										  profile,
										  &profile->event_queue
										  ) < 0) {
					switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", profile->name);
					globals.panic = 1;
					woomera_profile_thread_running(&default_profile, 1, 0);
					switch_sleep(WOOMERA_RECONNECT_TIME);
					if (*woomera_socket) {
						woomera_socket_close(woomera_socket);
					}
					
					continue;
				}
			}

		}
		switch_sleep(100);
		break;
	}	
	return *woomera_socket ? 1 : 0;
}



static int tech_create_read_socket(private_object *tech_pvt)
{
	switch_memory_pool *pool = switch_core_session_get_pool(tech_pvt->session);

	if ((tech_pvt->port = globals.next_woomera_port++) >= WOOMERA_MAX_PORT) {
		tech_pvt->port = globals.next_woomera_port = WOOMERA_MIN_PORT;
	}

	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connect %s:%d\n", tech_pvt->profile->audio_ip, tech_pvt->port);
	//tech_pvt->udp_socket = create_udp_socket(tech_pvt->profile->audio_ip, tech_pvt->port, &tech_pvt->udpread, 0);

	switch_sockaddr_info_get(&tech_pvt->udpread, tech_pvt->profile->audio_ip, SWITCH_UNSPEC, tech_pvt->port, 0, pool);
	if (switch_socket_create(&tech_pvt->udp_socket, AF_INET, SOCK_DGRAM, 0, pool) == SWITCH_STATUS_SUCCESS) {
		switch_socket_bind(tech_pvt->udp_socket, tech_pvt->udpread);
		switch_socket_create_pollfd(&tech_pvt->read_poll, tech_pvt->udp_socket, SWITCH_POLLIN|SWITCH_POLLERR, pool);
		switch_socket_create_pollfd(&tech_pvt->write_poll, tech_pvt->udp_socket, SWITCH_POLLOUT|SWITCH_POLLERR, pool);
	}

	return 0;
}


static int tech_activate(private_object *tech_pvt) 
{
	woomera_message wmsg;

	if (tech_pvt) {
		if((connect_woomera(&tech_pvt->command_channel, tech_pvt->profile, 0))) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connected to woomera!\n");
		} else {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't connect to woomera!\n");
			switch_sleep(WOOMERA_RECONNECT_TIME);
			return -1;
		}

		if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {

			woomera_printf(tech_pvt->profile,
						   tech_pvt->command_channel, 
						   "CALL %s%sRaw-Audio: %s/%d%sLocal-Name: %s!%s%s", 
						   tech_pvt->caller_profile->destination_number,
						   WOOMERA_LINE_SEPERATOR,
						   tech_pvt->profile->audio_ip,
						   tech_pvt->port,
						   WOOMERA_LINE_SEPERATOR,
						   tech_pvt->caller_profile->caller_id_name,
						   tech_pvt->caller_profile->caller_id_number,
						   WOOMERA_RECORD_SEPERATOR
						   );

			woomera_message_parse(tech_pvt->command_channel,
								  &wmsg,
								  WOOMERA_HARD_TIMEOUT,
								  tech_pvt->profile,
								  &tech_pvt->event_queue
								  );
		} else {
			switch_set_flag(tech_pvt, TFLAG_PARSE_INCOMING);
			woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
			if (woomera_message_parse(tech_pvt->command_channel,
									  &wmsg,
									  WOOMERA_HARD_TIMEOUT,
									  tech_pvt->profile,
									  &tech_pvt->event_queue
									  ) < 0) {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
				switch_set_flag(tech_pvt, TFLAG_ABORT);
				globals.panic = 1;
			}
		}
	} else {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Where's my tech_pvt?\n");
	}

	return 0;
}



static void *woomera_channel_thread_run(switch_thread *thread, void *obj)
{
	switch_core_session *session = obj;
	switch_channel *channel = NULL;
	struct private_object *tech_pvt = NULL;
	woomera_message wmsg;
	int res = 0;

	assert(session != NULL);

	channel = switch_core_session_get_channel(session);
	assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	assert(tech_pvt != NULL);

	if (!tech_pvt->udp_socket) {
		tech_create_read_socket(tech_pvt);
	}
	

	for(;;) {
		if (globals.panic) {
			switch_set_flag(tech_pvt, TFLAG_ABORT);
		}

		if (switch_test_flag(tech_pvt, TFLAG_ABORT)) {
			if (switch_channel_get_state(channel) < CS_HANGUP) {
				switch_channel_set_state(channel, CS_HANGUP);
			}
			udp_socket_close(tech_pvt);
			break;
		}

		if (switch_test_flag(tech_pvt, TFLAG_ACTIVATE)) {
			switch_clear_flag(tech_pvt, TFLAG_ACTIVATE);
			tech_activate(tech_pvt);
		}

		if (switch_test_flag(tech_pvt, TFLAG_ANSWER)) {
			switch_clear_flag(tech_pvt, TFLAG_ANSWER);
#ifdef USE_ANSWER
			woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "ANSWER %s%s",tech_pvt->call_info.callid, WOOMERA_RECORD_SEPERATOR);
			if(woomera_message_parse(tech_pvt->command_channel,
									 &wmsg,
									 WOOMERA_HARD_TIMEOUT,
									 tech_pvt->profile,
									 &tech_pvt->event_queue
									 ) < 0) {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE,	 "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
				switch_set_flag(tech_pvt, TFLAG_ABORT);
				globals.panic = 1;
				continue;
			}
#endif
		}
		
		if (switch_test_flag(tech_pvt, TFLAG_DTMF)) {
			switch_mutex_lock(tech_pvt->iolock);
			woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "DTMF %s %s%s",tech_pvt->call_info.callid, tech_pvt->dtmfbuf, WOOMERA_RECORD_SEPERATOR);
			if(woomera_message_parse(tech_pvt->command_channel,
									 &wmsg,
									 WOOMERA_HARD_TIMEOUT,
									 tech_pvt->profile,
									 &tech_pvt->event_queue
									 ) < 0) {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE,	 "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
				switch_set_flag(tech_pvt, TFLAG_ABORT);
				globals.panic = 1;
				continue;
			}
			switch_clear_flag(tech_pvt, TFLAG_DTMF);
			memset(tech_pvt->dtmfbuf, 0, sizeof(tech_pvt->dtmfbuf));
			switch_mutex_unlock(tech_pvt->iolock);
		}

#if 1==0   /*convert to use switch_time_now */
		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! */
				switch_set_flag(tech_pvt, TFLAG_ABORT);
			}
		}
#endif

		if (!tech_pvt->command_channel) {
			break;
		}
		/* 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 (res < 0 || !strcasecmp(wmsg.command, "HANGUP")) {
				switch_set_flag(tech_pvt, TFLAG_ABORT);
				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++) {
				  dtmf_frame.subclass = wmsg.command_args[x];
				  ast_queue_frame(tech_pvt->owner, ast_frdup(&dtmf_frame));
				  if (globals.debug > 1) {
				  switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "SEND DTMF [%c] to %s\n", dtmf_frame.subclass, tech_pvt->owner->name);
				  }
				  }
				*/
			} else if (!strcasecmp(wmsg.command, "PROCEED")) {
				/* This packet has lots of info so well keep it */
				tech_pvt->call_info = wmsg;
			} else if (switch_test_flag(tech_pvt, TFLAG_PARSE_INCOMING) && !strcasecmp(wmsg.command, "INCOMING")) {
				char *exten;
				char cid_name[512];
				char *cid_num;
				char *ip;
				char *p;
				switch_clear_flag(tech_pvt, TFLAG_PARSE_INCOMING);
				switch_set_flag(tech_pvt, TFLAG_INCOMING);
				tech_pvt->call_info = wmsg;

				exten = woomera_message_header(&wmsg, "Local-Number");
				if (switch_strlen_zero(exten)) {
					exten = "s";
				}

				if ((p = woomera_message_header(&wmsg, "Remote-Name"))) {
					strncpy(cid_name, p, sizeof(cid_name));
				}
				
				if ((cid_num = strchr(cid_name, '!'))) {
					*cid_num = '\0';
					cid_num++;
				} else {
					cid_num = woomera_message_header(&wmsg, "Remote-Number");
				}
				ip = woomera_message_header(&wmsg, "Remote-Address");

				if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
																		  tech_pvt->profile->dialplan,
																		  cid_name,
																		  cid_num,
																		  ip,
																		  NULL,
																		  NULL,
																		  exten))) {
					char name[128];
					switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
					snprintf(name, sizeof(name), "Woomera/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
					switch_channel_set_name(channel, name);
				}

				woomera_printf(tech_pvt->profile, tech_pvt->command_channel, 
							   "%s %s%s"
							   "Raw-Audio: %s/%d%s",
							   MEDIA_ANSWER,
							   wmsg.callid,
							   WOOMERA_LINE_SEPERATOR,
							   tech_pvt->profile->audio_ip,
							   tech_pvt->port,
							   WOOMERA_RECORD_SEPERATOR);
				
				if(woomera_message_parse(tech_pvt->command_channel, 
										 &wmsg,
										 WOOMERA_HARD_TIMEOUT,
										 tech_pvt->profile,
										 &tech_pvt->event_queue
										 ) < 0) {
					switch_console_printf(SWITCH_CHANNEL_CONSOLE,	 "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
					switch_set_flag(tech_pvt, TFLAG_ABORT);
					globals.panic = 1;
					continue;
				}
			
			} else if (!strcasecmp(wmsg.command, "CONNECT")) {

			} else if (!strcasecmp(wmsg.command, "MEDIA")) {
				char *raw_audio_header;

				if ((raw_audio_header = woomera_message_header(&wmsg, "Raw-Audio"))) {
					char ip[25];
					char *ptr;
					int port = 0;

					strncpy(ip, raw_audio_header, sizeof(ip) - 1);
					if ((ptr=strchr(ip, '/'))) {
						*ptr = '\0';
						ptr++;
						port = atoi(ptr);
					}
					/* Move Channel's State Machine to RING */
					switch_channel_answer(channel);
					switch_channel_set_state(channel, CS_RING);

					if (switch_sockaddr_info_get(&tech_pvt->udpwrite,
												 ip,
												 SWITCH_UNSPEC,
												 port,
												 0,
												 switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
						if (globals.debug) {
							switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "{%s} Cannot resolve %s\n", tech_pvt->profile->name, ip);
						}
						switch_channel_hangup(channel);
					}
				}
			}
		}
		if (globals.debug > 2) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "CHECK {%s}(%d)\n", tech_pvt->profile->name, res);
		}
	}
	if (globals.debug > 1) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "Monitor thread for %s done.\n", tech_pvt->profile->name);
	}

	return NULL;
}
	



static void *woomera_thread_run(void *obj) 
{

	int res = 0;
	woomera_message wmsg;
	woomera_profile *profile;

	profile = obj;
	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Started Woomera Thread {%s}.\n", profile->name);
 
	profile->thread_running = 1;
	profile->woomera_socket = NULL;


	while(woomera_profile_thread_running(profile, 0, 0)) {
		/* listen on socket and handle events */
		if (globals.panic == 2) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera is disabled!\n");
			switch_sleep(WOOMERA_RECONNECT_TIME);
			continue;
		}

		if (! profile->woomera_socket) {
			if (woomera_locate_socket(profile, &profile->woomera_socket)) {
				globals.panic = 0;
			}
			if (!woomera_profile_thread_running(profile, 0, 0)) {
				break;
			}
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera Thread Up {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);

		}

		if (globals.panic) {
			if (globals.panic != 2) {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Help I'm in a state of panic!\n");
				globals.panic = 0;//fix
			}
			woomera_socket_close(&profile->woomera_socket);


			continue;
		}

		if ((res = woomera_dequeue_event(&profile->event_queue, &wmsg) ||
			 (res = woomera_message_parse(profile->woomera_socket,
										  &wmsg,
										  /* if we are not stingy with threads we can block forever */
										  0,
										  profile,
										  NULL
										  )))) {
			if (res < 0) {
				switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! I lost my connection to woomera!\n", profile->name);
				woomera_socket_close(&profile->woomera_socket);

				//global_set_flag(TFLAG_ABORT);
				globals.panic = 1;
				continue;

				if (profile->woomera_socket) {
					if (switch_test_flag(profile, PFLAG_INBOUND)) {
						woomera_printf(profile, profile->woomera_socket, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
						if (woomera_message_parse(profile->woomera_socket,
												  &wmsg,
												  WOOMERA_HARD_TIMEOUT,
												  profile,
												  &profile->event_queue
												  ) < 0) {
							switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", profile->name);
							globals.panic = 1;
							woomera_socket_close(&profile->woomera_socket);
						} 
					}
					if (profile->woomera_socket) {
						switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera Thread Up {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);
					}
				}
				continue;
			}

			if (!strcasecmp(wmsg.command, "INCOMING")) {
				char *name;
				switch_core_session *session;
				
				if (!(name = woomera_message_header(&wmsg, "Remote-Address"))) {
					name = woomera_message_header(&wmsg, "Channel-Name");
				}
				
				switch_console_printf(SWITCH_CHANNEL_CONSOLE, "New Inbound Channel %s!\n", name);
				if ((session = switch_core_session_request(&woomerachan_endpoint_interface, NULL))) {
					struct private_object *tech_pvt;
					switch_channel *channel;

					switch_core_session_add_stream(session, NULL);

					if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object)))) {
						memset(tech_pvt, 0, sizeof(*tech_pvt));
						tech_pvt->profile = &default_profile;
						channel = switch_core_session_get_channel(session);
						switch_core_session_set_private(session, tech_pvt);
						tech_pvt->session = session;
					} else {
						switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
						switch_core_session_destroy(&session);
						break;
					}
					switch_channel_set_state(channel, CS_INIT);
					switch_core_session_thread_launch(session);
				}
			}
		}
		
		if (globals.debug > 2) {
			switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Main Thread {%s} Select Return %d\n", profile->name, res);
		}

		switch_yield(100);
	}
	

	if (profile->woomera_socket) {
		woomera_printf(profile, profile->woomera_socket, "BYE%s", WOOMERA_RECORD_SEPERATOR);
		woomera_socket_close(&profile->woomera_socket);
	}

	switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Ended Woomera Thread {%s}.\n", profile->name);
	woomera_profile_thread_running(profile, 1, -1);
	return NULL;
}

SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
{

	woomera_thread_run(&default_profile);

	return SWITCH_STATUS_TERM;
}

SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
{
	int x = 0;
	woomera_profile_thread_running(&default_profile, 1, 0);
	while (!woomera_profile_thread_running(&default_profile, 0, 0)) {
		woomera_socket_close(&default_profile.woomera_socket);
		if (x++ > 10) {
			break;
		}
		switch_yield(1);
	}
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {

	switch_config cfg;
	char *var, *val;
	struct woomera_profile *profile = &default_profile;
	char *cf = "woomera.conf";
	
	memset(&globals, 0, sizeof(globals));
	globals.next_woomera_port = WOOMERA_MIN_PORT;
	
	if (!switch_config_open_file(&cfg, cf)) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
		return SWITCH_STATUS_TERM;
	}
	
	switch_set_flag(profile, PFLAG_INBOUND | PFLAG_OUTBOUND);
	profile->name = "main";
	strncpy(profile->dialplan, "default", sizeof(profile->dialplan) - 1);
	
	while (switch_config_next_pair(&cfg, &var, &val)) {
		if (!strcasecmp(cfg.category, "settings")) {
		if (!strcmp(var, "noload") && atoi(val)) {			
			return SWITCH_STATUS_TERM;
		}	
			if (!strcmp(var, "debug")) {
				globals.debug = atoi(val);
			}
		} else if (!strcasecmp(cfg.category, "profile")) {
			if (!strcmp(var, "audio_ip")) {
				strncpy(profile->audio_ip, val, sizeof(profile->audio_ip) - 1);
			} else if (!strcmp(var, "host")) {
				strncpy(profile->woomera_host, val, sizeof(profile->woomera_host) - 1);
			} else if (!strcmp(var, "port")) {
				profile->woomera_port = atoi(val);
			} else if (!strcmp(var, "disabled")) {
				if (atoi(val) > 0) {
					switch_set_flag(profile, PFLAG_DISABLED);
				}
			} else if (!strcmp(var, "inbound")) {
				if (atoi(val) < 1) {
					switch_clear_flag(profile, PFLAG_INBOUND);
				}
			} else if (!strcmp(var, "outbound")) {
				if (atoi(val) < 1) {
					switch_clear_flag(profile, PFLAG_OUTBOUND);
				}
			} else if (!strcmp(var, "dialplan")) {
				strncpy(profile->dialplan, val, sizeof(profile->dialplan) - 1);
			}
		}
	}

	switch_config_close_file(&cfg);

	
	if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
		//switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");

		if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
			//switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
			return SWITCH_STATUS_MEMERR;
		}
		return SWITCH_STATUS_MEMERR;
	}



	if (switch_mutex_init(&default_profile.iolock, SWITCH_MUTEX_NESTED, module_pool) != SWITCH_STATUS_SUCCESS) {
		switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no lock\n");
		return SWITCH_STATUS_TERM;
	}
	

	/* connect my internal structure to the blank pointer passed to me */
	*interface = &woomerachan_module_interface;

	/* indicate that the module should continue to be loaded */
	return SWITCH_STATUS_SUCCESS;
}



