include $(TOPDIR)/rules.mk
PKG_NAME:=asterisk
-PKG_VERSION:=1.2.1
+PKG_VERSION:=1.2.14
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://ftp.digium.com/pub/asterisk/old-releases/ ftp://ftp.digium.com/pub/asterisk/old-releases/
-PKG_MD5SUM:=04657086791e80f319c0d728af705001
+PKG_MD5SUM:=2ce03466b99e0b9471e6c791ed14a5f2
PKG_CAT:=zcat
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
protocols, and can interoperate with almost all standards-based telephony \\\
equipment using relatively inexpensive hardware.
URL:=http://www.asterisk.org/
+ SUBMENU:=asterisk (Complete Open Source PBX)
endef
define Package/asterisk
$(call Package/asterisk/Default)
DEPENDS:=+libncurses +libpthread
- TITLE:=Complete open source PBX
- MENU:=1
+ TITLE:=Complete Open Source PBX
endef
define Package/asterisk/conffiles
define Package/asterisk-mini
$(call Package/asterisk/Default)
- DEPENDS:=asterisk
- TITLE:=Minimal open source PBX
+ DEPENDS:=+libncurses +libpthread
+ PROVIDES:=asterisk
+ TITLE:=Minimal Open Source PBX
DESCRIPTION+=\\\
\\\
This package contains only the following modules: \\\
+ - app_dial\\\
- chan_iax2\\\
- chan_local\\\
- chan_sip\\\
/etc/asterisk/sip.conf
endef
+define Package/asterisk-mysql
+ $(call Package/asterisk/Default)
+ DEPENDS:=asterisk +libmysqlclient
+ TITLE:=MySQL support
+ DESCRIPTION+=\\\
+ \\\
+ This package contains MySQL support modules for Asterisk.
+endef
+
+define Package/asterisk-mysql/conffiles
+/etc/asterisk/cdr_mysql.conf
+endef
+
+define Package/asterisk-pgsql
+ $(call Package/asterisk/Default)
+ DEPENDS:=asterisk +libpq
+ TITLE:=PostgreSQL support
+ DESCRIPTION+=\\\
+ \\\
+ This package contains PostgreSQL support modules for Asterisk.
+endef
+
+define Package/asterisk-pgsql/conffiles
+/etc/asterisk/cdr_pgsql.conf
+endef
+
+define Package/asterisk-sqlite
+ $(call Package/asterisk/Default)
+ DEPENDS:=asterisk +libsqlite2
+ TITLE:=SQLite modules
+ DESCRIPTION+=\\\
+ \\\
+ This package contains SQLite support modules for Asterisk.
+endef
+
+define Package/asterisk-sounds
+ $(call Package/asterisk)
+ MENU:=0
+ DEPENDS:=asterisk
+ TITLE:=Sound files
+ DESCRIPTION+=\\\
+ \\\
+ This package contains sound files for Asterisk.
+endef
+
+define Package/asterisk-voicemail
+ $(call Package/asterisk/Default)
+ DEPENDS:=asterisk
+ TITLE:=Voicemail support
+ DESCRIPTION+=\\\
+ \\\
+ This package contains voicemail related modules for Asterisk.
+endef
+
+define Package/asterisk-voicemail/conffiles
+/etc/asterisk/voicemail.conf
+endef
+
+define Package/asterisk-app-meetme
+ $(call Package/asterisk/Default)
+ DEPENDS:=asterisk +zaptel-libtonezone
+ TITLE:=MeetMe Confererencing support
+ DESCRIPTION+=\\\
+ \\\
+ This package provides the application MeetMe and conferencing support to \\\
+ Asterisk.
+endef
+
+define Package/asterisk-app-meetme/conffiles
+/etc/asterisk/meetme.conf
+endef
+
define Package/asterisk-chan-bluetooth
$(call Package/asterisk/Default)
DEPENDS:=asterisk +bluez-libs
Asterisk.
endef
-define Package/asterisk-mysql
- $(call Package/asterisk/Default)
- DEPENDS:=asterisk +libmysqlclient
- TITLE:=MySQL support
- DESCRIPTION+=\\\
- \\\
- This package contains MySQL support modules for Asterisk.
-endef
-
-define Package/asterisk-mysql/conffiles
-/etc/asterisk/cdr_mysql.conf
-endef
-
-define Package/asterisk-pgsql
- $(call Package/asterisk/Default)
- DEPENDS:=asterisk +libpq
- TITLE:=PostgreSQL support
- DESCRIPTION+=\\\
- \\\
- This package contains PostgreSQL support modules for Asterisk.
-endef
-
-define Package/asterisk-pgsql/conffiles
-/etc/asterisk/cdr_pgsql.conf
-endef
-
-define Package/asterisk-sqlite
- $(call Package/asterisk/Default)
- DEPENDS:=asterisk +libsqlite2
- TITLE:=SQLite modules
- DESCRIPTION+=\\\
- \\\
- This package contains SQLite support modules for Asterisk.
-endef
-
-define Package/asterisk-sounds
- $(call Package/asterisk)
- MENU:=0
- DEPENDS:=asterisk
- TITLE:=Sound files
- DESCRIPTION+=\\\
- \\\
- This package contains sound files for Asterisk.
-endef
-
-define Package/asterisk-voicemail
+define Package/asterisk-res-crypto
$(call Package/asterisk/Default)
- DEPENDS:=asterisk
- TITLE:=Voicemail support
+ DEPENDS:=asterisk +libopenssl
+ TITLE:=Cryptographic Digital Signatures support
DESCRIPTION+=\\\
- \\\
- This package contains voicemail related modules for Asterisk.
+ \\\
+ This package provides Cryptographic Digital Signatures support to \\\
+ Asterisk.
endef
-define Package/asterisk-voicemail/conffiles
-/etc/asterisk/voicemail.conf
-endef
ifneq ($(SDK),)
# Make sure the options below are enabled when building with the SDK
CONFIG_PACKAGE_asterisk-mysql:=m
CONFIG_PACKAGE_asterisk-pgsql:=m
CONFIG_PACKAGE_asterisk-sqlite:=m
+ CONFIG_PACKAGE_asterisk-app-meetme:=m
CONFIG_PACKAGE_asterisk-chan-bluetooth:=m
CONFIG_PACKAGE_asterisk-chan-h323:=m
CONFIG_PACKAGE_asterisk-codec-speex:=m
+ CONFIG_PACKAGE_asterisk-res-crypto:=m
endif
EXTRA_CFLAGS:= -I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/include
ifneq ($(CONFIG_PACKAGE_asterisk-sqlite),)
EXTRA_CDR_MODULES+= cdr_sqlite.so
endif
+ifneq ($(CONFIG_PACKAGE_asterisk-app-meetme),)
+ EXTRA_APP_MODULES+= app_meetme.so
+endif
ifneq ($(CONFIG_PACKAGE_asterisk-chan-bluetooth),)
EXTRA_CHAN_MODULES+= chan_bluetooth.so
endif
EXTRA_CFLAGS+= -I$(STAGING_DIR)/usr/include/speex
EXTRA_CODEC_MODULES+= codec_speex.so
endif
+ifneq ($(CONFIG_PACKAGE_asterisk-res-crypto),)
+ EXTRA_RES_MODULES+= res_crypto.so
+endif
define Build/Configure
endef
rm -f codec_lpc10.so ; \
rm -f pbx_dundi.so ; \
rm -f res_agi.so ; \
+ rm -f res_crypto.so ; \
)
(cd $(1)/etc/asterisk; \
rm -f *odbc* *mysql* *postgres* *pgsql* *voicemail* *adsi* *oss* *alsa* \
$(CP) $(PKG_BUILD_DIR)/sounds/vm-*.gsm $(1)/usr/lib/asterisk/sounds/
endef
+define Package/asterisk-app-meetme/install
+ $(INSTALL_DIR) $(1)/etc/asterisk
+ $(INSTALL_DATA) $(PKG_BUILD_DIR)/configs/meetme.conf.sample $(1)/etc/asterisk/meetme.conf
+ $(INSTALL_DIR) $(1)/usr/lib/asterisk/modules
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/apps/app_meetme.so $(1)/usr/lib/asterisk/modules/
+endef
+
define Package/asterisk-chan-bluetooth/install
$(INSTALL_DIR) $(1)/etc/asterisk
$(INSTALL_DATA) $(PKG_BUILD_DIR)/configs/bluetooth.conf $(1)/etc/asterisk/bluetooth.conf
$(INSTALL_BIN) $(PKG_BUILD_DIR)/res/res_agi.so $(1)/usr/lib/asterisk/modules/
endef
+define Package/asterisk-res-crypto/install
+ $(INSTALL_DIR) $(1)/usr/lib/asterisk/modules
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/res/res_crypto.so $(1)/usr/lib/asterisk/modules/
+endef
+
$(eval $(call BuildPackage,asterisk))
-$(eval $(call BuildPackage,asterisk-mini))
$(eval $(call BuildPackage,asterisk-mysql))
$(eval $(call BuildPackage,asterisk-pgsql))
$(eval $(call BuildPackage,asterisk-sqlite))
-$(eval $(call BuildPackage,asterisk-voicemail))
$(eval $(call BuildPackage,asterisk-sounds))
+$(eval $(call BuildPackage,asterisk-voicemail))
+$(eval $(call BuildPackage,asterisk-app-meetme))
$(eval $(call BuildPackage,asterisk-chan-bluetooth))
$(eval $(call BuildPackage,asterisk-chan-h323))
$(eval $(call BuildPackage,asterisk-chan-mgcp))
$(eval $(call BuildPackage,asterisk-codec-speex))
$(eval $(call BuildPackage,asterisk-pbx-dundi))
$(eval $(call BuildPackage,asterisk-res-agi))
+$(eval $(call BuildPackage,asterisk-res-crypto))
+$(eval $(call BuildPackage,asterisk-mini))
--- /dev/null
+diff -Nru asterisk-1.2.14.org/channels/chan_iax2.c asterisk-1.2.14/channels/chan_iax2.c
+--- asterisk-1.2.14.org/channels/chan_iax2.c 2006-12-09 16:45:37.000000000 +0100
++++ asterisk-1.2.14/channels/chan_iax2.c 2006-12-27 08:46:38.000000000 +0100
+@@ -1191,7 +1191,7 @@
+ last++;
+ else
+ last = s;
+- snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)rand());
++ snprintf(s2, strlen(s) + 100, "/tmp/%s-%ld", last, (unsigned long)rand());
+ res = stat(s, &stbuf);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
--- /dev/null
+diff -Nru asterisk-1.2.14.org/dns.c asterisk-1.2.14/dns.c
+--- asterisk-1.2.14.org/dns.c 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/dns.c 2006-12-27 08:50:05.000000000 +0100
+@@ -175,7 +175,13 @@
+
+ #if defined(res_ninit)
+ #define HAS_RES_NINIT
+-#else
++#endif
++
++#ifdef __UCLIBC__
++#undef HAS_RES_NINIT
++#endif
++
++#ifndef HAS_RES_NINIT
+ AST_MUTEX_DEFINE_STATIC(res_lock);
+ #if 0
+ #warning "Warning, res_ninit is missing... Could have reentrancy issues"
--- /dev/null
+diff -Nru asterisk-1.2.14.org/db1-ast/Makefile asterisk-1.2.14/db1-ast/Makefile
+--- asterisk-1.2.14.org/db1-ast/Makefile 2006-04-30 16:27:56.000000000 +0200
++++ asterisk-1.2.14/db1-ast/Makefile 2006-12-27 08:52:14.000000000 +0100
+@@ -32,8 +32,8 @@
+
+ $(LIBDB): $(OBJS)
+ rm -f $@
+- ar cq $@ $(OBJS)
+- ranlib $@
++ $(AR) cq $@ $(OBJS)
++ $(RANLIB) $@
+
+ $(LIBDBSO): $(SHOBJS)
+ $(CC) -Wl,-O1 -Wl,--version-script=libdb.map -Wl,-soname=$(LIBDBSO) -shared -o $@ $^
--- /dev/null
+diff -Nru asterisk-1.2.14.org/codecs/lpc10/Makefile asterisk-1.2.14/codecs/lpc10/Makefile
+--- asterisk-1.2.14.org/codecs/lpc10/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/codecs/lpc10/Makefile 2006-12-27 08:54:16.000000000 +0100
+@@ -34,6 +34,7 @@
+ ifneq ($(PROC),ppc)
+ ifneq ($(PROC),x86_64)
+ ifneq ($(PROC),alpha)
++ifneq ($(PROC),mipsel)
+ #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only.
+ #This works for even old (2.96) versions of gcc and provides a small boost either way.
+ #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn.t support it.
+@@ -53,6 +54,7 @@
+ endif
+ endif
+ endif
++endif
+
+ LIB = $(LIB_TARGET_DIR)/liblpc10.a
+
+@@ -69,7 +71,7 @@
+
+ $(LIB): $(OBJ)
+ $(AR) cr $@ $(OBJ)
+- ranlib $@
++ $(RANLIB) $@
+
+ clean:
+ -rm -f *.o $(LIB)
--- /dev/null
+diff -Nru asterisk-1.2.14.org/stdtime/Makefile asterisk-1.2.14/stdtime/Makefile
+--- asterisk-1.2.14.org/stdtime/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/stdtime/Makefile 2006-12-27 08:56:14.000000000 +0100
+@@ -3,8 +3,8 @@
+ all: libtime.a
+
+ libtime.a: $(OBJS)
+- ar rv $@ $(OBJS)
+- ranlib $@
++ $(AR) rv $@ $(OBJS)
++ $(RANLIB) $@
+
+ install:
+
--- /dev/null
+diff -Nru asterisk-1.2.14.org/channels/chan_bluetooth.c asterisk-1.2.14/channels/chan_bluetooth.c
+--- asterisk-1.2.14.org/channels/chan_bluetooth.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/channels/chan_bluetooth.c 2006-12-27 09:04:03.000000000 +0100
+@@ -0,0 +1,3599 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Asterisk Bluetooth Channel
++ *
++ * Author: Theo Zourzouvillys <theo@adaptive-it.co.uk>
++ *
++ * Adaptive Linux Solutions <http://www.adaptive-it.co.uk>
++ *
++ * Copyright (C) 2004 Adaptive Linux Solutions
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ *
++ * ******************* NOTE NOTE NOTE NOTE NOTE *********************
++ *
++ * This code is not at all tested, and only been developed with a
++ * HBH-200 headset and a Nokia 6310i right now.
++ *
++ * Expect it to crash, dial random numbers, and steal all your money.
++ *
++ * PLEASE try any headsets and phones, and let me know the results,
++ * working or not, along with all debug output!
++ *
++ * ------------------------------------------------------------------
++ *
++ * Asterisk Bluetooth Support
++ *
++ * Well, here we go - Attempt to provide Handsfree profile support in
++ * both AG and HF modes, AG (AudioGateway) mode support for using
++ * headsets, and HF (Handsfree) mode for utilising mobile/cell phones
++ *
++ * It would be nice to also provide Headset support at some time in
++ * the future, however, a working Handsfree profile is nice for now,
++ * and as far as I can see, almost all new HS devices also support HF
++ *
++ * ------------------------------------------------------------------
++ * INSTRUCTIONS
++ *
++ * You need to have bluez's bluetooth stack, along with user space
++ * tools (>=v2.10), and running hcid and sdsp.
++ *
++ * See bluetooth.conf for configuration details.
++ *
++ * - Ensure bluetooth subsystem is up and running. 'hciconfig'
++ * should show interface as UP.
++ *
++ * - If you're trying to use a headset/HS, start up asterisk, and try
++ * to pair it as you normally would.
++ *
++ * - If you're trying to use a Phone/AG, just make sure bluetooth is
++ * enabled on your phone, and start up asterisk.
++ *
++ * - 'bluetooth show peers' will show all bluetooth devices states.
++ *
++ * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS
++ * with Dial(BLT/DevName)
++ *
++ * ------------------------------------------------------------------
++ * BUGS
++ *
++ * - What should happen when an AG is paired with asterisk and
++ * someone uses the AG dalling a number manually? My test phone
++ * seems to try to open an SCO link. Perhaps an extension to
++ * route the call to, or maybe drop the RFCOM link all together?
++ *
++ * ------------------------------------------------------------------
++ * COMPATIBILITY
++ *
++ * PLEASE email <theo@adaptive-it.co.uk> with the results of ANY
++ * device not listed in here (working or not), or if the device is
++ * listed and it doesn't work! Please also email full debug output
++ * for any device not working correctly or generating errors in log.
++ *
++ * HandsFree Profile:
++ *
++ * HS (HeadSet):
++ * - Ericsson HBH-200
++ *
++ * AG (AudioGateway):
++ * - Nokia 6310i
++ *
++ * ------------------------------------------------------------------
++ *
++ * Questions, bugs, or (preferably) patches to:
++ *
++ * <theo@adaptive-it.co.uk>
++ *
++ * ------------------------------------------------------------------
++ */
++
++/* ---------------------------------- */
++
++#include <stdio.h>
++#include <string.h>
++#include <asterisk/lock.h>
++#include <asterisk/utils.h>
++#include <asterisk/channel.h>
++#include <asterisk/config.h>
++#include <asterisk/logger.h>
++#include <asterisk/module.h>
++#include <asterisk/pbx.h>
++#include <asterisk/sched.h>
++#include <asterisk/options.h>
++#include <asterisk/cli.h>
++#include <asterisk/callerid.h>
++#include <asterisk/version.h>
++#include <sys/socket.h>
++#include <sys/signal.h>
++#include <sys/time.h>
++#include <errno.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <arpa/inet.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <ctype.h>
++#include <endian.h>
++
++#include <bluetooth/bluetooth.h>
++#include <bluetooth/hci.h>
++#include <bluetooth/hci_lib.h>
++#include <bluetooth/sco.h>
++#include <bluetooth/rfcomm.h>
++#include <bluetooth/sdp.h>
++#include <bluetooth/sdp_lib.h>
++
++/* --- Data types and definitions --- */
++
++#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID
++# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f
++#endif
++#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR
++#define BLT_CHAN_NAME "BLT"
++#define BLT_CONFIG_FILE "bluetooth.conf"
++#define BLT_RDBUFF_MAX 1024
++#define BLT_DEFAULT_HCI_DEV 0
++#define BLT_SVN_REVISION "$Rev$"
++
++/* ---------------------------------- */
++
++typedef enum {
++ BLT_ROLE_NONE = 0, // Unknown Device
++ BLT_ROLE_HS = 1, // Device is a Headset
++ BLT_ROLE_AG = 2, // Device is an Audio Gateway
++ BLT_ROLE_GUI = 3 // Device is used as an GUI
++} blt_role_t;
++
++/* State when we're in HS mode */
++
++typedef enum {
++ BLT_STATE_WANT_R = 0,
++ BLT_STATE_WANT_N = 1,
++ BLT_STATE_WANT_CMD = 2,
++ BLT_STATE_WANT_N2 = 3,
++} blt_state_t;
++
++typedef enum {
++ BLT_STATUS_DOWN,
++ BLT_STATUS_CONNECTING,
++ BLT_STATUS_NEGOTIATING,
++ BLT_STATUS_READY,
++ BLT_STATUS_RINGING,
++ BLT_STATUS_IN_CALL,
++} blt_status_t;
++
++/* ---------------------------------- */
++
++/* Default config settings */
++
++#define BLT_DEFAULT_CHANNEL_AG 5
++#define BLT_DEFAULT_CHANNEL_HS 6
++#define BLT_DEFAULT_CHANNEL_GUI 1
++#define BLT_DEFAULT_ROLE BLT_ROLE_HS
++#define BLT_OBUF_LEN (48 * 25)
++
++#define BUFLEN (4800)
++
++/* ---------------------------------- */
++
++typedef struct blt_dev blt_dev_t;
++
++void ag_cgmi_response(blt_dev_t * dev, char * cmd);
++void ag_unknown_response(blt_dev_t * dev, char * cmd);
++void ag_cgmi_valid_response(blt_dev_t * dev, char * cmd);
++void ag_clip_response(blt_dev_t * dev, char * cmd);
++void ag_cmer_response(blt_dev_t * dev, char * cmd);
++void ag_cind_status_response(blt_dev_t * dev, char * cmd);
++void ag_cind_response(blt_dev_t * dev, char * cmd);
++void ag_brsf_response(blt_dev_t * dev, char * cmd);
++void remove_sdp_records(void);
++
++void gui_easm_response(blt_dev_t * dev, char * cmd);
++
++int sock_err(int fd);
++int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type);
++int set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len);
++int get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy);
++void gui_eaid_response(blt_dev_t * dev, char * cmd);
++
++
++
++struct blt_ring {
++ unsigned char buf[BUFLEN];
++};
++// XXX:T: Tidy this lot up.
++struct blt_dev {
++
++ blt_status_t status; /* Device Status */
++
++ struct ast_channel * owner; /* Channel we belong to, possibly NULL */
++ blt_dev_t * dev; /* The bluetooth device channel is for */
++ struct ast_frame fr; /* Recieved frame */
++
++ /* SCO Handler */
++ int sco_pipe[2]; /* SCO alert pipe */
++ int sco; /* SCO fd */
++ int sco_handle; /* SCO Handle */
++ int sco_mtu; /* SCO MTU */
++ int sco_running; /* 1 when sCO thread should be running */
++ pthread_t sco_thread; /* SCO thread */
++ ast_mutex_t sco_lock; /* SCO lock */
++ int sco_pos_in; /* Reader in position (drain)*/
++ int sco_pos_inrcv; /* Reader in position (fill) */
++ int wakeread; /* blt_read() needs to be woken */
++ int sco_pos_out; /* Reader out position */
++ int sco_sending; /* Sending SCO packets */
++ char buf[1200]; /* Incoming data buffer */
++ int bufpos;
++ char sco_buf_out[BUFLEN]; /* 24 chunks of 48 */
++ char sco_buf_in[BUFLEN]; /* 24 chunks of 48 */
++
++ char dnid[1024]; /* Outgoi gncall dialed number */
++ unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */
++ int obuf_len; /* Output Buffer Position */
++ int obuf_wpos; /* Buffer Reader */
++
++ // device
++ int autoconnect; /* 1 for autoconnect */
++ int outgoing_id; /* Outgoing connection scheduler id */
++ char * name; /* Devices friendly name */
++ blt_role_t role; /* Device role (HS or AG) */
++ bdaddr_t bdaddr; /* remote address */
++ int channel; /* remote channel */
++ int rd; /* RFCOMM fd */
++ int tmp_rd; /* RFCOMM fd */
++ int call_cnt; /* Number of attempted calls */
++ ast_mutex_t lock; /* RFCOMM socket lock */
++ char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */
++ int rd_buff_pos; /* RFCOMM input buffer position */
++ int ready; /* 1 When ready */
++ char *context;
++
++ /* AG mode */
++ char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */
++ int cind; /* Runtime[AG]: Recieved +CIND */
++ int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */
++ int call, service, callsetup; /* Runtime[AG]: Values */
++ char cid_num[AST_MAX_EXTENSION];
++ char cid_name[AST_MAX_EXTENSION];
++
++ /* HS mode */
++ blt_state_t state; /* Runtime: Device state (AG mode only) */
++ int ring_timer; /* Runtime:Ring Timer */
++ char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */
++ void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */
++
++ int brsf; /* Runtime: Bluetooth Retrieve Supported Features */
++ int bvra; /* Runtime: Bluetooth Voice Recognised Activation */
++ int gain_speaker; /* Runtime: Gain Of Speaker */
++ int clip; /* Runtime: Supports CLID */
++ int colp; /* Runtime: Connected Line ID */
++ int elip; /* Runtime: (Ericsson) Supports CLID */
++ int eolp; /* Runtime: (Ericsson) Connected Line ID */
++ int ringing; /* Runtime: Device is ringing */
++
++ blt_dev_t * next; /* Next in linked list */
++
++};
++
++typedef struct blt_atcb {
++
++ /* The command */
++ char * str;
++
++ /* DTE callbacks: */
++ int (*set)(blt_dev_t * dev, const char * arg, int len);
++ int (*read)(blt_dev_t * dev);
++ int (*execute)(blt_dev_t * dev, const char * data);
++ int (*test)(blt_dev_t * dev);
++
++ /* DCE callbacks: */
++ int (*unsolicited)(blt_dev_t * dev, const char * value);
++
++} blt_atcb_t;
++
++/* ---------------------------------- */
++
++static void rd_close(blt_dev_t * dev, int reconnect, int err);
++static int send_atcmd(blt_dev_t * device, const char * fmt, ...);
++static int sco_connect(blt_dev_t * dev);
++static int sco_start(blt_dev_t * dev, int fd);
++
++/* ---------------------------------- */
++
++/* RFCOMM channel we listen on*/
++static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG;
++static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS;
++static int rfcomm_channel_gui = BLT_DEFAULT_CHANNEL_GUI;
++
++static char* gui_default_sip_number = "";
++static char* gui_default_sip_address = "";
++
++/* Address of local bluetooth interface */
++static int hcidev_id;
++static bdaddr_t local_bdaddr;
++
++/* All the current sockets */
++AST_MUTEX_DEFINE_STATIC(iface_lock);
++static blt_dev_t * iface_head;
++static int ifcount = 0;
++
++static int sdp_record_hs = -1;
++static int sdp_record_ag = -1;
++static int sdp_record_gui = -1;
++
++/* RFCOMM listen socket */
++static int rfcomm_sock_ag = -1;
++static int rfcomm_sock_hs = -1;
++static int rfcomm_sock_gui = -1;
++
++static int sco_socket = -1;
++
++static int monitor_pid = -1;
++
++/* The socket monitoring thread */
++static pthread_t monitor_thread = AST_PTHREADT_NULL;
++AST_MUTEX_DEFINE_STATIC(monitor_lock);
++
++/* Count how many times this module is currently in use */
++static int usecnt = 0;
++AST_MUTEX_DEFINE_STATIC(usecnt_lock);
++
++static struct sched_context * sched = NULL;
++
++/* ---------------------------------- */
++
++#if ASTERISK_VERSION_NUM <= 010107
++#include <asterisk/channel_pvt.h>
++#define tech_pvt pvt->pvt
++#else /* CVS. FIXME: Version number */
++static struct ast_channel *blt_request(const char *type, int format, void *data, int *cause);
++static int blt_hangup(struct ast_channel *c);
++static int blt_answer(struct ast_channel *c);
++static struct ast_frame *blt_read(struct ast_channel *chan);
++static int blt_call(struct ast_channel *c, char *dest, int timeout);
++static int blt_write(struct ast_channel *chan, struct ast_frame *f);
++static int blt_indicate(struct ast_channel *chan, int cond);
++
++static const struct ast_channel_tech blt_tech = {
++ .type = BLT_CHAN_NAME,
++ .description = "Bluetooth Channel Driver",
++ .capabilities = BLUETOOTH_FORMAT,
++ .requester = blt_request,
++ .hangup = blt_hangup,
++ .answer = blt_answer,
++ .read = blt_read,
++ .call = blt_call,
++ .write = blt_write,
++ .indicate = blt_indicate,
++};
++#endif
++/* ---------------------------------- */
++
++static const char *
++role2str(blt_role_t role)
++{
++ switch (role) {
++ case BLT_ROLE_HS:
++ return "HS";
++ case BLT_ROLE_AG:
++ return "AG";
++ case BLT_ROLE_GUI:
++ return "GUI";
++ case BLT_ROLE_NONE:
++ default:
++ return "??";
++ }
++}
++
++static const char *
++status2str(blt_status_t status)
++{
++ switch (status) {
++ case BLT_STATUS_DOWN:
++ return "Down";
++ case BLT_STATUS_CONNECTING:
++ return "Connecting";
++ case BLT_STATUS_NEGOTIATING:
++ return "Negotiating";
++ case BLT_STATUS_READY:
++ return "Ready";
++ case BLT_STATUS_RINGING:
++ return "Ringing";
++ case BLT_STATUS_IN_CALL:
++ return "InCall";
++ };
++ return "Unknown";
++}
++
++int sock_err(int fd)
++{
++ int ret;
++ int len = sizeof(ret);
++ getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
++ return ret;
++}
++
++/* ---------------------------------- */
++int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type)
++{
++ const char *c = str;
++ const char *start;
++ int length;
++ char typestr[256];
++
++ memset(number, 0, number_len);
++ memset(name, 0, name_len);
++ *type = 0;
++
++ number[0] = '\0';
++ name[0] = '\0';
++ while(*c && *c != '"')
++ c++;
++ c++;
++ start = c;
++ while(*c && *c != '"')
++ c++;
++ length = c - start < number_len ? c - start : number_len;
++ strncpy(number, start, length);
++ number[length] = '\0';
++ c++;
++ while(*c && *c != ',')
++ c++;
++ c++;
++ start = c;
++ while(*c && *c != ',')
++ c++;
++ length = c - start < number_len ? c - start : number_len;
++ strncpy(typestr, start, length);
++ typestr[length] = '\0';
++ *type = atoi(typestr);
++ c++;
++ while(*c && *c != ',')
++ c++;
++ c++;
++ while(*c && *c != ',')
++ c++;
++ c++;
++ while(*c && *c != '"')
++ c++;
++ c++;
++ start = c;
++ while(*c && *c != '"')
++ c++;
++ length = c - start < number_len ? c - start : number_len;
++ strncpy(name, start, length);
++ name[length] = '\0';
++
++ return(1);
++}
++
++
++static const char *
++parse_cind(const char * str, char * name, int name_len)
++{
++ int c = 0;
++
++ memset(name, 0, name_len);
++
++ while (*str) {
++ if (*str == '(') {
++ if (++c == 1 && *(str+1) == '"') {
++ const char * start = str + 2;
++ int len = 0;
++ str += 2;
++ while (*str && *str != '"') {
++ len++;
++ str++;
++ }
++ if (len == 0)
++ return NULL;
++ strncpy(name, start, (len > name_len) ? name_len : len);
++ }
++ } else if (*str == ')')
++ c--;
++ else if (c == 0 && *str == ',')
++ return str + 1;
++ str++;
++ }
++ return NULL;
++}
++
++static void
++set_cind(blt_dev_t * dev, int indicator, int val)
++{
++
++ ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val);
++
++ if (indicator == dev->callsetup_pos) {
++
++ // call progress
++
++ dev->callsetup = val;
++
++ switch (val) {
++ case 3:
++ // Outgoing ringing
++ if ((dev->owner && dev->role == BLT_ROLE_AG) ||
++ (dev->owner && dev->role == BLT_ROLE_GUI))
++ ast_queue_control(dev->owner, AST_CONTROL_RINGING);
++ break;
++ case 2:
++ break;
++ case 1:
++ break;
++ case 0:
++ if ((dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0) ||
++ (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0))
++ ast_queue_control(dev->owner, AST_CONTROL_CONGESTION);
++ break;
++ }
++
++ } else if (indicator == dev->service_pos) {
++
++ // Signal
++
++ if (val == 0)
++ ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name);
++ else if (dev->service == 0 && val > 0)
++ ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name);
++
++ dev->service = val;
++
++ } else if (indicator == dev->call_pos) {
++
++ // Call
++
++ dev->call = val;
++
++ if (dev->owner) {
++ if (val == 1) {
++ sco_start(dev, -1);
++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ } else if (val == 0)
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ }
++
++ }
++
++
++}
++
++/* ---------------------------------- */
++
++int
++set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len)
++{
++ int start_pos = *(pos);
++ int done = 0;
++ int copy;
++
++ while (data_len) {
++ // Set can_do to the most we can do in this copy.
++
++ copy = MIN(circular_len - start_pos, data_len);
++ memcpy(ring + start_pos, data + done, copy);
++ done += copy;
++ start_pos += copy;
++ data_len -= copy;
++
++ if (start_pos == circular_len) {
++ start_pos = 0;
++ }
++ }
++ *(pos) = start_pos;
++ return 0;
++}
++
++int
++get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy)
++{
++ int copy;
++
++ // |1|2|3|4|5|6|7|8|9|
++ // |-----|
++
++ while (to_copy) {
++
++ // Set can_do to the most we can do in this copy.
++ copy = MIN(ring_size - *head, to_copy);
++
++ // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head);
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ memcpy(dst, ring + *head, copy);
++#else
++ // memcpy(dst, ring + *head, copy);
++ ast_swapcopy_samples(dst, ring+*head, copy/2);
++#endif
++ memset(ring+*head, 0, copy);
++ dst += copy;
++ *head += copy;
++ to_copy -= copy;
++
++ if (*head == ring_size ) {
++ *head = 0;
++ }
++
++ }
++
++ return 0;
++}
++
++/* Handle SCO audio sync.
++ *
++ * If we are the MASTER, then we control the timing,
++ * in 48 byte chunks. If we're the SLAVE, we send
++ * as and when we recieve a packet.
++ *
++ * Because of packet/timing nessecity, we
++ * start up a thread when we're passing audio, so
++ * that things are timed exactly right.
++ *
++ * sco_thread() is the function that handles it.
++ *
++ */
++
++static void *
++sco_thread(void * data)
++{
++ blt_dev_t * dev = (blt_dev_t*)data;
++ int res;
++ struct pollfd pfd[2];
++ int in_pos = 0;
++ int out_pos = 0;
++ char c = 1;
++ int sending;
++ char buf[1024];
++ int len;
++
++ // Avoid deadlock in odd circumstances
++
++ ast_log(LOG_WARNING, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid());
++
++ if (fcntl(dev->sco_pipe[1], F_SETFL, O_RDWR|O_NONBLOCK)) {
++ ast_log(LOG_WARNING, "fcntl failed on sco_pipe\n");
++ }
++
++ // dev->status = BLT_STATUS_IN_CALL;
++ // ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ // Set buffer to silence, just incase.
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ memset(dev->sco_buf_in, 0, BUFLEN);
++ memset(dev->sco_buf_out, 0, BUFLEN);
++
++ dev->sco_pos_in = 0;
++ dev->sco_pos_out = 0;
++ dev->sco_pos_inrcv = 0;
++ dev->wakeread = 1;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ while (1) {
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ if (dev->sco_running != 1) {
++ ast_log(LOG_DEBUG, "SCO stopped.\n");
++ break;
++ }
++
++ pfd[0].fd = dev->sco;
++ pfd[0].events = POLLIN;
++
++ pfd[1].fd = dev->sco_pipe[1];
++ pfd[1].events = POLLIN;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ res = poll(pfd, 2, 50);
++
++ if (res == -1 && errno != EINTR) {
++ ast_log(LOG_DEBUG, "SCO poll() error\n");
++ break;
++ }
++
++ if (res == 0)
++ continue;
++
++
++ if (pfd[0].revents & POLLIN) {
++
++ len = read(dev->sco, buf, 48);
++
++ if (len) {
++ ast_mutex_lock(&(dev->lock));
++
++ if (dev->owner && dev->owner->_state == AST_STATE_UP) {
++ ast_mutex_lock(&(dev->sco_lock));
++ set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len);
++ dev->sco_pos_inrcv = in_pos;
++
++ get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len);
++ if (write(dev->sco, buf, len) != len)
++ ast_log(LOG_WARNING, "Wrote <48 to sco\n");
++
++ if (dev->wakeread) {
++ /* blt_read has caught up. Kick it */
++ dev->wakeread = 0;
++ if(write(dev->sco_pipe[1], &c, 1) != 1)
++ ast_log(LOG_WARNING, "write to kick sco_pipe failed\n");
++ }
++ ast_mutex_unlock(&(dev->sco_lock));
++ }
++ ast_mutex_unlock(&(dev->lock));
++ }
++
++ } else if (pfd[0].revents) {
++
++ int e = sock_err(pfd[0].fd);
++ ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e);
++ break;
++
++ } else if (pfd[1].revents & POLLIN) {
++
++ int len;
++
++ len = read(pfd[1].fd, &c, 1);
++ sending = (sending) ? 0 : 1;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ } else if (pfd[1].revents) {
++
++ int e = sock_err(pfd[1].fd);
++ ast_log(LOG_ERROR, "SCO pipe connection event %d on pipe[1]=%d: %s (errno %d)\n", pfd[1].revents, pfd[1].fd, strerror(e), e);
++ break;
++
++ } else {
++ ast_log(LOG_NOTICE, "Unhandled poll output\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ }
++
++ }
++
++ ast_mutex_lock(&(dev->lock));
++ close(dev->sco);
++ dev->sco = -1;
++ dev->sco_running = -1;
++
++ memset(dev->sco_buf_in, 0, BUFLEN);
++ memset(dev->sco_buf_out, 0, BUFLEN);
++
++ dev->sco_pos_in = 0;
++ dev->sco_pos_out = 0;
++ dev->sco_pos_inrcv = 0;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++ if (dev->owner)
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ ast_mutex_unlock(&(dev->lock));
++ ast_log(LOG_DEBUG, "SCO thread stopped\n");
++ return NULL;
++}
++
++/* Start SCO thread. Must be called with dev->lock */
++
++static int
++sco_start(blt_dev_t * dev, int fd)
++{
++
++ if (dev->sco_pipe[1] <= 0) {
++ ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]);
++ return -1;
++ }
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ if (dev->sco_running != -1) {
++ ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++
++ if (dev->sco == -1) {
++ if (fd > 0) {
++ dev->sco = fd;
++ } else if (sco_connect(dev) != 0) {
++ ast_log(LOG_ERROR, "SCO fd invalid\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++ }
++
++ dev->sco_running = 1;
++
++ if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) {
++ ast_log(LOG_ERROR, "Unable to start SCO thread.\n");
++ dev->sco_running = -1;
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ return 0;
++}
++
++/* Stop SCO thread. Must be called with dev->lock */
++
++static int
++sco_stop(blt_dev_t * dev)
++{
++ ast_mutex_lock(&(dev->sco_lock));
++ if (dev->sco_running == 1)
++ dev->sco_running = 0;
++ else
++ dev->sco_running = -1;
++ dev->sco_sending = 0;
++ ast_mutex_unlock(&(dev->sco_lock));
++ return 0;
++}
++
++/* ---------------------------------- */
++
++/* Answer the call. Call with lock held on device */
++
++static int
++answer(blt_dev_t * dev)
++{
++
++ if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) {
++ ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n",
++ dev->owner, dev->ready, status2str(dev->status));
++ return -1;
++ }
++
++ // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0);
++ // dev->status = BLT_STATUS_IN_CALL;
++ // dev->owner->fds[0] = dev->sd;
++ // if we are answering (hitting button):
++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ // if asterisk signals us to answer:
++ // ast_setstate(ast, AST_STATE_UP);
++
++ /* Start SCO link */
++ sco_start(dev, -1);
++ return 0;
++}
++
++/* ---------------------------------- */
++
++static int
++blt_write(struct ast_channel * ast, struct ast_frame * frame)
++{
++ blt_dev_t * dev = ast->tech_pvt;
++
++ /* Write a frame of (presumably voice) data */
++
++ if (frame->frametype != AST_FRAME_VOICE) {
++ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
++ return 0;
++ }
++
++ if (!(frame->subclass & BLUETOOTH_FORMAT)) {
++ static int fish = 5;
++ if (fish) {
++ ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass);
++ fish--;
++ }
++ return 0;
++ }
++
++ if (ast->_state != AST_STATE_UP) {
++ return 0;
++ }
++
++ ast_mutex_lock(&(dev->sco_lock));
++ set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN));
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ return 0;
++
++}
++
++static struct ast_frame *
++blt_read(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->tech_pvt;
++ char c = 1;
++ int len;
++ static int fish = 0;
++ /* Some nice norms */
++
++ dev->fr.datalen = 0;
++ dev->fr.samples = 0;
++ dev->fr.data = NULL;
++ dev->fr.src = BLT_CHAN_NAME;
++ dev->fr.offset = 0;
++ dev->fr.mallocd = AST_MALLOCD_DATA;
++ dev->fr.delivery.tv_sec = 0;
++ dev->fr.delivery.tv_usec = 0;
++ read(dev->sco_pipe[0], &c, 1);
++ ast_mutex_lock(&(dev->sco_lock));
++ dev->sco_sending = 1;
++
++ if (dev->sco_pos_inrcv < dev->sco_pos_in) {
++ /* Buffer wrapped. Read only till the end */
++ len = BUFLEN - dev->sco_pos_in + dev->sco_pos_inrcv;
++ } else {
++ len = dev->sco_pos_inrcv - dev->sco_pos_in;
++ }
++ dev->fr.data = malloc(AST_FRIENDLY_OFFSET+len) + AST_FRIENDLY_OFFSET;
++
++ get_buffer(dev->fr.data, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), len);
++ dev->wakeread = 1;
++ ast_mutex_unlock(&(dev->sco_lock));
++ if (fish) {
++ unsigned char *x = dev->fr.data;
++ ast_log(LOG_WARNING, "blt_read %d: %02x %02x %02x %02x %02x %02x\n",
++ dev->fr.datalen, x[0], x[1], x[2], x[3], x[4], x[5]);
++ fish--;
++ }
++
++ dev->fr.samples = len / 2;
++ dev->fr.datalen = len;
++ dev->fr.frametype = AST_FRAME_VOICE;
++ dev->fr.subclass = BLUETOOTH_FORMAT;
++ dev->fr.offset = AST_FRIENDLY_OFFSET;
++ return &dev->fr;
++}
++
++/* Escape Any '"' in str. Return malloc()ed string */
++static char *
++escape_str(char * str)
++{
++ char * ptr = str;
++ char * pret;
++ char * ret;
++ int len = 0;
++
++ while (*ptr) {
++ if (*ptr == '"')
++ len++;
++ len++;
++ ptr++;
++ }
++
++ ret = malloc(len + 1);
++ pret = memset(ret, 0, len + 1);
++
++ ptr = str;
++
++ while (*ptr) {
++ if (*ptr == '"')
++ *pret++ = '\\';
++ *pret++ = *ptr++;
++ }
++
++ return ret;
++}
++
++static int
++ring_hs(blt_dev_t * dev)
++{
++#if (ASTERISK_VERSION_NUM < 010100)
++ char tmp[AST_MAX_EXTENSION];
++ char *name, *num;
++#endif
++
++ ast_mutex_lock(&(dev->lock));
++
++ if (dev->owner == NULL) {
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ dev->ringing = 1;
++ dev->status = BLT_STATUS_RINGING;
++
++ send_atcmd(dev, "RING");
++
++ dev->owner->rings++;
++
++ // XXX:T: '"' needs to be escaped in ELIP.
++
++#if (ASTERISK_VERSION_NUM < 010100)
++
++ if (dev->owner->callerid) {
++
++ memset(tmp, 0, sizeof(tmp));
++ strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1);
++
++ if (!ast_callerid_parse(tmp, &name, &num)) {
++
++ if (dev->clip && num)
++ send_atcmd(dev, "+CLIP: \"%s\",129", num);
++
++ if (dev->elip && name) {
++ char * esc = escape_str(name);
++ send_atcmd(dev, "*ELIP: \"%s\"", esc);
++ free(esc);
++ }
++ }
++ }
++
++
++#else
++
++ if (dev->clip && dev->owner->cid.cid_num)
++ send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num);
++
++ if (dev->elip && dev->owner->cid.cid_name) {
++ char * esc = escape_str(dev->owner->cid.cid_name);
++ send_atcmd(dev, "*ELIP: \"%s\"", esc);
++ free(esc);
++ }
++
++#endif
++
++ ast_mutex_unlock(&(dev->lock));
++
++ return 1;
++}
++
++/*
++ * If the HS is already connected, then just send RING, otherwise, things get a
++ * little more sticky. We first have to find the channel for HS using SDP,
++ * then initiate the connection. Once we've done that, we can start the call.
++ */
++
++static int
++blt_call(struct ast_channel * ast, char * dest, int timeout)
++{
++ blt_dev_t * dev = ast->tech_pvt;
++
++ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
++ ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name);
++ return -1;
++ }
++
++ ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout);
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return -1;
++ }
++
++// ast_mutex_lock(&(dev->lock));
++
++ if (dev->ready == 0) {
++ ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n");
++ ast_setstate(ast, AST_CONTROL_CONGESTION);
++// ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++ return 0;
++ }
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ send_atcmd(dev, "+CIEV: 3,1");
++
++ dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev);
++
++ ring_hs(dev);
++
++ ast_setstate(ast, AST_STATE_RINGING);
++ ast_queue_control(ast, AST_CONTROL_RINGING);
++
++ } else if (dev->role == BLT_ROLE_AG) {
++
++ send_atcmd(dev, "ATD%s;", dev->dnid);
++// it does not seem like we should start the audio until the call is connected
++// sco_start(dev, -1);
++ } else if (dev->role == BLT_ROLE_GUI) {
++
++ send_atcmd(dev, "ATD%s;", dev->dnid);
++
++ } else {
++
++ ast_setstate(ast, AST_CONTROL_CONGESTION);
++ ast_log(LOG_ERROR, "Unknown device role\n");
++
++ }
++
++// ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++
++ return 0;
++}
++
++static int
++blt_hangup(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->tech_pvt;
++
++ ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name);
++
++ if (!ast->tech_pvt) {
++ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
++ return 0;
++ }
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock\n");
++ return 0;
++ }
++
++ ast_mutex_lock(&(dev->lock));
++
++ sco_stop(dev);
++ dev->sco_sending = 0;
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ if (dev->ringing == 0) {
++ // Actual call in progress
++ send_atcmd(dev, "+CIEV: 2,0");
++ } else {
++
++ // Just ringing still
++
++ if (dev->role == BLT_ROLE_HS)
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ if (dev->ring_timer >= 0)
++ ast_sched_del(sched, dev->ring_timer);
++
++ dev->ring_timer = -1;
++ dev->ringing = 0;
++
++ }
++
++ } else if (dev->role == BLT_ROLE_AG) {
++
++ // Cancel call.
++ send_atcmd(dev, "ATH");
++ send_atcmd(dev, "AT+CHUP");
++
++ }
++
++ if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING)
++ dev->status = BLT_STATUS_READY;
++
++ ast->tech_pvt = NULL;
++ dev->owner = NULL;
++ ast_mutex_unlock(&(dev->lock));
++ ast_setstate(ast, AST_STATE_DOWN);
++ ast_mutex_unlock(&(iface_lock));
++
++ return 0;
++}
++
++static int
++blt_indicate(struct ast_channel * c, int condition)
++{
++ ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition);
++
++ switch(condition) {
++ case AST_CONTROL_RINGING:
++ return -1;
++ default:
++ ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition);
++ break;
++ }
++ return -1;
++}
++
++static int
++blt_answer(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->tech_pvt;
++
++ ast_mutex_lock(&dev->lock);
++
++ // if (dev->ring_timer >= 0)
++ // ast_sched_del(sched, dev->ring_timer);
++ // dev->ring_timer = -1;
++
++ ast_log(LOG_DEBUG, "Answering interface\n");
++
++ if (ast->_state != AST_STATE_UP) {
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++ sco_start(dev, -1);
++ ast_setstate(ast, AST_STATE_UP);
++ }
++
++ ast_mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static struct ast_channel *
++blt_new(blt_dev_t * dev, int state, const char * context, const char * number)
++{
++ struct ast_channel * ast;
++ char c = 0;
++
++ if ((ast = ast_channel_alloc(1)) == NULL) {
++ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
++ return NULL;
++ }
++
++ snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name);
++
++ // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
++
++ ast->nativeformats = BLUETOOTH_FORMAT;
++ //ast->rawreadformat = BLUETOOTH_FORMAT;
++ //ast->rawwriteformat = BLUETOOTH_FORMAT;
++ ast->writeformat = BLUETOOTH_FORMAT;
++ ast->readformat = BLUETOOTH_FORMAT;
++
++ ast_setstate(ast, state);
++
++ ast->type = BLT_CHAN_NAME;
++
++ ast->tech_pvt = dev;
++#if ASTERISK_VERSION_NUM > 010107
++ ast->tech = &blt_tech;
++#else
++ ast->pvt->call = blt_call;
++ ast->pvt->indicate = blt_indicate;
++ ast->pvt->hangup = blt_hangup;
++ ast->pvt->read = blt_read;
++ ast->pvt->write = blt_write;
++ ast->pvt->answer = blt_answer;
++#endif
++ strncpy(ast->context, context, sizeof(ast->context)-1);
++ strncpy(ast->exten, number, sizeof(ast->exten) - 1);
++ if(0 == strcmp(number, "s"))
++ {
++ //ast_set_callerid(ast, dev->cid_num, dev->cid_name, dev->cid_num);
++ }
++
++ ast->language[0] = '\0';
++
++ ast->fds[0] = dev->sco_pipe[0];
++ write(dev->sco_pipe[1], &c, 1);
++
++ dev->owner = ast;
++
++ ast_mutex_lock(&usecnt_lock);
++ usecnt++;
++ ast_mutex_unlock(&usecnt_lock);
++
++ ast_update_use_count();
++
++ if (state != AST_STATE_DOWN) {
++ if (ast_pbx_start(ast)) {
++ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name);
++ ast_hangup(ast);
++ }
++ }
++
++ return ast;
++}
++
++static struct ast_channel *
++#if (ASTERISK_VERSION_NUM < 010100)
++blt_request(char * type, int format, void * local_data)
++#elif (ASTERISK_VERSION_NUM <= 010107)
++blt_request(const char * type, int format, void * local_data)
++#else
++blt_request(const char * type, int format, void * local_data, int *cause)
++#endif
++{
++ char * data = (char*)local_data;
++ int oldformat;
++ blt_dev_t * dev = NULL;
++ struct ast_channel * ast = NULL;
++ char * number = data, * dname;
++
++ dname = strsep(&number, "/");
++
++ oldformat = format;
++
++ format &= BLUETOOTH_FORMAT;
++
++ if (!format) {
++ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
++ return NULL;
++ }
++
++ ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname);
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Unable to lock iface_list\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++
++ while (dev) {
++ if (strcmp(dev->name, dname) == 0) {
++ ast_mutex_lock(&(dev->lock));
++ if (!dev->ready) {
++ ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name);
++ ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++ return NULL;
++ }
++ break;
++ }
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ if (!dev) {
++ ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname);
++ return NULL;
++ }
++
++ if (number && dev->role != BLT_ROLE_AG) {
++ ast_log(LOG_WARNING, "Tried to send a call out on non AG\n");
++ ast_mutex_unlock(&(dev->lock));
++ return NULL;
++ }
++
++ if (dev->role == BLT_ROLE_AG)
++ strncpy(dev->dnid, number, sizeof(dev->dnid) - 1);
++
++ ast = blt_new(dev, AST_STATE_DOWN, dev->context, "s");
++
++ ast_mutex_unlock(&(dev->lock));
++
++ return ast;
++}
++
++/* ---------------------------------- */
++
++
++/* ---- AT COMMAND SOCKET STUFF ---- */
++
++static int
++send_atcmd(blt_dev_t * dev, const char * fmt, ...)
++{
++ char buf[1024];
++ va_list ap;
++ int len;
++
++ va_start(ap, fmt);
++ len = vsnprintf(buf, 1023, fmt, ap);
++ va_end(ap);
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf);
++
++ write(dev->rd, "\r\n", 2);
++ len = write(dev->rd, buf, len);
++ write(dev->rd, "\r\n", 2);
++ return (len) ? 0 : -1;
++}
++
++
++static int
++send_atcmd_ok(blt_dev_t * dev, const char * cmd)
++{
++ int len;
++ strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1);
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name);
++ len = write(dev->rd, "\r\nOK\r\n", 6);
++ return (len) ? 0 : -1;
++}
++
++static int
++send_atcmd_error(blt_dev_t * dev)
++{
++ int len;
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name);
++
++// write(dev->rd, "\r\n", 2);
++// len = write(dev->rd, dev->last_ok_cmd, 5);
++ write(dev->rd, "\r\n", 2);
++ len = write(dev->rd, "ERROR", 5);
++ write(dev->rd, "\r\n", 2);
++
++ return (len) ? 0 : -1;
++}
++
++
++/* ---------------------------------- */
++
++/* -- Handle negotiation when we're an AG -- */
++
++/* Bluetooth Support */
++
++static int
++atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_DEBUG, "Device Supports: %s\n", arg);
++ dev->brsf = atoi(arg);
++ send_atcmd(dev, "+BRSF: %d", 23);
++ return 0;
++}
++
++/* Bluetooth Voice Recognition */
++
++static int
++atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n");
++ return -1;
++#if 0
++ // XXX:T: Fix voice recognition somehow!
++ int action = atoi(arg);
++ ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED");
++ if ((action == 0) & (dev->bvra == 1)) {
++ /* Disable it */
++ dev->bvra = 0;
++ // XXX:T: Shutdown any active bvra channel
++ ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n");
++ } else if ((action == 1) && (dev->bvra == 0)) {
++ /* Enable it */
++ dev->bvra = 1;
++ // XXX:T: Schedule connection to voice recognition extension/application
++ ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n");
++ } else {
++ ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action);
++ return -1;
++ }
++ return 0;
++#endif
++}
++
++/* Clock */
++
++static int
++atcmd_cclk_read(blt_dev_t * dev)
++{
++ struct tm t, *tp;
++ const time_t ti = time(0);
++ tp = localtime_r(&ti, &t);
++ send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"",
++ (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday),
++ tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15));
++ return 0;
++}
++
++/* CHUP - Hangup Call */
++
++static int
++atcmd_chup_execute(blt_dev_t * dev, const char * data)
++{
++ if (!dev->owner) {
++ ast_log(LOG_ERROR, "Request to hangup call when none in progress\n");
++ return -1;
++ }
++ ast_log(LOG_DEBUG, "Hangup Call\n");
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ return 0;
++}
++
++/* CIND - Call Indicator */
++
++static int
++atcmd_cind_read(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CIND: 1,0,0");
++ return 0;
++}
++
++static int
++atcmd_cind_test(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))");
++ return 0;
++}
++
++/* Set Language */
++
++static int
++atcmd_clan_read(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CLAN: \"en\"");
++ return 0;
++}
++
++/* Caller Id Presentation */
++
++static int
++atcmd_clip_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->clip = atoi(arg);
++ return 0;
++}
++
++/* Connected Line Identification Presentation */
++
++static int
++atcmd_colp_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->colp = atoi(arg);
++ return 0;
++}
++
++/* CMER - Mobile Equipment Event Reporting */
++
++static int
++atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->ready = 1;
++ dev->status = BLT_STATUS_READY;
++ return 0;
++}
++
++/* PhoneBook Types:
++ *
++ * - FD - SIM Fixed Dialing Phone Book
++ * - ME - ME Phone book
++ * - SM - SIM Phone Book
++ * - DC - ME dialled-calls list
++ * - RC - ME recieved-calls lisr
++ * - MC - ME missed-calls list
++ * - MV - ME Voice Activated Dialing List
++ * - HP - Hierachial Phone Book
++ * - BC - Own Business Card (PIN2 required)
++ *
++ */
++
++/* Read Phone Book Entry */
++
++static int
++atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: Fix the phone book!
++ // * Maybe add res_phonebook or something? */
++ send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg);
++ return 0;
++}
++
++/* Select Phone Book */
++
++static int
++atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: I guess we'll just accept any?
++ return 0;
++}
++
++static int
++atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: Language
++ return 0;
++}
++
++static int
++atcmd_eips_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n",
++ (*(arg) == 49) ? "ELIP" : "EOLP",
++ (*(arg+2) == 49) ? "ON" : "OFF");
++
++ if (*(arg) == 49)
++ dev->eolp = (*(arg+2) == 49) ? 1 : 0;
++ else
++ dev->elip = (*(arg+2) == 49) ? 1 : 0;
++
++ return 0;
++}
++
++/* VGS - Speaker Volume Gain */
++
++static int
++atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->gain_speaker = atoi(arg);
++ return 0;
++}
++
++void
++gui_eaid_response(blt_dev_t * dev, char * cmd)
++{
++ ast_log(LOG_NOTICE, "Submenu displayed.\n");
++}
++
++static int
++atcmd_eami_execute(blt_dev_t * dev, const char * data)
++{
++ char * number = NULL;
++
++ number = strndup(data, strlen(data));
++ int menuitem = atoi(number);
++
++ ast_log(LOG_NOTICE, "Menu Item '%d'.\n", menuitem);
++
++ dev->cb = gui_eaid_response;
++
++ if (menuitem == 1) {
++ char command[1024] = "";
++ const char* c1 = "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"";
++ const char* c2 = "\"";
++
++ (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
++ (void)strncat(command, gui_default_sip_number, sizeof(command) - strlen(command) - 1);
++ (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
++
++ //strcat(command, "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"");
++ //strcat(command, gui_default_sip_number);
++ //strcat(command, "\"");
++ send_atcmd(dev, command);
++ } else if (menuitem == 2) {
++ char command[1024] = "";
++ const char* c1 = "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"";
++ const char* c2 = "\"";
++
++ (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
++ (void)strncat(command, gui_default_sip_address, sizeof(command) - strlen(command) - 1);
++ (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
++
++ //strcat(command, "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"");
++ //strcat(command, gui_default_sip_address);
++ //strcat(command, "\"");
++ send_atcmd(dev, command);
++ } else if (menuitem == 0) {
++ dev->cb = gui_easm_response;
++// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
++ send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
++ } else {
++ ast_log(LOG_ERROR, "Menu item not implementented.\n");
++ }
++ return 0;
++}
++
++static int
++atcmd_eaii_execute(blt_dev_t * dev, const char * data)
++{
++ int pos = 1, len = 0;
++ char type[128];
++ char val[128];
++ const char * start = data;
++ struct sockaddr_in addr;
++
++ while (*data) {
++ if (*data == ',') {
++ memset(type, 0, 128);
++ strncpy(type, start, len);
++
++ ast_log(LOG_NOTICE, "Number(8)/Address(11): '%s'.\n", type);
++
++ pos++;
++ len = 0;
++ data++;
++ start = data;
++ continue;
++ }
++ len++;
++ data++;
++ }
++
++ memset(val, 0, 128);
++ strncpy(val, start, len);
++
++ char del[]= "\"";
++ char* address;
++ address = strtok(val, del);
++ int type_int = atoi(type);
++
++ if (strcmp(address, " 0") == 0) {
++ ast_log(LOG_NOTICE, "Spurious EAII:\n");
++ ast_log(LOG_NOTICE, data);
++ return 0;
++ }
++
++ if (type_int == 8) {
++ (void)strncat(address, "@sipgate.de", sizeof(address) - strlen(address) - 1);
++ }
++
++ ast_log(LOG_NOTICE, "SIP number/address: '%i','%s'.\n", type_int, address);
++
++ if (type_int == 8 || type_int == 11) {
++
++ char messagebox[1024] = "";
++ const char* mb1 = "AT*EAID=1,1,\"Setting up SIP call to ";
++ const char* mb2 = "\",30";
++
++ (void)strncat(messagebox, mb1, sizeof(messagebox) - strlen(messagebox) - 1);
++ (void)strncat(messagebox, address, sizeof(messagebox) - strlen(messagebox) - 1);
++ (void)strncat(messagebox, mb2, sizeof(messagebox) - strlen(messagebox) - 1);
++
++ //strcat(messagebox, "AT*EAID=1,1,\"Setting up SIP call to ");
++ //strcat(messagebox, address);
++ //strcat(messagebox, "\",30");
++ send_atcmd(dev, messagebox);
++
++ send_atcmd(dev, "AT*ESKS=2");
++ send_atcmd(dev, "AT*EKSP");
++ send_atcmd(dev, "AT*ESKS=0");
++
++ //Create manager connection to create call
++ int s = socket(AF_INET,SOCK_STREAM,0);
++ if (s < 0) {
++ ast_log(LOG_ERROR, "Manager connection failed.");
++
++ dev->cb = ag_cgmi_response;
++ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
++ return -1;
++ }
++ addr.sin_family = AF_INET;
++ addr.sin_port = htons(5038);
++ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
++ memset(&(addr.sin_zero), '\0', 8);
++
++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ ast_log(LOG_ERROR, "Manager connection failed. (2)");
++ dev->cb = ag_cgmi_response;
++ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
++ return -1;
++ }
++ char* command = "Action: login\r\nUsername: markus\r\nSecret: supAEr\r\n\r\n";
++ if (write(s,command,strlen(command)) < 0) {
++ ast_log(LOG_ERROR, "Manager connection failed. (3)");
++ dev->cb = ag_cgmi_response;
++ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
++ return -1;
++ }
++
++ char command3[1024] = "";
++ const char* action = "Action: Originate\r\nChannel: SIP/";
++ const char* action2 = "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\nAction: logoff\r\n\r\n";
++
++ (void)strncat(command3, action, sizeof(command3) - strlen(command3) - 1);
++ (void)strncat(command3, address, sizeof(command3) - strlen(command3) - 1);
++ (void)strncat(command3, action2, sizeof(command3) - strlen(command3) - 1);
++
++ //strcat(command3, "Action: Originate\r\nChannel: SIP/");
++ //strcat(command3, address);
++ //strcat(command3, "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\n");
++ ast_log(LOG_NOTICE, command3);
++
++ if (write(s,command3,strlen(command3)) < 0) {
++ ast_log(LOG_ERROR, "Manager connection failed. (5)");
++ return -1;
++ }
++ }
++ //dev->cb = ag_cgmi_response;
++ return 0;
++}
++
++/* Dial */
++static int
++atcmd_dial_execute(blt_dev_t * dev, const char * data)
++{
++ char * number = NULL;
++
++ /* Make sure there is a ';' at the end of the line */
++ if (*(data + (strlen(data) - 1)) != ';') {
++ ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data);
++ return -1;
++ }
++
++ number = strndup(data, strlen(data) - 1);
++ ast_log(LOG_NOTICE, "Dial: [%s]\n", number);
++
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ sco_start(dev, -1);
++
++ if (blt_new(dev, AST_STATE_UP, dev->context, number) == NULL) {
++ sco_stop(dev);
++ }
++
++ free(number);
++
++ return 0;
++}
++
++static int atcmd_bldn_execute(blt_dev_t * dev, const char *data)
++{
++ return atcmd_dial_execute(dev, "bldn;");
++}
++
++/* Answer */
++
++static int
++atcmd_answer_execute(blt_dev_t * dev, const char * data)
++{
++
++ if (!dev->ringing || !dev->owner) {
++ ast_log(LOG_WARNING, "Can't answer non existant call\n");
++ return -1;
++ }
++
++ dev->ringing = 0;
++
++ if (dev->ring_timer >= 0)
++ ast_sched_del(sched, dev->ring_timer);
++
++ dev->ring_timer = -1;
++
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ return answer(dev);
++}
++
++static int
++ag_unsol_ciev(blt_dev_t * dev, const char * data)
++{
++ const char * orig = data;
++ int indicator;
++ int status;
++
++ while (*(data) && *(data) == ' ')
++ data++;
++
++ if (*(data) == 0) {
++ ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ indicator = *(data++) - 48;
++
++ if (*(data++) != ',') {
++ ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ if (*(data) == 0) {
++ ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ status = *(data) - 48;
++
++ set_cind(dev, indicator, status);
++
++ return 0;
++}
++
++static int
++ag_unsol_cind(blt_dev_t * dev, const char * data)
++{
++
++ while (*(data) && *(data) == ' ')
++ data++;
++
++
++ if (dev->cind == 0)
++ {
++ int pos = 1;
++ char name[1024];
++
++ while ((data = parse_cind(data, name, 1023)) != NULL) {
++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
++ if (strcmp(name, "call") == 0)
++ dev->call_pos = pos;
++ else if (strcmp(name, "service") == 0)
++ dev->service_pos = pos;
++ else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0)
++ dev->callsetup_pos = pos;
++ pos++;
++ }
++
++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
++
++ } else {
++
++ int pos = 1, len = 0;
++ char val[128];
++ const char * start = data;
++
++ while (*data) {
++ if (*data == ',') {
++ memset(val, 0, 128);
++ strncpy(val, start, len);
++ set_cind(dev, pos, atoi(val));
++ pos++;
++ len = 0;
++ data++;
++ start = data;
++ continue;
++ }
++ len++;
++ data++;
++ }
++
++ memset(val, 0, 128);
++ strncpy(val, start, len);
++ ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val);
++
++
++ }
++
++ return 0;
++}
++
++/*
++ * handle an incoming call
++ */
++static int
++ag_unsol_clip(blt_dev_t * dev, const char * data)
++{
++ const char * orig = data;
++ char name[256];
++ char number[64];
++ int type;
++
++ while (*(data) && *(data) == ' ')
++ data++;
++
++ if (*(data) == 0) {
++ ast_log(LOG_WARNING, "Invalid value[1] for '+CLIP:%s'\n", orig);
++ return -1;
++ }
++
++ parse_clip(data, number, sizeof(number)-1, name, sizeof(name)-1, &type);
++ ast_log(LOG_NOTICE, "Parsed '+CLIP: %s' number='%s' type='%d' name='%s'\n", data, number, type, name);
++
++ blt_new(dev, AST_STATE_RING, dev->context, "s");
++
++ return 0;
++}
++
++
++
++static blt_atcb_t
++atcmd_list[] =
++{
++ { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL },
++ { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL },
++ { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL },
++ { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL },
++ { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL },
++ { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL },
++ { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev },
++ { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind },
++ { "*EAMI", NULL, NULL, atcmd_eami_execute, NULL, NULL},
++ { "*EAII", NULL, NULL, atcmd_eaii_execute, NULL, NULL},
++
++ { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL },
++ { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, ag_unsol_clip },
++ { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL },
++ { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL },
++ { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL },
++ { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL },
++ { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL },
++ { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL },
++ { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL },
++ { "+BLDN", NULL, NULL, atcmd_bldn_execute, NULL, NULL },
++};
++
++#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t))
++
++/* ---------------------------------- */
++
++/* -- Handle negotiation when we're a HS -- */
++
++void
++ag_unknown_response(blt_dev_t * dev, char * cmd)
++{
++ ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd);
++
++ // DELAYED
++ // NO CARRIER
++
++}
++
++void
++gui_easm_response(blt_dev_t * dev, char * cmd)
++{
++ ast_log(LOG_NOTICE, "Menu displayed.\n");
++}
++
++void
++ag_cgmi_response(blt_dev_t * dev, char * cmd)
++{
++ // CGMM - Phone Model
++ // CGMR - Phone Revision
++ // CGSN - IMEI
++ // AT*
++ // VTS - send tone
++ // CREG
++ // CBC - BATTERY
++ // CSQ - SIGANL
++ // CSMS - SMS STUFFS
++ // CMGL
++ // CMGR
++ // CMGS
++ // CSCA - sms CENTER NUMBER
++ // CNMI - SMS INDICATION
++ // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd);
++
++ if (dev->role == BLT_ROLE_GUI) {
++ ast_log(LOG_NOTICE, "Displaying Menu.\n");
++ dev->cb = gui_easm_response;
++// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
++ send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
++ } else {
++ dev->cb = ag_unknown_response;
++ }
++}
++
++void
++ag_cgmi_valid_response(blt_dev_t * dev, char * cmd)
++{
++ // send_atcmd(dev, "AT+WS46?");
++ // send_atcmd(dev, "AT+CRC=1");
++ // send_atcmd(dev, "AT+CNUM");
++
++ if (strcmp(cmd, "OK") == 0) {
++ send_atcmd(dev, "AT+CGMI");
++ dev->cb = ag_cgmi_response;
++ } else {
++ dev->cb = ag_unknown_response;
++ }
++}
++
++void
++ag_clip_response(blt_dev_t * dev, char * cmd)
++{
++ send_atcmd(dev, "AT+CGMI=?");
++ dev->cb = ag_cgmi_valid_response;
++}
++
++void
++ag_cmer_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_clip_response;
++ dev->ready = 1;
++ dev->status = BLT_STATUS_READY;
++ send_atcmd(dev, "AT+CLIP=1");
++}
++
++void
++ag_cind_status_response(blt_dev_t * dev, char * cmd)
++{
++ // XXX:T: Handle response.
++ dev->cb = ag_cmer_response;
++ send_atcmd(dev, "AT+CMER=3,0,0,1");
++ // Initiliase SCO link!
++}
++
++void
++ag_cind_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_cind_status_response;
++ dev->cind = 1;
++ send_atcmd(dev, "AT+CIND?");
++}
++
++void
++ag_brsf_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_cind_response;
++ ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd);
++ dev->cind = 0;
++ send_atcmd(dev, "AT+CIND=?");
++}
++
++/* ---------------------------------- */
++
++static int
++sdp_register(sdp_session_t * session)
++{
++ // XXX:T: Fix this horrible function so it makes some sense and is extensible!
++ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++ sdp_profile_desc_t profile;
++ sdp_list_t *aproto, *proto[2];
++ sdp_record_t record;
++ uint8_t u8 = rfcomm_channel_ag;
++ uint8_t u8_hs = rfcomm_channel_hs;
++ sdp_data_t *channel;
++ int ret = 0;
++
++ memset((void *)&record, 0, sizeof(sdp_record_t));
++ record.handle = 0xffffffff;
++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++ root = sdp_list_append(0, &root_uuid);
++ sdp_set_browse_groups(&record, root);
++
++ // Register as an AG
++
++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
++ svclass_id = sdp_list_append(0, &svclass_uuid);
++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++ sdp_set_service_classes(&record, svclass_id);
++ sdp_uuid16_create(&profile.uuid, 0x111f);
++ profile.version = 0x0100;
++ pfseq = sdp_list_append(0, &profile);
++
++ sdp_set_profile_descs(&record, pfseq);
++
++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++ proto[0] = sdp_list_append(0, &l2cap_uuid);
++ apseq = sdp_list_append(0, proto[0]);
++
++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++ proto[1] = sdp_list_append(0, &rfcomm_uuid);
++ channel = sdp_data_alloc(SDP_UINT8, &u8);
++ proto[1] = sdp_list_append(proto[1], channel);
++ apseq = sdp_list_append(apseq, proto[1]);
++
++ aproto = sdp_list_append(0, apseq);
++ sdp_set_access_protos(&record, aproto);
++
++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
++
++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++ ast_log(LOG_ERROR, "Service Record registration failed\n");
++ ret = -1;
++ goto end;
++ }
++
++ sdp_record_ag = record.handle;
++ sdp_record_gui = record.handle;
++
++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
++
++ sdp_data_free(channel);
++ sdp_list_free(proto[0], 0);
++ sdp_list_free(proto[1], 0);
++ sdp_list_free(apseq, 0);
++ sdp_list_free(aproto, 0);
++
++ // -------------
++
++ memset((void *)&record, 0, sizeof(sdp_record_t));
++ record.handle = 0xffffffff;
++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++ root = sdp_list_append(0, &root_uuid);
++ sdp_set_browse_groups(&record, root);
++
++ // Register as an HS
++
++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
++ svclass_id = sdp_list_append(0, &svclass_uuid);
++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++ sdp_set_service_classes(&record, svclass_id);
++ sdp_uuid16_create(&profile.uuid, 0x111e);
++ profile.version = 0x0100;
++ pfseq = sdp_list_append(0, &profile);
++ sdp_set_profile_descs(&record, pfseq);
++
++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++ proto[0] = sdp_list_append(0, &l2cap_uuid);
++ apseq = sdp_list_append(0, proto[0]);
++
++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++ proto[1] = sdp_list_append(0, &rfcomm_uuid);
++ channel = sdp_data_alloc(SDP_UINT8, &u8_hs);
++ proto[1] = sdp_list_append(proto[1], channel);
++ apseq = sdp_list_append(apseq, proto[1]);
++
++ aproto = sdp_list_append(0, apseq);
++ sdp_set_access_protos(&record, aproto);
++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
++
++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++ ast_log(LOG_ERROR, "Service Record registration failed\n");
++ ret = -1;
++ goto end;
++ }
++
++ sdp_record_hs = record.handle;
++
++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
++
++end:
++ sdp_data_free(channel);
++ sdp_list_free(proto[0], 0);
++ sdp_list_free(proto[1], 0);
++ sdp_list_free(apseq, 0);
++ sdp_list_free(aproto, 0);
++
++ return ret;
++}
++
++static int
++rfcomm_listen(bdaddr_t * bdaddr, int channel)
++{
++
++ int sock = -1;
++ struct sockaddr_rc loc_addr;
++ int on = 1;
++
++ if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
++ ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno);
++ return -1;
++ }
++
++ loc_addr.rc_family = AF_BLUETOOTH;
++
++ /* Local Interface Address */
++ bacpy(&loc_addr.rc_bdaddr, bdaddr);
++
++ /* Channel */
++ loc_addr.rc_channel = channel;
++
++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
++ ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
++ close(sock);
++ return -1;
++ }
++
++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
++
++ if (listen(sock, 10) < 0) {
++ ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock);
++
++ return sock;
++}
++
++
++static int
++sco_listen(bdaddr_t * bdaddr)
++{
++ int sock = -1;
++ int on = 1;
++ struct sockaddr_sco loc_addr;
++
++ memset(&loc_addr, 0, sizeof(loc_addr));
++
++ if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
++ ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno);
++ return -1;
++ }
++
++ loc_addr.sco_family = AF_BLUETOOTH;
++ bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY);
++
++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
++ ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
++ close(sock);
++ return -1;
++ }
++
++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++
++ if (listen(sock, 10) < 0) {
++ ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock);
++
++ return sock;
++}
++
++static int
++rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio)
++{
++ struct sockaddr_rc addr;
++ int s;
++
++ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.rc_family = AF_BLUETOOTH;
++ bacpy(&addr.rc_bdaddr, src);
++ addr.rc_channel = 0;
++
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ close(s);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.rc_family = AF_BLUETOOTH;
++ bacpy(&addr.rc_bdaddr, dst);
++ addr.rc_channel = channel;
++
++ if (nbio) {
++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
++ }
++
++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) {
++ close(s);
++ return -1;
++ }
++
++ return s;
++}
++
++/* Must be called with dev->lock held */
++
++static int
++sco_connect(blt_dev_t * dev)
++{
++ struct sockaddr_sco addr;
++ // struct sco_conninfo conn;
++ // struct sco_options opts;
++ // int size;
++ // bdaddr_t * src = &local_bdaddr;
++
++ int s;
++ bdaddr_t * dst = &(dev->bdaddr);
++
++ if (dev->sco != -1) {
++ ast_log(LOG_ERROR, "SCO fd already open.\n");
++ return -1;
++ }
++
++ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
++ ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno));
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++
++ addr.sco_family = AF_BLUETOOTH;
++ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
++
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sco_family = AF_BLUETOOTH;
++ bacpy(&addr.sco_bdaddr, dst);
++
++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++
++ if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) {
++ ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno);
++ close(s);
++ return -1;
++ }
++
++ //size = sizeof(conn);
++
++
++/* XXX:T: HERE, fix getting SCO conninfo.
++
++ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ size = sizeof(opts);
++
++ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ dev->sco_handle = conn.hci_handle;
++ dev->sco_mtu = opts.mtu;
++
++*/
++
++ ast_log(LOG_DEBUG, "SCO: %d\n", s);
++
++ dev->sco = s;
++
++ return 0;
++}
++
++
++/* ---------------------------------- */
++
++/* Non blocking (async) outgoing bluetooth connection */
++
++static int
++try_connect(blt_dev_t * dev)
++{
++ int fd;
++ ast_mutex_lock(&(dev->lock));
++
++ if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) {
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ if (dev->rd != -1) {
++
++ int ret;
++ struct pollfd pfd;
++
++ if (dev->status != BLT_STATUS_CONNECTING) {
++ ast_mutex_unlock(&(dev->lock));
++ dev->outgoing_id = -1;
++ return 0;
++ }
++
++ // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); //
++
++ pfd.fd = dev->rd;
++ pfd.events = POLLIN | POLLOUT;
++
++ ret = poll(&pfd, 1, 0);
++
++ if (ret == -1) {
++ close(dev->rd);
++ dev->rd = -1;
++ dev->status = BLT_STATUS_DOWN;
++ dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ if (ret > 0) {
++
++ int len = sizeof(ret);
++ getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len);
++
++ if (ret == 0) {
++
++ ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name);
++
++#if 0
++ {
++ struct hci_conn_info_req * cr;
++ int dd;
++ char name[248];
++
++ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
++ dd = hci_open_dev(hcidev_id);
++ cr->type = ACL_LINK;
++ bacpy(&cr->bdaddr, &(dev->bdaddr));
++
++ if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) {
++ ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno));
++ } else {
++ ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle);
++ }
++
++ if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0)
++ ast_log(LOG_DEBUG, "Remote Name: %s\n", name);
++ free(cr);
++ }
++#endif
++
++ dev->status = BLT_STATUS_NEGOTIATING;
++
++ /* If this device is an AG/GUI, we initiate the negotiation. */
++
++ if (dev->role == BLT_ROLE_AG ||
++ dev->role == BLT_ROLE_GUI) {
++ dev->cb = ag_brsf_response;
++ send_atcmd(dev, "AT+BRSF=23");
++ }
++
++ dev->outgoing_id = -1;
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++
++ } else {
++
++ if (ret != EHOSTDOWN)
++ ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret);
++
++ close(dev->rd);
++ dev->rd = -1;
++ dev->status = BLT_STATUS_DOWN;
++ dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++
++ }
++
++ }
++
++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ ast_log(LOG_NOTICE, "RFCOMM connect start.\n");
++ fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1);
++ ast_log(LOG_NOTICE, "RFCOMM connect done.\n");
++
++ if (fd == -1) {
++ ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno));
++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ dev->rd = fd;
++ dev->status = BLT_STATUS_CONNECTING;
++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++}
++
++
++/* Called whenever a new command is received while we're the AG */
++
++
++static int
++process_rfcomm_cmd(blt_dev_t * dev, char * cmd)
++{
++ int i;
++ char * fullcmd = cmd;
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd);
++
++ /* Read the 'AT' from the start of the string */
++ if (strncmp(cmd, "AT", 2)) {
++ ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd);
++ send_atcmd_error(dev);
++ return 0;
++ }
++
++ cmd += 2;
++
++ // Don't forget 'AT' on its own is OK.
++
++ if (strlen(cmd) == 0) {
++ send_atcmd_ok(dev, fullcmd);
++ return 0;
++ }
++
++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
++ if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) {
++ char * pos = (cmd + strlen(atcmd_list[i].str));
++ if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) {
++ /* TEST command */
++ if (atcmd_list[i].test) {
++ if (atcmd_list[i].test(dev) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ send_atcmd_ok(dev, fullcmd);
++ }
++ } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) {
++ /* READ command */
++ if (atcmd_list[i].read) {
++ if (atcmd_list[i].read(dev) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ } else if (strncmp(pos, "=", 1) == 0) {
++ /* SET command */
++ if (atcmd_list[i].set) {
++ if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ } else {
++ /* EXECUTE command */
++ if (atcmd_list[i].execute) {
++ if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ }
++ return 0;
++ }
++ }
++
++ ast_log(LOG_NOTICE, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd);
++ send_atcmd_error(dev);
++
++ return 0;
++}
++
++/* Called when a socket is incoming */
++
++static void
++handle_incoming(int fd, blt_role_t role)
++{
++ blt_dev_t * dev;
++ struct sockaddr_rc addr;
++ int len = sizeof(addr);
++
++ // Got a new incoming socket.
++ ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n");
++
++ ast_mutex_lock(&iface_lock);
++
++ fd = accept(fd, (struct sockaddr*)&addr, &len);
++
++ dev = iface_head;
++ while (dev) {
++ if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) {
++ ast_log(LOG_DEBUG, "Connect from %s\n", dev->name);
++ ast_mutex_lock(&(dev->lock));
++ /* Kill any outstanding connect attempt. */
++ if (dev->outgoing_id > -1) {
++ ast_sched_del(sched, dev->outgoing_id);
++ dev->outgoing_id = -1;
++ }
++
++ rd_close(dev, 0, 0);
++
++ dev->status = BLT_STATUS_NEGOTIATING;
++ dev->rd = fd;
++
++ if (dev->role == BLT_ROLE_AG ||
++ dev->role == BLT_ROLE_GUI) {
++ dev->cb = ag_brsf_response;
++ send_atcmd(dev, "AT+BRSF=23");
++ }
++ ast_mutex_unlock(&(dev->lock));
++ break;
++ }
++ dev = dev->next;
++ }
++
++ if (dev == NULL) {
++ ast_log(LOG_WARNING, "Connect from unknown device\n");
++ close(fd);
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ return;
++}
++
++static void
++handle_incoming_sco(int master)
++{
++
++ blt_dev_t * dev;
++ struct sockaddr_sco addr;
++ struct sco_conninfo conn;
++ struct sco_options opts;
++ int len = sizeof(addr);
++ int fd;
++
++ ast_log(LOG_DEBUG, "Incoming SCO socket\n");
++
++ fd = accept(master, (struct sockaddr*)&addr, &len);
++
++ if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) {
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++ close(fd);
++ return;
++ }
++
++ len = sizeof(conn);
++
++ if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
++ close(fd);
++ return;
++ }
++
++ len = sizeof(opts);
++
++ if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
++ close(fd);
++ return;
++ }
++
++ ast_mutex_lock(&iface_lock);
++ dev = iface_head;
++ while (dev) {
++ if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) {
++ ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name);
++ ast_mutex_lock(&(dev->lock));
++ if (dev->sco_running != -1) {
++ ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n");
++ } else {
++ sco_start(dev, fd);
++ }
++ ast_mutex_unlock(&(dev->lock));
++ break;
++ }
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ if (dev == NULL) {
++ ast_log(LOG_WARNING, "SCO Connect from unknown device\n");
++ close(fd);
++ } else {
++ // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress.
++ ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu);
++ }
++
++
++
++ return;
++}
++
++/* Called when there is data waiting on a socket */
++
++static int
++handle_rd_data(blt_dev_t * dev)
++{
++ char c;
++ int ret;
++
++ while ((ret = read(dev->rd, &c, 1)) == 1) {
++
++ // log_buf[i++] = c;
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ if (c == '\r') {
++ ret = process_rfcomm_cmd(dev, dev->rd_buff);
++ dev->rd_buff_pos = 0;
++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
++ return ret;
++ }
++
++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX)
++ return 0;
++
++ dev->rd_buff[dev->rd_buff_pos++] = c;
++
++ } else if (dev->role == BLT_ROLE_AG ||
++ dev->role == BLT_ROLE_GUI) {
++
++ //ast_log(LOG_ERROR, "%s: %c\n", dev->name, c);
++
++ switch (dev->state) {
++ case BLT_STATE_WANT_R:
++ if (c == '\r' || c == 10) {
++ dev->state = BLT_STATE_WANT_N;
++ } else if (c == '+') {
++ dev->state = BLT_STATE_WANT_CMD;
++ dev->rd_buff[dev->rd_buff_pos++] = '+';
++ } else {
++ ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c);
++ return -1;
++ }
++ break;
++
++ case BLT_STATE_WANT_N:
++ if (c == '\n' || c == 13)
++ dev->state = BLT_STATE_WANT_CMD;
++ else {
++ ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c);
++ return -1;
++ }
++ break;
++
++ case BLT_STATE_WANT_CMD:
++ if (c == '\r' || c == 10)
++ dev->state = BLT_STATE_WANT_N2;
++ else {
++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) {
++ ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name);
++ return -1;
++ }
++ dev->rd_buff[dev->rd_buff_pos++] = c;
++ }
++ break;
++
++ case BLT_STATE_WANT_N2:
++ if (c == '\n' || c == 13) {
++
++ dev->state = BLT_STATE_WANT_R;
++
++ if (dev->rd_buff[0] == '+') {
++ int i;
++ // find unsolicited
++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
++ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
++ if (atcmd_list[i].unsolicited)
++ atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
++ else
++ ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff);
++ break;
++ }
++ }
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++
++ if (i == ATCMD_LIST_LEN)
++ ast_log(LOG_NOTICE, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff);
++
++ } else if (dev->rd_buff[0] == '*') {
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s]* %*s > %s\n", role2str(dev->role), 9, dev->name, dev->rd_buff);
++
++ int i;
++ // find execute
++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
++ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
++ if (atcmd_list[i].execute)
++ atcmd_list[i].execute(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
++ else
++ ast_log(LOG_ERROR, "Device %s: Unhandled Execute: %s\n", dev->name, dev->rd_buff);
++ break;
++ }
++ }
++
++
++ } else {
++
++ if (
++ strcmp(dev->rd_buff, "OK") != 0 &&
++ strcmp(dev->rd_buff, "CONNECT") != 0 &&
++ strcmp(dev->rd_buff, "RING") != 0 &&
++ strcmp(dev->rd_buff, "NO CARRIER") != 0 &&
++ strcmp(dev->rd_buff, "ERROR") != 0 &&
++ strcmp(dev->rd_buff, "NO DIALTONE") != 0 &&
++ strcmp(dev->rd_buff, "BUSY") != 0 &&
++ strcmp(dev->rd_buff, "NO ANSWER") != 0 &&
++ strcmp(dev->rd_buff, "DELAYED") != 0
++ ){
++ // It must be a multiline error
++ strncpy(dev->last_err_cmd, dev->rd_buff, 1023);
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++ } else if (dev->cb) {
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++ dev->cb(dev, dev->rd_buff);
++ } else {
++ ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name);
++ }
++
++ }
++
++ dev->rd_buff_pos = 0;
++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
++ } else {
++
++ ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c);
++ return -1;
++
++ }
++
++ break;
++
++ default:
++ ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state);
++ return -1;
++
++ }
++
++ }
++
++ }
++
++ return 0;
++}
++
++/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */
++
++static void
++rd_close(blt_dev_t * dev, int reconnect, int e)
++{
++ dev->ready = 0;
++
++ if (dev->rd)
++ close(dev->rd);
++
++ dev->rd = -1;
++
++ dev->status = BLT_STATUS_DOWN;
++
++ sco_stop(dev);
++
++ if (dev->owner) {
++ ast_setstate(dev->owner, AST_STATE_DOWN);
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ }
++
++ /* Schedule a reconnect */
++ if (reconnect && dev->autoconnect) {
++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
++
++ if (monitor_thread == pthread_self()) {
++ // Because we're not the monitor thread, we needd to inturrupt poll().
++ pthread_kill(monitor_thread, SIGURG);
++ }
++
++ if (e)
++ ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e);
++ } else if (e) {
++ ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e);
++ }
++
++ return;
++}
++
++/*
++ * Remember that we can only add to the scheduler from
++ * the do_monitor thread, as it calculates time to next one from
++ * this loop.
++ */
++
++static void *
++do_monitor(void * data)
++{
++#define SRV_SOCK_CNT 4
++
++ int res = 0;
++ blt_dev_t * dev;
++ struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT));
++
++ /* -- We start off by trying to connect all of our devices (non blocking) -- */
++
++ monitor_pid = getpid();
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++ while (dev) {
++
++ if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) {
++ ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno);
++ ast_mutex_unlock(&iface_lock);
++ return NULL;
++ }
++
++ if (dev->autoconnect && dev->status == BLT_STATUS_DOWN)
++ dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev);
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- Now, Scan all sockets, and service scheduler -- */
++
++ pfds[0].fd = rfcomm_sock_ag;
++ pfds[0].events = POLLIN;
++
++ pfds[1].fd = rfcomm_sock_hs;
++ pfds[1].events = POLLIN;
++
++ pfds[2].fd = rfcomm_sock_gui;
++ pfds[2].events = POLLIN;
++
++ pfds[3].fd = sco_socket;
++ pfds[3].events = POLLIN;
++
++ while (1) {
++ int cnt = SRV_SOCK_CNT;
++ int i;
++
++ /* -- Build pfds -- */
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++ dev = iface_head;
++ while (dev) {
++ ast_mutex_lock(&(dev->lock));
++ if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) {
++ pfds[cnt].fd = dev->rd;
++ pfds[cnt].events = POLLIN;
++ cnt++;
++ }
++ ast_mutex_unlock(&(dev->lock));
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- End Build pfds -- */
++
++ res = ast_sched_wait(sched);
++ res = poll(pfds, cnt, MAX(100, MIN(100, res)));
++
++ if (res == 0)
++ ast_sched_runq(sched);
++
++ if (pfds[0].revents) {
++ handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG);
++ res--;
++ }
++
++ if (pfds[1].revents) {
++ handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS);
++ res--;
++ }
++
++ if (pfds[2].revents) {
++ handle_incoming(rfcomm_sock_gui, BLT_ROLE_GUI);
++ res--;
++ }
++
++ if (pfds[3].revents) {
++ handle_incoming_sco(sco_socket);
++ res--;
++ }
++
++ if (res == 0)
++ continue;
++
++ for (i = SRV_SOCK_CNT ; i < cnt ; i++) {
++
++ /* Optimise a little bit */
++ if (res == 0)
++ break;
++ else if (pfds[i].revents == 0)
++ continue;
++
++ /* -- Find the socket that has activity -- */
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++
++ while (dev) {
++ if (pfds[i].fd == dev->rd) {
++ ast_mutex_lock(&(dev->lock));
++ if (pfds[i].revents & POLLIN) {
++ if (handle_rd_data(dev) == -1) {
++ rd_close(dev, 0, 0);
++ }
++ } else {
++ rd_close(dev, 1, sock_err(dev->rd));
++ }
++ ast_mutex_unlock(&(dev->lock));
++ res--;
++ break;
++ }
++ dev = dev->next;
++ }
++
++ if (dev == NULL) {
++ ast_log(LOG_ERROR, "Unhandled fd from poll()\n");
++ close(pfds[i].fd);
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- End find socket with activity -- */
++
++ }
++
++ }
++
++ return NULL;
++}
++
++static int
++restart_monitor(void)
++{
++
++ if (monitor_thread == AST_PTHREADT_STOP)
++ return 0;
++
++ if (ast_mutex_lock(&monitor_lock)) {
++ ast_log(LOG_WARNING, "Unable to lock monitor\n");
++ return -1;
++ }
++
++ if (monitor_thread == pthread_self()) {
++ ast_mutex_unlock(&monitor_lock);
++ ast_log(LOG_WARNING, "Cannot kill myself\n");
++ return -1;
++ }
++
++ if (monitor_thread != AST_PTHREADT_NULL) {
++
++ /* Just signal it to be sure it wakes up */
++ pthread_cancel(monitor_thread);
++ pthread_kill(monitor_thread, SIGURG);
++ ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n");
++ pthread_join(monitor_thread, NULL);
++ ast_log(LOG_DEBUG, "joined\n");
++
++ } else {
++
++ /* Start a new monitor */
++ if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
++ ast_mutex_unlock(&monitor_lock);
++ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
++ return -1;
++ }
++
++ }
++
++ ast_mutex_unlock(&monitor_lock);
++ return 0;
++}
++
++static int
++blt_parse_config(void)
++{
++ struct ast_config * cfg;
++ struct ast_variable * v;
++ char * cat;
++
++ cfg = ast_config_load(BLT_CONFIG_FILE);
++
++ if (!cfg) {
++ ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
++ return -1;
++ }
++
++ v = ast_variable_browse(cfg, "general");
++
++ while (v) {
++ if (!strcasecmp(v->name, "rfchannel_ag")) {
++ rfcomm_channel_ag = atoi(v->value);
++ } else if (!strcasecmp(v->name, "rfchannel_hs")) {
++ rfcomm_channel_hs = atoi(v->value);
++ } else if (!strcasecmp(v->name, "rfchannel_gui")) {
++ rfcomm_channel_gui = atoi(v->value);
++ } else if (!strcasecmp(v->name, "interface")) {
++ hcidev_id = atoi(v->value);
++ } else if (!strcasecmp(v->name, "gui_default_sip_number")) {
++ gui_default_sip_number = v->value;
++ } else if (!strcasecmp(v->name, "gui_default_sip_address")) {
++ gui_default_sip_address = v->value;
++ } else {
++ ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name);
++ }
++ v = v->next;
++ }
++ cat = ast_category_browse(cfg, NULL);
++
++ while(cat) {
++
++ char * str;
++
++ if (strcasecmp(cat, "general")) {
++ blt_dev_t * device = malloc(sizeof(blt_dev_t));
++ memset(device, 0, sizeof(blt_dev_t));
++ device->sco_running = -1;
++ device->sco = -1;
++ device->rd = -1;
++ device->outgoing_id = -1;
++ device->status = BLT_STATUS_DOWN;
++ str2ba(cat, &(device->bdaddr));
++ device->name = ast_variable_retrieve(cfg, cat, "name");
++
++ str = ast_variable_retrieve(cfg, cat, "type");
++
++ if (str == NULL) {
++ ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=<HS/AG>\n", cat);
++ return -1;
++ } else if (strcasecmp(str, "HS") == 0) {
++ device->role = BLT_ROLE_HS;
++ } else if (strcasecmp(str, "AG") == 0) {
++ device->role = BLT_ROLE_AG;
++ } else if (strcasecmp(str, "GUI") == 0) {
++ device->role = BLT_ROLE_GUI;
++ } else {
++ ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str);
++ return -1;
++ }
++
++ /* XXX:T: Find channel to use using SDP.
++ * However, this needs to be non blocking, and I can't see
++ * anything in sdp_lib.h that will allow non blocking calls.
++ */
++
++ device->channel = 1;
++
++ if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL)
++ device->channel = atoi(str);
++
++ if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL)
++ device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0;
++
++ if ((str = ast_variable_retrieve(cfg, cat, "context")) != NULL)
++ device->context = str;
++ else
++ device->context = "bluetooth";
++
++ device->next = iface_head;
++ iface_head = device;
++ ifcount++;
++ }
++
++ cat = ast_category_browse(cfg, cat);
++ }
++ return 0;
++}
++
++
++static int
++blt_show_peers(int fd, int argc, char *argv[])
++{
++ blt_dev_t * dev;
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get Iface lock\n");
++ ast_cli(fd, "Failed to get iface lock\n");
++ return RESULT_FAILURE;
++ }
++
++ dev = iface_head;
++
++ ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n");
++ ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n");
++
++ while (dev) {
++ char b1[18];
++ ba2str(&(dev->bdaddr), b1);
++ ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n",
++ b1, dev->name,
++// (dev->role == BLT_ROLE_HS) ? "HS" : "AG",
++ (dev->role == BLT_ROLE_HS) ? "HS" : (dev->role == BLT_ROLE_AG) ? "AG" : "GUI",
++ status2str(dev->status),
++ (dev->autoconnect) ? "Yes" : "No",
++ dev->sco_running,
++ dev->sco,
++ dev->sco_thread,
++ (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A"
++ );
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++ return RESULT_SUCCESS;
++}
++
++static int
++blt_show_information(int fd, int argc, char *argv[])
++{
++ char b1[18];
++ ba2str(&local_bdaddr, b1);
++ ast_cli(fd, "-------------------------------------------\n");
++ ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION);
++ ast_cli(fd, " Monitor PID : %d\n", monitor_pid);
++ ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag);
++ ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs);
++ ast_cli(fd, " RFCOMM GUI : Channel %d, FD %d\n", rfcomm_channel_gui, rfcomm_sock_gui);
++ ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1);
++ ast_cli(fd, "-------------------------------------------\n");
++ return RESULT_SUCCESS;
++}
++
++static int
++blt_ag_sendcmd(int fd, int argc, char *argv[])
++{
++ blt_dev_t * dev;
++
++ if (argc != 4)
++ return RESULT_SHOWUSAGE;
++
++ ast_mutex_lock(&iface_lock);
++ dev = iface_head;
++ while (dev) {
++ if (!strcasecmp(argv[2], dev->name))
++ break;
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ if (!dev) {
++ ast_cli(fd, "Device '%s' does not exist\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if ((dev->role != BLT_ROLE_AG) && (dev->role != BLT_ROLE_GUI)) {
++ ast_cli(fd, "Device '%s' is not an AG or GUI\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) {
++ ast_cli(fd, "Device '%s' is not connected\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if (*(argv[3] + strlen(argv[3]) - 1) == '.')
++ *(argv[3] + strlen(argv[3]) - 1) = '?';
++
++ ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]);
++
++ ast_mutex_lock(&(dev->lock));
++ send_atcmd(dev, argv[3]);
++ ast_mutex_unlock(&(dev->lock));
++
++ return RESULT_SUCCESS;
++}
++
++static char *
++complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role)
++{
++ blt_dev_t * dev;
++ int which = 0;
++ char *ret;
++
++ if (pos != rpos)
++ return NULL;
++
++ ast_mutex_lock(&iface_lock);
++
++ dev = iface_head;
++
++ while (dev) {
++
++ if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) {
++ if (++which > state)
++ break;
++ }
++
++ dev = dev->next;
++ }
++
++ if (dev)
++ ret = strdup(dev->name);
++ else
++ ret = NULL;
++
++ ast_mutex_unlock(&iface_lock);
++
++ return ret;
++}
++
++static char *
++complete_device_2_ag_gui(char * line, char * word, int pos, int state)
++{
++ return complete_device(line, word, pos, state, 2, BLT_ROLE_AG);
++}
++
++static char show_peers_usage[] =
++"Usage: bluetooth show peers\n"
++" List all bluetooth peers and their status\n";
++
++static struct ast_cli_entry
++cli_show_peers =
++ { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage };
++
++
++static char ag_sendcmd[] =
++"Usage: bluetooth <device> sendcmd <cmd>\n"
++" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n";
++
++static struct ast_cli_entry
++cli_ag_sendcmd =
++ { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG/GUI an AT command", ag_sendcmd, complete_device_2_ag_gui };
++
++static char show_information[] =
++"Usage: bluetooth show information\n"
++" Lists information about the bluetooth subsystem\n";
++
++static struct ast_cli_entry
++cli_show_information =
++ { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information };
++
++void
++remove_sdp_records(void)
++{
++
++ sdp_session_t * sdp;
++ sdp_list_t * attr;
++ sdp_record_t * rec;
++ int res = -1;
++ uint32_t range = 0x0000ffff;
++
++ if (sdp_record_ag == -1 || sdp_record_gui == -1 || sdp_record_hs == -1)
++ return;
++
++ ast_log(LOG_DEBUG, "Removing SDP records\n");
++
++ sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++
++ if (!sdp)
++ return;
++
++ attr = sdp_list_append(0, &range);
++ rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr);
++ sdp_list_free(attr, 0);
++
++ if (rec)
++ if (sdp_record_unregister(sdp, rec) == 0)
++ res = 0;
++
++ rec = sdp_service_attr_req(sdp, sdp_record_gui, SDP_ATTR_REQ_RANGE, attr);
++ sdp_list_free(attr, 0);
++
++ if (rec)
++ if (sdp_record_unregister(sdp, rec) == 0)
++ res = 0;
++
++ attr = sdp_list_append(0, &range);
++ rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr);
++ sdp_list_free(attr, 0);
++
++ if (rec)
++ if (sdp_record_unregister(sdp, rec) == 0)
++ res = 0;
++
++ sdp_close(sdp);
++
++ if (res == 0)
++ ast_log(LOG_NOTICE, "Removed SDP records\n");
++ else
++ ast_log(LOG_ERROR, "Failed to remove SDP records\n");
++
++}
++
++static int
++__unload_module(void)
++{
++
++#if ASTERISK_VERSION_NUM <= 010107
++ ast_channel_unregister(BLT_CHAN_NAME);
++#else
++ ast_channel_unregister(&blt_tech);
++#endif
++
++ if (monitor_thread != AST_PTHREADT_NULL) {
++
++ if (ast_mutex_lock(&monitor_lock)) {
++
++ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
++ pthread_cancel(monitor_thread);
++ pthread_kill(monitor_thread, SIGURG);
++ fprintf(stderr, "Waiting for monitor thread to join...\n");
++ pthread_join(monitor_thread, NULL);
++ fprintf(stderr, "joined\n");
++ }
++ monitor_thread = AST_PTHREADT_STOP;
++ ast_mutex_unlock(&monitor_lock);
++
++ } else {
++
++ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
++ return -1;
++
++ }
++
++ }
++
++ ast_unregister_atexit(remove_sdp_records);
++ remove_sdp_records();
++ return 0;
++}
++
++int
++load_module()
++{
++ sdp_session_t * sess;
++ int dd;
++ uint16_t vs;
++
++ hcidev_id = BLT_DEFAULT_HCI_DEV;
++
++ if (blt_parse_config() != 0) {
++ ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n");
++ return unload_module();
++ }
++
++ dd = hci_open_dev(hcidev_id);
++ if (dd == -1) {
++ ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno));
++ return -1;
++ }
++
++ hci_read_voice_setting(dd, &vs, 1000);
++ vs = htobs(vs);
++ close(dd);
++
++ if (vs != 0x0060) {
++ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs);
++ unload_module();
++ return 0;
++ }
++
++ if ((sched = sched_context_create()) == NULL) {
++ ast_log(LOG_WARNING, "Unable to create schedule context\n");
++ return -1;
++ }
++
++ memset(&local_bdaddr, 0, sizeof(local_bdaddr));
++
++ hci_devba(hcidev_id, &local_bdaddr);
++
++ /* --- Add SDP record --- */
++
++ sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++
++ if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) {
++ return -1;
++ }
++
++ if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0)
++ return -1;
++
++ if ((rfcomm_sock_gui = rfcomm_listen(&local_bdaddr, rfcomm_channel_gui)) < 0)
++ return -1;
++
++ if ((sco_socket = sco_listen(&local_bdaddr)) < 0)
++ return -1;
++
++ if (!sess) {
++ ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno));
++ return -1;
++ }
++
++ if (sdp_register(sess) != 0) {
++ ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n");
++ return -1;
++ }
++
++ sdp_close(sess);
++
++ if (restart_monitor() != 0)
++ return -1;
++
++#if ASTERISK_VERSION_NUM <= 010107
++ if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) {
++#else
++ if (ast_channel_register(&blt_tech)) {
++#endif
++ ast_log(LOG_ERROR, "Unable to register channel class BTL\n");
++ __unload_module();
++ return -1;
++ }
++
++ ast_cli_register(&cli_show_information);
++ ast_cli_register(&cli_show_peers);
++ ast_cli_register(&cli_ag_sendcmd);
++
++ ast_register_atexit(remove_sdp_records);
++
++ ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1);
++
++ return 0;
++}
++
++int
++unload_module(void)
++{
++ ast_cli_unregister(&cli_ag_sendcmd);
++ ast_cli_unregister(&cli_show_peers);
++ ast_cli_unregister(&cli_show_information);
++ return __unload_module();
++}
++
++int
++usecount()
++{
++ int res;
++ ast_mutex_lock(&usecnt_lock);
++ res = usecnt;
++ ast_mutex_unlock(&usecnt_lock);
++ return res;
++}
++
++char *description()
++{
++ return "Bluetooth Channel Driver";
++}
++
++char *
++key()
++{
++ return ASTERISK_GPL_KEY;
++}
++
++
+diff -Nru asterisk-1.2.14.org/channels/Makefile asterisk-1.2.14/channels/Makefile
+--- asterisk-1.2.14.org/channels/Makefile 2006-08-17 23:57:19.000000000 +0200
++++ asterisk-1.2.14/channels/Makefile 2006-12-27 09:03:53.000000000 +0100
+@@ -249,6 +249,13 @@
+ #chan_modem.so : chan_modem.o
+ # $(CC) -rdynamic -shared -Xlinker -x -o $@ $<
+
++#
++# Asterisk Bluetooth Support
++# http://www.crazygreek.co.uk/content/chan_bluetooth
++#
++chan_bluetooth.so: chan_bluetooth.o
++ $(CC) $(SOLINK) -o $@ $< $(EXTRA_LDFLAGS) -lbluetooth
++
+ install: all
+ for x in $(CHANNEL_LIBS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
+ if ! [ -f chan_iax.so ]; then rm -f $(DESTDIR)$(MODULES_DIR)/chan_iax.so ; fi
+diff -Nru asterisk-1.2.14.org/configs/bluetooth.conf asterisk-1.2.14/configs/bluetooth.conf
+--- asterisk-1.2.14.org/configs/bluetooth.conf 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/configs/bluetooth.conf 2006-12-27 09:03:53.000000000 +0100
+@@ -0,0 +1,46 @@
++[general]
++; Channel we listen on as a HS (Headset)
++rfchannel_hs = 2
++; Channel we listen on as an AG (AudioGateway)
++rfchannel_ag = 3
++; Channel we listen on as GUI
++rfchannel_gui = 4
++; hci interface to use (number - e.g '0')
++interface = 0
++
++; RFCOMM channel to connect to. For a HandsSet:
++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E
++; or,for an AudioGateway (Phone):
++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F
++;
++; Find the 'channel' value under RFCOMM.
++;
++;channel = 6
++; Automatically connect?
++;autoconnect = yes
++
++;example for a SonyEricsson mobile as a GUI device
++[00:0F:DE:6E:77:6B]
++name = T610
++type = GUI
++channel = 6
++;channel = 1
++autoconnect = yes
++
++;[00:0E:6D:1A:3D:86]
++;name = Nokia
++;type = AG
++;channel = 13
++;autoconnect = yes
++
++[00:0E:A1:01:49:AE]
++name = AutoBlue
++type = HS
++channel = 2
++autoconnect = yes
++
++;[00:0A:D9:EB:FD:D8]
++;name = P900
++;type = AG
++;channel = 8
++;autoconnect = no
--- /dev/null
+diff -Nru asterisk-1.2.14.org/apps/app_sql_mysql.c asterisk-1.2.14/apps/app_sql_mysql.c
+--- asterisk-1.2.14.org/apps/app_sql_mysql.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/apps/app_sql_mysql.c 2006-12-27 09:00:04.000000000 +0100
+@@ -0,0 +1,445 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Connect to MySQL
++ *
++ * Copyright (C) 2004, Constantine Filin and Christos Ricudis
++ *
++ * Christos Ricudis <ricudis@itc.auth.gr>
++ * Constantine Filin <cf@intermedia.net>
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdlib.h>
++#include <sys/types.h>
++#include <stdio.h>
++#include <unistd.h>
++
++#include <mysql.h>
++
++#include <asterisk/file.h>
++#include <asterisk/logger.h>
++#include <asterisk/channel.h>
++#include <asterisk/pbx.h>
++#include <asterisk/module.h>
++#include <asterisk/linkedlists.h>
++#include <asterisk/chanvars.h>
++#include <asterisk/lock.h>
++
++#define EXTRA_LOG 0
++
++static char *tdesc = "Simple Mysql Interface";
++
++static char *app = "MYSQL";
++
++static char *synopsis = "Do several mySQLy things";
++
++static char *descrip =
++"MYSQL(): Do several mySQLy things\n"
++"Syntax:\n"
++" MYSQL(Connect connid dhhost dbuser dbpass dbname)\n"
++" Connects to a database. Arguments contain standard MySQL parameters\n"
++" passed to function mysql_real_connect. Connection identifer returned\n"
++" in ${var}\n"
++" MYSQL(Query resultid ${connid} query-string)\n"
++" Executes standard MySQL query contained in query-string using established\n"
++" connection identified by ${connection_identifier}. Result of query is\n"
++" is stored in ${var}.\n"
++" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n"
++" Fetches a single row from a result set contained in ${result_identifier}.\n"
++" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n"
++" if additional rows exist in result set.\n"
++" MYSQL(Clear ${resultid})\n"
++" Frees memory and datastructures associated with result set.\n"
++" MYSQL(Disconnect ${connid})\n"
++" Disconnects from named connection to MySQL.\n"
++" On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n";
++
++/*
++EXAMPLES OF USE :
++
++exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit)
++exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM})
++exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2)
++exten => s,5,GotoIf(${fetchid}?6:8)
++exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.")
++exten => s,7,Goto(s,4)
++exten => s,8,MYSQL(Clear ${resultid})
++exten => s,9,MYSQL(Disconnect ${connid})
++*/
++
++STANDARD_LOCAL_USER;
++LOCAL_USER_DECL;
++
++AST_MUTEX_DEFINE_STATIC(_mysql_mutex);
++
++#define AST_MYSQL_ID_DUMMY 0
++#define AST_MYSQL_ID_CONNID 1
++#define AST_MYSQL_ID_RESID 2
++#define AST_MYSQL_ID_FETCHID 3
++
++struct ast_MYSQL_id {
++ int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
++ int identifier;
++ void *data;
++ AST_LIST_ENTRY(ast_MYSQL_id) entries;
++} *ast_MYSQL_id;
++
++AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head;
++
++/* helpful procs */
++static void *find_identifier(int identifier,int identifier_type) {
++ struct MYSQLidshead *headp;
++ struct ast_MYSQL_id *i;
++ void *res=NULL;
++ int found=0;
++
++ headp=&_mysql_ids_head;
++
++ if (AST_LIST_LOCK(headp)) {
++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
++ } else {
++ AST_LIST_TRAVERSE(headp,i,entries) {
++ if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) {
++ found=1;
++ res=i->data;
++ break;
++ }
++ }
++ if (!found) {
++ ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type);
++ }
++ AST_LIST_UNLOCK(headp);
++ }
++
++ return res;
++}
++
++static int add_identifier(int identifier_type,void *data) {
++ struct ast_MYSQL_id *i,*j;
++ struct MYSQLidshead *headp;
++ int maxidentifier=0;
++
++ headp=&_mysql_ids_head;
++ i=NULL;
++ j=NULL;
++
++ if (AST_LIST_LOCK(headp)) {
++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
++ return(-1);
++ } else {
++ i=malloc(sizeof(struct ast_MYSQL_id));
++ AST_LIST_TRAVERSE(headp,j,entries) {
++ if (j->identifier>maxidentifier) {
++ maxidentifier=j->identifier;
++ }
++ }
++ i->identifier=maxidentifier+1;
++ i->identifier_type=identifier_type;
++ i->data=data;
++ AST_LIST_INSERT_HEAD(headp,i,entries);
++ AST_LIST_UNLOCK(headp);
++ }
++ return i->identifier;
++}
++
++static int del_identifier(int identifier,int identifier_type) {
++ struct ast_MYSQL_id *i;
++ struct MYSQLidshead *headp;
++ int found=0;
++
++ headp=&_mysql_ids_head;
++
++ if (AST_LIST_LOCK(headp)) {
++ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
++ } else {
++ AST_LIST_TRAVERSE(headp,i,entries) {
++ if ((i->identifier==identifier) &&
++ (i->identifier_type==identifier_type)) {
++ AST_LIST_REMOVE(headp,i,entries);
++ free(i);
++ found=1;
++ break;
++ }
++ }
++ AST_LIST_UNLOCK(headp);
++ }
++
++ if (found==0) {
++ ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type);
++ return(-1);
++ } else {
++ return(0);
++ }
++}
++
++static int set_asterisk_int(struct ast_channel *chan, char *varname, int id) {
++ if( id>=0 ) {
++ char s[100] = "";
++ snprintf(s, sizeof(s)-1, "%d", id);
++#if EXTRA_LOG
++ ast_log(LOG_WARNING,"MYSQL: setting var '%s' to value '%s'\n",varname,s);
++#endif
++ pbx_builtin_setvar_helper(chan,varname,s);
++ }
++ return id;
++}
++
++static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data) {
++ return set_asterisk_int(chan,varname,add_identifier(identifier_type,data));
++}
++
++static int safe_scan_int( char** data, char* delim, int def ) {
++ char* end;
++ int res = def;
++ char* s = strsep(data,delim);
++ if( s ) {
++ res = strtol(s,&end,10);
++ if (*end) res = def; /* not an integer */
++ }
++ return res;
++}
++
++/* MYSQL operations */
++static int aMYSQL_connect(struct ast_channel *chan, char *data) {
++
++ MYSQL *mysql;
++
++ char *connid_var;
++ char *dbhost;
++ char *dbuser;
++ char *dbpass;
++ char *dbname;
++
++ strsep(&data," "); // eat the first token, we already know it :P
++
++ connid_var=strsep(&data," ");
++ dbhost=strsep(&data," ");
++ dbuser=strsep(&data," ");
++ dbpass=strsep(&data," ");
++ dbname=strsep(&data,"\n");
++
++ if( connid_var && dbhost && dbuser && dbpass && dbname ) {
++ mysql = mysql_init(NULL);
++ if (mysql) {
++ if (mysql_real_connect(mysql,dbhost,dbuser,dbpass,dbname,0,NULL,0)) {
++ add_identifier_and_set_asterisk_int(chan,connid_var,AST_MYSQL_ID_CONNID,mysql);
++ return 0;
++ }
++ else {
++ ast_log(LOG_WARNING,"mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed\n",dbhost,dbuser,dbname);
++ }
++ }
++ else {
++ ast_log(LOG_WARNING,"myslq_init returned NULL\n");
++ }
++ }
++ else {
++ ast_log(LOG_WARNING,"MYSQL(connect is missing some arguments\n");
++ }
++
++ return -1;
++}
++
++static int aMYSQL_query(struct ast_channel *chan, char *data) {
++
++ MYSQL *mysql;
++ MYSQL_RES *mysqlres;
++
++ char *resultid_var;
++ int connid;
++ char *querystring;
++
++ strsep(&data," "); // eat the first token, we already know it :P
++
++ resultid_var = strsep(&data," ");
++ connid = safe_scan_int(&data," ",-1);
++ querystring = strsep(&data,"\n");
++
++ if (resultid_var && (connid>=0) && querystring) {
++ if ((mysql=find_identifier(connid,AST_MYSQL_ID_CONNID))!=NULL) {
++ mysql_query(mysql,querystring);
++ if ((mysqlres=mysql_use_result(mysql))!=NULL) {
++ add_identifier_and_set_asterisk_int(chan,resultid_var,AST_MYSQL_ID_RESID,mysqlres);
++ return 0;
++ }
++ else if( mysql_field_count(mysql)==0 ) {
++ return 0; // See http://dev.mysql.com/doc/mysql/en/mysql_field_count.html
++ }
++ else {
++ ast_log(LOG_WARNING,"aMYSQL_query: mysql_store_result() failed on query %s\n",querystring);
++ }
++ }
++ else {
++ ast_log(LOG_WARNING,"aMYSQL_query: Invalid connection identifier %d passed in aMYSQL_query\n",connid);
++ }
++ }
++ else {
++ ast_log(LOG_WARNING,"aMYSQL_query: missing some arguments\n");
++ }
++
++ return -1;
++}
++
++
++static int aMYSQL_fetch(struct ast_channel *chan, char *data) {
++
++ MYSQL_RES *mysqlres;
++ MYSQL_ROW mysqlrow;
++
++ char *fetchid_var,*s5,*s6;
++ int resultid,numFields,j;
++
++ strsep(&data," "); // eat the first token, we already know it :P
++
++ fetchid_var = strsep(&data," ");
++ resultid = safe_scan_int(&data," ",-1);
++
++ if (fetchid_var && (resultid>=0) ) {
++ if ((mysqlres=find_identifier(resultid,AST_MYSQL_ID_RESID))!=NULL) {
++ /* Grab the next row */
++ if ((mysqlrow=mysql_fetch_row(mysqlres))!=NULL) {
++ numFields=mysql_num_fields(mysqlres);
++ for (j=0;j<numFields;j++) {
++ s5=strsep(&data," ");
++ if (s5==NULL) {
++ ast_log(LOG_WARNING,"ast_MYSQL_fetch: More fields (%d) than variables (%d)\n",numFields,j);
++ break;
++ }
++ s6=mysqlrow[j];
++ pbx_builtin_setvar_helper(chan,s5, s6 ? s6 : "NULL");
++ }
++#ifdef EXTRA_LOG
++ ast_log(LOG_WARNING,"ast_MYSQL_fetch: numFields=%d\n",numFields);
++#endif
++ set_asterisk_int(chan,fetchid_var,1); // try more rows
++ } else {
++#if EXTRA_LOG
++ ast_log(LOG_WARNING,"ast_MYSQL_fetch : EOF\n");
++#endif
++ set_asterisk_int(chan,fetchid_var,0); // no more rows
++ }
++ return 0;
++ }
++ else {
++ ast_log(LOG_WARNING,"aMYSQL_fetch: Invalid result identifier %d passed\n",resultid);
++ }
++ }
++ else {
++ ast_log(LOG_WARNING,"aMYSQL_fetch: missing some arguments\n");
++ }
++
++ return -1;
++}
++
++static int aMYSQL_clear(struct ast_channel *chan, char *data) {
++
++ MYSQL_RES *mysqlres;
++
++ int id;
++ strsep(&data," "); // eat the first token, we already know it :P
++ id = safe_scan_int(&data," \n",-1);
++ if ((mysqlres=find_identifier(id,AST_MYSQL_ID_RESID))==NULL) {
++ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aMYSQL_clear\n",id);
++ } else {
++ mysql_free_result(mysqlres);
++ del_identifier(id,AST_MYSQL_ID_RESID);
++ }
++
++ return 0;
++}
++
++static int aMYSQL_disconnect(struct ast_channel *chan, char *data) {
++
++ MYSQL *mysql;
++ int id;
++ strsep(&data," "); // eat the first token, we already know it :P
++
++ id = safe_scan_int(&data," \n",-1);
++ if ((mysql=find_identifier(id,AST_MYSQL_ID_CONNID))==NULL) {
++ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aMYSQL_disconnect\n",id);
++ } else {
++ mysql_close(mysql);
++ del_identifier(id,AST_MYSQL_ID_CONNID);
++ }
++
++ return 0;
++}
++
++static int MYSQL_exec(struct ast_channel *chan, void *data)
++{
++ struct localuser *u;
++ int result;
++ char sresult[10];
++
++#if EXTRA_LOG
++ fprintf(stderr,"MYSQL_exec: data=%s\n",(char*)data);
++#endif
++
++ if (!data) {
++ ast_log(LOG_WARNING, "APP_MYSQL requires an argument (see manual)\n");
++ return -1;
++ }
++
++ LOCAL_USER_ADD(u);
++ result=0;
++
++ ast_mutex_lock(&_mysql_mutex);
++
++ if (strncasecmp("connect",data,strlen("connect"))==0) {
++ result=aMYSQL_connect(chan,ast_strdupa(data));
++ } else if (strncasecmp("query",data,strlen("query"))==0) {
++ result=aMYSQL_query(chan,ast_strdupa(data));
++ } else if (strncasecmp("fetch",data,strlen("fetch"))==0) {
++ result=aMYSQL_fetch(chan,ast_strdupa(data));
++ } else if (strncasecmp("clear",data,strlen("clear"))==0) {
++ result=aMYSQL_clear(chan,ast_strdupa(data));
++ } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) {
++ result=aMYSQL_disconnect(chan,ast_strdupa(data));
++ } else {
++ ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n",(char *)data);
++ result=-1;
++ }
++
++ ast_mutex_unlock(&_mysql_mutex);
++
++ LOCAL_USER_REMOVE(u);
++ snprintf(sresult, sizeof(sresult), "%d", result);
++ pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult);
++ return 0;
++}
++
++int unload_module(void)
++{
++ STANDARD_HANGUP_LOCALUSERS;
++ return ast_unregister_application(app);
++}
++
++int load_module(void)
++{
++ struct MYSQLidshead *headp = &_mysql_ids_head;
++ AST_LIST_HEAD_INIT(headp);
++ return ast_register_application(app, MYSQL_exec, synopsis, descrip);
++}
++
++char *description(void)
++{
++ return tdesc;
++}
++
++int usecount(void)
++{
++ int res;
++ STANDARD_USECOUNT(res);
++ return res;
++}
++
++char *key()
++{
++ return ASTERISK_GPL_KEY;
++}
--- /dev/null
+diff -Nru asterisk-1.2.14.org/cdr/cdr_mysql.c asterisk-1.2.14/cdr/cdr_mysql.c
+--- asterisk-1.2.14.org/cdr/cdr_mysql.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/cdr/cdr_mysql.c 2006-12-27 09:02:18.000000000 +0100
+@@ -0,0 +1,493 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * MySQL CDR logger
++ *
++ * James Sharp <jsharp@psychoses.org>
++ *
++ * Modified August 2003
++ * Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com>
++ *
++ * Modified August 6, 2005
++ * Joseph Benden <joe@thrallingpenguin.com>
++ * Added mysql connection timeout parameter
++ * Added an automatic reconnect as to not lose a cdr record
++ * Cleaned up the original code to match the coding guidelines
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License.
++ *
++ */
++
++#include <sys/types.h>
++
++#include <stdio.h>
++#include <string.h>
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <time.h>
++
++#include <mysql.h>
++#include <errmsg.h>
++
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <errno.h>
++
++#include <asterisk/config.h>
++#include <asterisk/options.h>
++#include <asterisk/channel.h>
++#include <asterisk/cdr.h>
++#include <asterisk/module.h>
++#include <asterisk/logger.h>
++#include <asterisk/cli.h>
++
++#define DATE_FORMAT "%Y-%m-%d %T"
++
++static char *desc = "MySQL CDR Backend";
++static char *name = "mysql";
++static char *config = "cdr_mysql.conf";
++static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL;
++static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbtable_alloc = 0;
++static int dbport = 0;
++static int connected = 0;
++static time_t connect_time = 0;
++static int records = 0;
++static int totalrecords = 0;
++static int userfield = 0;
++static unsigned int timeout = 0;
++
++AST_MUTEX_DEFINE_STATIC(mysql_lock);
++
++static MYSQL mysql;
++
++static char cdr_mysql_status_help[] =
++"Usage: cdr mysql status\n"
++" Shows current connection status for cdr_mysql\n";
++
++static int handle_cdr_mysql_status(int fd, int argc, char *argv[])
++{
++ if (connected) {
++ char status[256], status2[100] = "";
++ int ctime = time(NULL) - connect_time;
++ if (dbport)
++ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, hostname, dbport);
++ else if (dbsock)
++ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
++ else
++ snprintf(status, 255, "Connected to %s@%s", dbname, hostname);
++
++ if (dbuser && *dbuser)
++ snprintf(status2, 99, " with username %s", dbuser);
++ if (dbtable && *dbtable)
++ snprintf(status2, 99, " using table %s", dbtable);
++ if (ctime > 31536000) {
++ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 86400) {
++ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 3600) {
++ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 60) {
++ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
++ } else {
++ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
++ }
++ if (records == totalrecords)
++ ast_cli(fd, " Wrote %d records since last restart.\n", totalrecords);
++ else
++ ast_cli(fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
++ return RESULT_SUCCESS;
++ } else {
++ ast_cli(fd, "Not currently connected to a MySQL server.\n");
++ return RESULT_FAILURE;
++ }
++}
++
++static struct ast_cli_entry cdr_mysql_status_cli =
++ { { "cdr", "mysql", "status", NULL },
++ handle_cdr_mysql_status, "Show connection status of cdr_mysql",
++ cdr_mysql_status_help, NULL };
++
++static int mysql_log(struct ast_cdr *cdr)
++{
++ struct tm tm;
++ struct timeval tv;
++ struct localuser *u;
++ char *userfielddata = NULL;
++ char sqlcmd[2048], timestr[128];
++ char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
++ int retries = 5;
++#ifdef MYSQL_LOGUNIQUEID
++ char *uniqueid = NULL;
++#endif
++
++ ast_mutex_lock(&mysql_lock);
++
++ memset(sqlcmd, 0, 2048);
++
++ localtime_r(&cdr->start.tv_sec, &tm);
++ strftime(timestr, 128, DATE_FORMAT, &tm);
++
++db_reconnect:
++ if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
++ /* Attempt to connect */
++ mysql_init(&mysql);
++ /* Add option to quickly timeout the connection */
++ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
++ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
++ }
++ if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
++ connected = 1;
++ connect_time = time(NULL);
++ records = 0;
++ } else {
++ ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s.\n", hostname);
++ connected = 0;
++ }
++ } else {
++ /* Long connection - ping the server */
++ int error;
++ if ((error = mysql_ping(&mysql))) {
++ connected = 0;
++ records = 0;
++ switch (error) {
++ case CR_SERVER_GONE_ERROR:
++ case CR_SERVER_LOST:
++ ast_log(LOG_ERROR, "cdr_mysql: Server has gone away. Attempting to reconnect.\n");
++ break;
++ default:
++ ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
++ }
++ retries--;
++ if (retries)
++ goto db_reconnect;
++ else
++ ast_log(LOG_ERROR, "cdr_mysql: Retried to connect fives times, giving up.\n");
++ }
++ }
++
++ /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */
++ /* WARNING: This code previously used mysql_real_escape_string, but the use of said function
++ requires an active connection to a database. If we are not connected, then this function
++ cannot be used. This is a problem since we need to store off the SQL statement into our
++ spool file for later restoration.
++ So the question is, what's the best way to handle this? This works for now.
++ */
++ if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
++ mysql_escape_string(clid, cdr->clid, strlen(cdr->clid));
++ if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
++ mysql_escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext));
++ if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
++ mysql_escape_string(channel, cdr->channel, strlen(cdr->channel));
++ if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
++ mysql_escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
++ if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
++ mysql_escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp));
++ if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
++ mysql_escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata));
++#ifdef MYSQL_LOGUNIQUEID
++ if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
++ mysql_escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
++#endif
++ if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL))
++ mysql_escape_string(userfielddata, cdr->userfield, strlen(cdr->userfield));
++
++ /* Check for all alloca failures above at once */
++#ifdef MYSQL_LOGUNIQUEID
++ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) {
++#else
++ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata)) {
++#endif
++ ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n");
++ ast_mutex_unlock(&mysql_lock);
++ return -1;
++ }
++
++ ast_log(LOG_DEBUG, "cdr_mysql: inserting a CDR record.\n");
++
++ if (userfield && userfielddata) {
++#ifdef MYSQL_LOGUNIQUEID
++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid, userfielddata);
++#else
++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, userfielddata);
++#endif
++ } else {
++#ifdef MYSQL_LOGUNIQUEID
++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid);
++#else
++ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode);
++#endif
++ }
++
++ ast_log(LOG_DEBUG, "cdr_mysql: SQL command as follows: %s\n", sqlcmd);
++
++ if (connected) {
++ if (mysql_real_query(&mysql, sqlcmd, strlen(sqlcmd))) {
++ ast_log(LOG_ERROR, "mysql_cdr: Failed to insert into database: (%d) %s", mysql_errno(&mysql), mysql_error(&mysql));
++ connected = 0;
++ } else {
++ records++;
++ totalrecords++;
++ }
++ }
++ ast_mutex_unlock(&mysql_lock);
++ return 0;
++}
++
++char *description(void)
++{
++ return desc;
++}
++
++static int my_unload_module(void)
++{
++ ast_cli_unregister(&cdr_mysql_status_cli);
++ if (connected) {
++ mysql_close(&mysql);
++ connected = 0;
++ records = 0;
++ }
++ if (hostname && hostname_alloc) {
++ free(hostname);
++ hostname = NULL;
++ hostname_alloc = 0;
++ }
++ if (dbname && dbname_alloc) {
++ free(dbname);
++ dbname = NULL;
++ dbname_alloc = 0;
++ }
++ if (dbuser && dbuser_alloc) {
++ free(dbuser);
++ dbuser = NULL;
++ dbuser_alloc = 0;
++ }
++ if (dbsock && dbsock_alloc) {
++ free(dbsock);
++ dbsock = NULL;
++ dbsock_alloc = 0;
++ }
++ if (dbtable && dbtable_alloc) {
++ free(dbtable);
++ dbtable = NULL;
++ dbtable_alloc = 0;
++ }
++ if (password && password_alloc) {
++ free(password);
++ password = NULL;
++ password_alloc = 0;
++ }
++ dbport = 0;
++ ast_cdr_unregister(name);
++ return 0;
++}
++
++static int my_load_module(void)
++{
++ int res;
++ struct ast_config *cfg;
++ struct ast_variable *var;
++ char *tmp;
++
++ cfg = ast_config_load(config);
++ if (!cfg) {
++ ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
++ return 0;
++ }
++
++ var = ast_variable_browse(cfg, "global");
++ if (!var) {
++ /* nothing configured */
++ return 0;
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "hostname");
++ if (tmp) {
++ hostname = malloc(strlen(tmp) + 1);
++ if (hostname != NULL) {
++ hostname_alloc = 1;
++ strcpy(hostname, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL server hostname not specified. Assuming localhost\n");
++ hostname = "localhost";
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "dbname");
++ if (tmp) {
++ dbname = malloc(strlen(tmp) + 1);
++ if (dbname != NULL) {
++ dbname_alloc = 1;
++ strcpy(dbname, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL database not specified. Assuming asteriskcdrdb\n");
++ dbname = "asteriskcdrdb";
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "user");
++ if (tmp) {
++ dbuser = malloc(strlen(tmp) + 1);
++ if (dbuser != NULL) {
++ dbuser_alloc = 1;
++ strcpy(dbuser, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL database user not specified. Assuming root\n");
++ dbuser = "root";
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "sock");
++ if (tmp) {
++ dbsock = malloc(strlen(tmp) + 1);
++ if (dbsock != NULL) {
++ dbsock_alloc = 1;
++ strcpy(dbsock, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL database sock file not specified. Using default\n");
++ dbsock = NULL;
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "table");
++ if (tmp) {
++ dbtable = malloc(strlen(tmp) + 1);
++ if (dbtable != NULL) {
++ dbtable_alloc = 1;
++ strcpy(dbtable, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_NOTICE, "MySQL database table not specified. Assuming \"cdr\"\n");
++ dbtable = "cdr";
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "password");
++ if (tmp) {
++ password = malloc(strlen(tmp) + 1);
++ if (password != NULL) {
++ password_alloc = 1;
++ strcpy(password, tmp);
++ } else {
++ ast_log(LOG_ERROR, "Out of memory error.\n");
++ return -1;
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL database password not specified. Assuming blank\n");
++ password = "";
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "port");
++ if (tmp) {
++ if (sscanf(tmp, "%d", &dbport) < 1) {
++ ast_log(LOG_WARNING, "Invalid MySQL port number. Using default\n");
++ dbport = 0;
++ }
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "timeout");
++ if (tmp) {
++ if (sscanf(tmp,"%d", &timeout) < 1) {
++ ast_log(LOG_WARNING, "Invalid MySQL timeout number. Using default\n");
++ timeout = 0;
++ }
++ }
++
++ tmp = ast_variable_retrieve(cfg, "global", "userfield");
++ if (tmp) {
++ if (sscanf(tmp, "%d", &userfield) < 1) {
++ ast_log(LOG_WARNING, "Invalid MySQL configurtation file\n");
++ userfield = 0;
++ }
++ }
++
++ ast_config_destroy(cfg);
++
++ ast_log(LOG_DEBUG, "cdr_mysql: got hostname of %s\n", hostname);
++ ast_log(LOG_DEBUG, "cdr_mysql: got port of %d\n", dbport);
++ ast_log(LOG_DEBUG, "cdr_mysql: got a timeout of %d\n", timeout);
++ if (dbsock)
++ ast_log(LOG_DEBUG, "cdr_mysql: got sock file of %s\n", dbsock);
++ ast_log(LOG_DEBUG, "cdr_mysql: got user of %s\n", dbuser);
++ ast_log(LOG_DEBUG, "cdr_mysql: got dbname of %s\n", dbname);
++ ast_log(LOG_DEBUG, "cdr_mysql: got password of %s\n", password);
++
++ mysql_init(&mysql);
++
++ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
++ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
++ }
++
++ if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
++ ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname);
++ connected = 0;
++ records = 0;
++ } else {
++ ast_log(LOG_DEBUG, "Successfully connected to MySQL database.\n");
++ connected = 1;
++ records = 0;
++ connect_time = time(NULL);
++ }
++
++ res = ast_cdr_register(name, desc, mysql_log);
++ if (res) {
++ ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
++ } else {
++ res = ast_cli_register(&cdr_mysql_status_cli);
++ }
++
++ return res;
++}
++
++int load_module(void)
++{
++ return my_load_module();
++}
++
++int unload_module(void)
++{
++ return my_unload_module();
++}
++
++int reload(void)
++{
++ int ret;
++
++ ast_mutex_lock(&mysql_lock);
++ my_unload_module();
++ ret = my_load_module();
++ ast_mutex_unlock(&mysql_lock);
++
++ return ret;
++}
++
++int usecount(void)
++{
++ /* Simplistic use count */
++ if (ast_mutex_trylock(&mysql_lock)) {
++ return 1;
++ } else {
++ ast_mutex_unlock(&mysql_lock);
++ return 0;
++ }
++}
++
++char *key()
++{
++ return ASTERISK_GPL_KEY;
++}
+diff -Nru asterisk-1.2.14.org/configs/cdr_mysql.conf.sample asterisk-1.2.14/configs/cdr_mysql.conf.sample
+--- asterisk-1.2.14.org/configs/cdr_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/configs/cdr_mysql.conf.sample 2006-12-27 09:02:18.000000000 +0100
+@@ -0,0 +1,21 @@
++;
++; Note - if the database server is hosted on the same machine as the
++; asterisk server, you can achieve a local Unix socket connection by
++; setting hostname=localhost
++;
++; port and sock are both optional parameters. If hostname is specified
++; and is not "localhost", then cdr_mysql will attempt to connect to the
++; port specified or use the default port. If hostname is not specified
++; or if hostname is "localhost", then cdr_mysql will attempt to connect
++; to the socket file specified by sock or otherwise use the default socket
++; file.
++;
++;[global]
++;hostname=database.host.name
++;dbname=asteriskcdrdb
++;table=cdr
++;password=password
++;user=asteriskcdruser
++;port=3306
++;sock=/tmp/mysql.sock
++;userfield=1
--- /dev/null
+diff -Nru asterisk-1.2.14.org/include/asterisk/compat.h asterisk-1.2.14/include/asterisk/compat.h
+--- asterisk-1.2.14.org/include/asterisk/compat.h 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/include/asterisk/compat.h 2006-12-27 09:07:28.000000000 +0100
+@@ -75,7 +75,9 @@
+ #define HAVE_STRTOQ
+
+ #ifdef _BSD_SOURCE
++#ifndef __UCLIBC__
+ #define HAVE_GETLOADAVG
++#endif /* __UCLIBC__ */
+ #endif
+
+ #ifdef __linux__
--- /dev/null
+diff -Nru asterisk-1.2.14.org/apps/Makefile asterisk-1.2.14/apps/Makefile
+--- asterisk-1.2.14.org/apps/Makefile 2006-04-30 15:38:22.000000000 +0200
++++ asterisk-1.2.14/apps/Makefile 2006-12-27 09:08:57.000000000 +0100
+@@ -45,7 +45,7 @@
+ #APPS+=app_rpt.so
+
+ ifndef WITHOUT_ZAPTEL
+-ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h),)
++ifneq ($(wildcard $(STAGING_DIR)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h),)
+ APPS+=app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so app_page.so
+ endif
+ endif # WITHOUT_ZAPTEL
+@@ -83,6 +83,9 @@
+ #CFLAGS+=-DEXTENDED_ODBC_STORAGE
+ # See doc/README.odbcstorage for more information
+
++CFLAGS += $(EXTRA_CFLAGS)
++APPS += $(EXTRA_APP_MODULES)
++
+ all: $(APPS)
+
+ clean:
+@@ -102,14 +105,17 @@
+ app_curl.so: app_curl.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(CURLLIBS)
+
++app_sql_mysql.so: app_sql_mysql.o
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient
++
+ app_sql_postgres.o: app_sql_postgres.c
+ $(CC) -pipe -I$(CROSS_COMPILE_TARGET)/usr/local/pgsql/include -I$(CROSS_COMPILE_TARGET)/usr/include/postgresql $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c
+
+ app_sql_postgres.so: app_sql_postgres.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -L/usr/local/pgsql/lib -lpq
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq
+
+ app_sql_odbc.so: app_sql_odbc.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc
+
+ ifeq (SunOS,$(shell uname))
+ app_chanspy.so: app_chanspy.o
--- /dev/null
+diff -Nru asterisk-1.2.14.org/cdr/Makefile asterisk-1.2.14/cdr/Makefile
+--- asterisk-1.2.14.org/cdr/Makefile 2006-11-16 21:29:28.000000000 +0100
++++ asterisk-1.2.14/cdr/Makefile 2006-12-27 09:10:57.000000000 +0100
+@@ -111,6 +111,9 @@
+ MODS+=cdr_sqlite.so
+ endif
+
++CFLAGS += $(EXTRA_CFLAGS)
++MODS += $(EXTRA_CDR_MODULES)
++
+ all: depend $(MODS)
+
+ install: all
+@@ -127,16 +130,19 @@
+ endif
+
+ cdr_odbc.so: cdr_odbc.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc $(MLFLAGS)
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc $(MLFLAGS)
+
+ cdr_tds.so: cdr_tds.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -ltds $(MLFLAGS)
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -ltds $(MLFLAGS)
++
++cdr_mysql.so: cdr_mysql.o
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz $(MLFLAGS)
+
+ cdr_pgsql.so: cdr_pgsql.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lpq -lz $(MLFLAGS)
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq -lz $(MLFLAGS)
+
+ cdr_sqlite.so: cdr_sqlite.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lsqlite $(MLFLAGS)
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite $(MLFLAGS)
+
+ depend: .depend
+
--- /dev/null
+diff -Nru asterisk-1.2.14.org/channels/Makefile asterisk-1.2.14/channels/Makefile
+--- asterisk-1.2.14.org/channels/Makefile 2006-08-17 23:57:19.000000000 +0200
++++ asterisk-1.2.14/channels/Makefile 2006-12-27 09:12:28.000000000 +0100
+@@ -110,7 +110,7 @@
+ endif
+
+ ifndef WITHOUT_ZAPTEL
+-ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/pkg/include/zaptel.h),)
++ifneq ($(wildcard $(STAGING_DIR)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/pkg/include/zaptel.h),)
+ ifeq (${OSARCH},NetBSD)
+ SOLINK+=-L$(CROSS_COMPILE_TARGET)/usr/pkg/lib
+ endif
+@@ -151,6 +151,9 @@
+
+ #CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)")
+
++CFLAGS += $(EXTRA_CFLAGS)
++CHANNEL_LIBS += $(EXTRA_CHAN_MODULES)
++
+ all: depend $(CHANNEL_LIBS)
+
+ clean:
+@@ -158,7 +161,7 @@
+ rm -f busy.h ringtone.h gentone gentone-ulaw
+
+ %.so : %.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${LIBS}
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB}
+
+ ifneq ($(wildcard .depend),)
+ include .depend
+@@ -204,7 +207,7 @@
+ $(CC) -c $(CFLAGS) -o chan_zap.o chan_zap.c
+
+ chan_zap.so: chan_zap.o
+- $(CC) $(SOLINK) -o $@ $< $(ZAPPRI) $(ZAPR2) -ltonezone
++ $(CC) $(SOLINK) -o $@ $< $(ZAPPRI) $(ZAPR2) $(EXTRA_LDFLAGS) -ltonezone
+
+ chan_sip.so: chan_sip.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} chan_sip.o ${CYGSOLIB}
--- /dev/null
+diff -Nru asterisk-1.2.14.org/codecs/gsm/Makefile asterisk-1.2.14/codecs/gsm/Makefile
+--- asterisk-1.2.14.org/codecs/gsm/Makefile 2006-07-24 19:05:56.000000000 +0200
++++ asterisk-1.2.14/codecs/gsm/Makefile 2006-12-27 09:14:51.000000000 +0100
+@@ -241,6 +241,8 @@
+ ifneq ($(shell uname -m),armv4l)
+ ifneq ($(shell uname -m),sparc64)
+ ifneq (${PROC},arm)
++ifneq (${PROC},mipsel)
++ifneq (${PROC},mips)
+ ifneq ($(shell uname -m), parisc)
+ ifneq ($(shell uname -m),s390)
+ GSM_SOURCES+= $(SRC)/k6opt.s
+@@ -255,6 +257,8 @@
+ endif
+ endif
+ endif
++endif
++endif
+
+ TOAST_SOURCES = $(SRC)/toast.c \
+ $(SRC)/toast_lin.c \
+@@ -308,6 +312,8 @@
+ ifneq ($(shell uname -m), alpha)
+ ifneq ($(shell uname -m), sparc64)
+ ifneq ($(shell uname -m), armv4l)
++ifneq (${PROC}, mipsel)
++ifneq (${PROC}, mips)
+ ifneq ($(shell uname -m), parisc)
+ ifneq ($(shell uname -m),s390)
+ GSM_OBJECTS+= $(SRC)/k6opt.o
+@@ -321,6 +327,8 @@
+ endif
+ endif
+ endif
++endif
++endif
+
+ TOAST_OBJECTS = $(SRC)/toast.o \
+ $(SRC)/toast_lin.o \
--- /dev/null
+diff -Nru asterisk-1.2.14.org/codecs/Makefile asterisk-1.2.14/codecs/Makefile
+--- asterisk-1.2.14.org/codecs/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/codecs/Makefile 2006-12-27 09:15:57.000000000 +0100
+@@ -72,6 +72,9 @@
+ codec_adpcm.so codec_ulaw.so codec_alaw.so codec_a_mu.so \
+ codec_g726.so
+
++CFLAGS += $(EXTRA_CFLAGS)
++CODECS += $(EXTRA_CODEC_MODULES)
++
+ all: depend $(CODECS)
+
+ clean:
--- /dev/null
+diff -Nru asterisk-1.2.14.org/Makefile asterisk-1.2.14/Makefile
+--- asterisk-1.2.14.org/Makefile 2006-12-11 22:55:43.000000000 +0100
++++ asterisk-1.2.14/Makefile 2006-12-27 09:16:50.000000000 +0100
+@@ -356,16 +356,6 @@
+ netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
+ cryptostub.o
+
+-ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),)
+- OBJS+= poll.o
+- ASTCFLAGS+=-DPOLLCOMPAT
+-endif
+-
+-ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/dlfcn.h),)
+- OBJS+= dlfcn.o
+- ASTCFLAGS+=-DDLFCNCOMPAT
+-endif
+-
+ ifeq ($(OSARCH),Linux)
+ LIBS+=-ldl -lpthread -lncurses -lm -lresolv #-lnjamd
+ else
+@@ -420,8 +410,6 @@
+ HAVEDOT=no
+ endif
+
+-LIBS+=-lssl
+-
+ _all: all
+ @echo " +--------- Asterisk Build Complete ---------+"
+ @echo " + Asterisk has successfully been built, but +"
+@@ -447,12 +435,12 @@
+ cd editline && unset CFLAGS LIBS && CFLAGS="$(OPTIMIZE)" ./configure ; \
+
+ editline/libedit.a: FORCE
+- cd editline && unset CFLAGS LIBS && test -f config.h || CFLAGS="$(OPTIMIZE)" ./configure
++ cd editline && unset CFLAGS LIBS && test -f config.h || CFLAGS="$(OPTIMIZE) $(EXTRA_CFLAGS)" LDFLAGS="$(EXTRA_LDFLAGS)" ./configure
+ $(MAKE) -C editline libedit.a
+
+ db1-ast/libdb1.a: FORCE
+ @if [ -d db1-ast ]; then \
+- $(MAKE) -C db1-ast libdb1.a ; \
++ $(MAKE) OORG="$(OPTIMIZE)" -C db1-ast libdb1.a ; \
+ else \
+ echo "You need to do a cvs update -d not just cvs update"; \
+ exit 1; \
+@@ -530,7 +518,7 @@
+ fi
+ rm -f include/asterisk/build.h.tmp
+ $(CC) -c -o buildinfo.o $(CFLAGS) buildinfo.c
+- $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(LIBS)
++ $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(EXTRA_LDFLAGS) $(LIBS)
+
+ muted: muted.o
+ $(CC) $(AUDIO_LIBS) -o muted muted.o
--- /dev/null
+diff -Nru asterisk-1.2.14.org/pbx/Makefile asterisk-1.2.14/pbx/Makefile
+--- asterisk-1.2.14.org/pbx/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/pbx/Makefile 2006-12-27 09:17:58.000000000 +0100
+@@ -38,6 +38,9 @@
+
+ KDE_CONSOLE_OBJS=pbx_kdeconsole_main.o pbx_kdeconsole.o
+
++CFLAGS += $(EXTRA_CFLAGS)
++PBX_LIBS += $(EXTRA_PBX_MODULES)
++
+ all: depend $(PBX_LIBS)
+
+ clean:
+@@ -59,7 +62,7 @@
+ $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
+
+ pbx_dundi.so: dundi-parser.o pbx_dundi.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o -lz ${CYGSOLIB}
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o $(EXTRA_LDFLAGS) -lz ${CYGSOLIB}
+
+ %.moc : %.h
+ $(MOC) $< -o $@
--- /dev/null
+diff -Nru asterisk-1.2.14.org/res/Makefile asterisk-1.2.14/res/Makefile
+--- asterisk-1.2.14.org/res/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/res/Makefile 2006-12-27 09:18:45.000000000 +0100
+@@ -55,7 +55,7 @@
+ CFLAGS+=
+
+ ifndef WITHOUT_ZAPTEL
+-ifneq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h),)
++ifneq ($(wildcard $(STAGING_DIR)/usr/include/linux/zaptel.h)$(wildcard $(CROSS_COMPILE_TARGET)/usr/local/include/zaptel.h),)
+ CFLAGS+=-DZAPATA_MOH
+ endif
+ endif # WITHOUT_ZAPTEL
+@@ -69,6 +69,9 @@
+ CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC
+ endif
+
++CFLAGS += $(EXTRA_CFLAGS)
++MODS += $(EXTRA_RES_MODULES)
++
+ all: depend $(MODS)
+
+ install: all
+@@ -89,7 +92,7 @@
+ fi
+
+ res_crypto.so: res_crypto.o
+- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(CRYPTO_LIBS)
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) $(CRYPTO_LIBS)
+
+ clean:
+ rm -f *.so *.o .depend
+@@ -109,6 +112,12 @@
+ res_config_odbc.so: res_config_odbc.o
+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB}
+
++res_config_mysql.so: res_config_mysql.o
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz
++
++res_sqlite.so: res_sqlite.o
++ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite
++
+ ifneq ($(wildcard .depend),)
+ include .depend
+ endif
--- /dev/null
+diff -Nru asterisk-1.2.14.org/configs/res_mysql.conf.sample asterisk-1.2.14/configs/res_mysql.conf.sample
+--- asterisk-1.2.14.org/configs/res_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/configs/res_mysql.conf.sample 2006-12-27 09:19:45.000000000 +0100
+@@ -0,0 +1,15 @@
++;
++; Sample configuration for res_config_mysql.c
++;
++; The value of dbhost may be either a hostname or an IP address.
++; If dbhost is commented out or the string "localhost", a connection
++; to the local host is assumed and dbsock is used instead of TCP/IP
++; to connect to the server.
++;
++[general]
++;dbhost = 127.0.0.1
++;dbname = asterisk
++;dbuser = myuser
++;dbpass = mypass
++;dbport = 3306
++;dbsock = /tmp/mysql.sock
+diff -Nru asterisk-1.2.14.org/res/res_config_mysql.c asterisk-1.2.14/res/res_config_mysql.c
+--- asterisk-1.2.14.org/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-1.2.14/res/res_config_mysql.c 2006-12-27 09:19:45.000000000 +0100
+@@ -0,0 +1,675 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Copyright (C) 1999-2005, Digium, Inc.
++ *
++ * Mark Spencer <markster@digium.com> - Asterisk Author
++ * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
++ *
++ * res_config_mysql.c <mysql plugin for RealTime configuration engine>
++ *
++ * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
++ *
++ * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
++ * in extconfig.conf (bug #4973)
++ *
++ * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
++ * what really happened.
++ *
++ * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
++ * in realtime_multi_mysql function which caused segfault.
++ *
++ * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
++ *
++ * v1.5.1 - (01-26-05) - Added better(?) locking stuff
++ *
++ * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
++ * - Added in extra locking provided by georg (bug #3248)
++ *
++ * v1.4 - (12-02-04) - Added realtime_multi_mysql function
++ * This function will return an ast_config with categories,
++ * unlike standard realtime_mysql which only returns
++ * a linked list of ast_variables
++ *
++ * v1.3 - (12-01-04) - Added support other operators
++ * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
++ *
++ * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
++ * Code beautification (doc/CODING-GUIDELINES)
++ */
++
++#include <asterisk/channel.h>
++#include <asterisk/logger.h>
++#include <asterisk/config.h>
++#include <asterisk/module.h>
++#include <asterisk/lock.h>
++#include <asterisk/options.h>
++#include <asterisk/cli.h>
++#include <asterisk/utils.h>
++#include <stdlib.h>
++#include <string.h>
++#include <mysql.h>
++#include <mysql_version.h>
++#include <errmsg.h>
++
++static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
++
++AST_MUTEX_DEFINE_STATIC(mysql_lock);
++#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
++MYSQL mysql;
++static char dbhost[50];
++static char dbuser[50];
++static char dbpass[50];
++static char dbname[50];
++static char dbsock[50];
++static int dbport;
++static int connected;
++static time_t connect_time;
++
++static int parse_config(void);
++static int mysql_reconnect(const char *database);
++static int realtime_mysql_status(int fd, int argc, char **argv);
++
++STANDARD_LOCAL_USER;
++
++LOCAL_USER_DECL;
++
++static char cli_realtime_mysql_status_usage[] =
++"Usage: realtime mysql status\n"
++" Shows connection information for the MySQL RealTime driver\n";
++
++static struct ast_cli_entry cli_realtime_mysql_status = {
++ { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
++ "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
++
++static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
++{
++ MYSQL_RES *result;
++ MYSQL_ROW row;
++ MYSQL_FIELD *fields;
++ int numFields, i;
++ char sql[256];
++ char *stringp;
++ char *chunk;
++ char *op;
++ const char *newparam, *newval;
++ struct ast_variable *var=NULL, *prev=NULL;
++
++ if(!table) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
++ return NULL;
++ }
++
++ /* Get the first parameter and first value in our list of passed paramater/value pairs */
++ newparam = va_arg(ap, const char *);
++ newval = va_arg(ap, const char *);
++ if(!newparam || !newval) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
++ mysql_close(&mysql);
++ return NULL;
++ }
++
++ /* Create the first part of the query using the first parameter/value pairs we just extracted
++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
++
++ if(!strchr(newparam, ' ')) op = " ="; else op = "";
++
++ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
++ while((newparam = va_arg(ap, const char *))) {
++ newval = va_arg(ap, const char *);
++ if(!strchr(newparam, ' ')) op = " ="; else op = "";
++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
++ }
++ va_end(ap);
++
++ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
++
++ /* We now have our complete statement; Lets connect to the server and execute it. */
++ ast_mutex_lock(&mysql_lock);
++ if(!mysql_reconnect(database)) {
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if(mysql_real_query(&mysql, sql, strlen(sql))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if((result = mysql_store_result(&mysql))) {
++ numFields = mysql_num_fields(result);
++ fields = mysql_fetch_fields(result);
++
++ while((row = mysql_fetch_row(result))) {
++ for(i = 0; i < numFields; i++) {
++ stringp = row[i];
++ while(stringp) {
++ chunk = strsep(&stringp, ";");
++ if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
++ if(prev) {
++ prev->next = ast_variable_new(fields[i].name, chunk);
++ if (prev->next) {
++ prev = prev->next;
++ }
++ } else {
++ prev = var = ast_variable_new(fields[i].name, chunk);
++ }
++ }
++ }
++ }
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
++ }
++
++ ast_mutex_unlock(&mysql_lock);
++ mysql_free_result(result);
++
++ return var;
++}
++
++static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
++{
++ MYSQL_RES *result;
++ MYSQL_ROW row;
++ MYSQL_FIELD *fields;
++ int numFields, i;
++ char sql[256];
++ const char *initfield = NULL;
++ char *stringp;
++ char *chunk;
++ char *op;
++ const char *newparam, *newval;
++ struct ast_realloca ra;
++ struct ast_variable *var=NULL;
++ struct ast_config *cfg = NULL;
++ struct ast_category *cat = NULL;
++
++ if(!table) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
++ return NULL;
++ }
++
++ memset(&ra, 0, sizeof(ra));
++
++ cfg = ast_config_new();
++ if (!cfg) {
++ /* If I can't alloc memory at this point, why bother doing anything else? */
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ return NULL;
++ }
++
++ /* Get the first parameter and first value in our list of passed paramater/value pairs */
++ newparam = va_arg(ap, const char *);
++ newval = va_arg(ap, const char *);
++ if(!newparam || !newval) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
++ mysql_close(&mysql);
++ return NULL;
++ }
++
++ initfield = ast_strdupa(newparam);
++ if(initfield && (op = strchr(initfield, ' '))) {
++ *op = '\0';
++ }
++
++ /* Create the first part of the query using the first parameter/value pairs we just extracted
++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
++
++ if(!strchr(newparam, ' ')) op = " ="; else op = "";
++
++ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
++ while((newparam = va_arg(ap, const char *))) {
++ newval = va_arg(ap, const char *);
++ if(!strchr(newparam, ' ')) op = " ="; else op = "";
++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
++ }
++
++ if(initfield) {
++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
++ }
++
++ va_end(ap);
++
++ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
++
++ /* We now have our complete statement; Lets connect to the server and execute it. */
++ ast_mutex_lock(&mysql_lock);
++ if(!mysql_reconnect(database)) {
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if(mysql_real_query(&mysql, sql, strlen(sql))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if((result = mysql_store_result(&mysql))) {
++ numFields = mysql_num_fields(result);
++ fields = mysql_fetch_fields(result);
++
++ while((row = mysql_fetch_row(result))) {
++ var = NULL;
++ cat = ast_category_new("");
++ if(!cat) {
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ continue;
++ }
++ for(i = 0; i < numFields; i++) {
++ stringp = row[i];
++ while(stringp) {
++ chunk = strsep(&stringp, ";");
++ if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
++ if(initfield && !strcmp(initfield, fields[i].name)) {
++ ast_category_rename(cat, chunk);
++ }
++ var = ast_variable_new(fields[i].name, chunk);
++ ast_variable_append(cat, var);
++ }
++ }
++ }
++ ast_category_append(cfg, cat);
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
++ }
++
++ ast_mutex_unlock(&mysql_lock);
++ mysql_free_result(result);
++
++ return cfg;
++}
++
++static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
++{
++ my_ulonglong numrows;
++ char sql[256];
++ const char *newparam, *newval;
++
++ if(!table) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
++ return -1;
++ }
++
++ /* Get the first parameter and first value in our list of passed paramater/value pairs */
++ newparam = va_arg(ap, const char *);
++ newval = va_arg(ap, const char *);
++ if(!newparam || !newval) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
++ mysql_close(&mysql);
++ return -1;
++ }
++
++ /* Create the first part of the query using the first parameter/value pairs we just extracted
++ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
++
++ snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
++ while((newparam = va_arg(ap, const char *))) {
++ newval = va_arg(ap, const char *);
++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
++ }
++ va_end(ap);
++ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
++
++ ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
++
++ /* We now have our complete statement; Lets connect to the server and execute it. */
++ ast_mutex_lock(&mysql_lock);
++ if(!mysql_reconnect(database)) {
++ ast_mutex_unlock(&mysql_lock);
++ return -1;
++ }
++
++ if(mysql_real_query(&mysql, sql, strlen(sql))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
++ ast_mutex_unlock(&mysql_lock);
++ return -1;
++ }
++
++ numrows = mysql_affected_rows(&mysql);
++ ast_mutex_unlock(&mysql_lock);
++
++ ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
++
++ /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
++ * An integer greater than zero indicates the number of rows affected
++ * Zero indicates that no records were updated
++ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
++ */
++
++ if(numrows >= 0)
++ return (int)numrows;
++
++ return -1;
++}
++
++static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
++{
++ MYSQL_RES *result;
++ MYSQL_ROW row;
++ my_ulonglong num_rows;
++ struct ast_config *new;
++ struct ast_variable *cur_v, *new_v;
++ struct ast_category *cur_cat, *new_cat;
++ char sql[250] = "";
++ char last[80] = "";
++ int cat_started = 0;
++ int var_started = 0;
++ int last_cat_metric = 0;
++
++ last[0] = '\0';
++
++ if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
++ return NULL;
++ }
++
++ snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
++
++ ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
++
++ /* We now have our complete statement; Lets connect to the server and execute it. */
++ ast_mutex_lock(&mysql_lock);
++ if(!mysql_reconnect(database)) {
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if(mysql_real_query(&mysql, sql, strlen(sql))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++
++ if((result = mysql_store_result(&mysql))) {
++ num_rows = mysql_num_rows(result);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
++
++ /* There might exist a better way to access the column names other than counting,
++ but I believe that would require another loop that we don't need. */
++
++ while((row = mysql_fetch_row(result))) {
++ if(!strcmp(row[1], "#include")) {
++ if (!ast_config_internal_load(row[2], cfg)) {
++ mysql_free_result(result);
++ ast_mutex_unlock(&mysql_lock);
++ return NULL;
++ }
++ continue;
++ }
++
++ if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
++ cur_cat = ast_category_new(row[0]);
++ if (!cur_cat) {
++ ast_log(LOG_WARNING, "Out of memory!\n");
++ break;
++ }
++ strcpy(last, row[0]);
++ last_cat_metric = atoi(row[3]);
++ ast_category_append(cfg, cur_cat);
++ }
++ new_v = ast_variable_new(row[1], row[2]);
++ ast_variable_append(cur_cat, new_v);
++ }
++ } else {
++ ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
++ }
++
++ mysql_free_result(result);
++ ast_mutex_unlock(&mysql_lock);
++
++ return cfg;
++}
++
++static struct ast_config_engine mysql_engine = {
++ .name = "mysql",
++ .load_func = config_mysql,
++ .realtime_func = realtime_mysql,
++ .realtime_multi_func = realtime_multi_mysql,
++ .update_func = update_mysql
++};
++
++int load_module (void)
++{
++ parse_config();
++
++ ast_mutex_lock(&mysql_lock);
++
++ if(!mysql_reconnect(NULL)) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
++ }
++
++ ast_config_engine_register(&mysql_engine);
++ if(option_verbose) {
++ ast_verbose("MySQL RealTime driver loaded.\n");
++ }
++ ast_cli_register(&cli_realtime_mysql_status);
++
++ ast_mutex_unlock(&mysql_lock);
++
++ return 0;
++}
++
++int unload_module (void)
++{
++ /* Aquire control before doing anything to the module itself. */
++ ast_mutex_lock(&mysql_lock);
++
++ mysql_close(&mysql);
++ ast_cli_unregister(&cli_realtime_mysql_status);
++ ast_config_engine_deregister(&mysql_engine);
++ if(option_verbose) {
++ ast_verbose("MySQL RealTime unloaded.\n");
++ }
++
++ STANDARD_HANGUP_LOCALUSERS;
++
++ /* Unlock so something else can destroy the lock. */
++ ast_mutex_unlock(&mysql_lock);
++
++ return 0;
++}
++
++int reload (void)
++{
++ /* Aquire control before doing anything to the module itself. */
++ ast_mutex_lock(&mysql_lock);
++
++ mysql_close(&mysql);
++ connected = 0;
++ parse_config();
++
++ if(!mysql_reconnect(NULL)) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
++ }
++
++ ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
++
++ /* Done reloading. Release lock so others can now use driver. */
++ ast_mutex_unlock(&mysql_lock);
++
++ return 0;
++}
++
++int parse_config (void)
++{
++ struct ast_config *config;
++ char *s;
++
++ config = ast_config_load(RES_CONFIG_MYSQL_CONF);
++
++ if(config) {
++ if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
++ strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
++ } else {
++ strncpy(dbuser, s, sizeof(dbuser) - 1);
++ }
++
++ if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
++ strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
++ } else {
++ strncpy(dbpass, s, sizeof(dbpass) - 1);
++ }
++
++ if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
++ dbhost[0] = '\0';
++ } else {
++ strncpy(dbhost, s, sizeof(dbhost) - 1);
++ }
++
++ if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
++ strncpy(dbname, "asterisk", sizeof(dbname) - 1);
++ } else {
++ strncpy(dbname, s, sizeof(dbname) - 1);
++ }
++
++ if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
++ dbport = 3306;
++ } else {
++ dbport = atoi(s);
++ }
++
++ if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
++ ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
++ strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
++ } else {
++ strncpy(dbsock, s, sizeof(dbsock) - 1);
++ }
++ }
++ ast_config_destroy(config);
++
++ if(dbhost) {
++ ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
++ ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
++ } else {
++ ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
++ }
++ ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
++ ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
++
++ return 1;
++}
++
++char *description (void)
++{
++ return res_config_mysql_desc;
++}
++
++int usecount (void)
++{
++ /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */
++ if(ast_mutex_trylock(&mysql_lock)) {
++ ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n");
++ return 1;
++ }
++ ast_mutex_unlock(&mysql_lock);
++ return 0;
++}
++
++char *key ()
++{
++ return ASTERISK_GPL_KEY;
++}
++
++static int mysql_reconnect(const char *database)
++{
++ char my_database[50];
++
++ if(!database || ast_strlen_zero(database))
++ ast_copy_string(my_database, dbname, sizeof(my_database));
++ else
++ ast_copy_string(my_database, database, sizeof(my_database));
++
++ /* mutex lock should have been locked before calling this function. */
++
++ if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
++ if(!mysql_init(&mysql)) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
++ connected = 0;
++ return 0;
++ }
++ if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
++ ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
++ connected = 1;
++ connect_time = time(NULL);
++ return 1;
++ } else {
++ ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
++ connected = 0;
++ return 0;
++ }
++ } else {
++ if(mysql_ping(&mysql) != 0) {
++ connected = 0;
++ ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n");
++ ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql));
++ return 0;
++ }
++
++ connected = 1;
++
++ if(mysql_select_db(&mysql, my_database) != 0) {
++ ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database);
++ ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql));
++ return 0;
++ }
++
++ ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
++ return 1;
++ }
++}
++
++static int realtime_mysql_status(int fd, int argc, char **argv)
++{
++ char status[256], status2[100] = "";
++ int ctime = time(NULL) - connect_time;
++
++ if(mysql_reconnect(NULL)) {
++ if(dbhost) {
++ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
++ } else if(dbsock) {
++ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
++ } else {
++ snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
++ }
++
++ if(dbuser && *dbuser) {
++ snprintf(status2, 99, " with username %s", dbuser);
++ }
++
++ if (ctime > 31536000) {
++ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 86400) {
++ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 3600) {
++ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
++ } else if (ctime > 60) {
++ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
++ } else {
++ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
++ }
++
++ return RESULT_SUCCESS;
++ } else {
++ return RESULT_FAILURE;
++ }
++}
--- /dev/null
+diff -Nru asterisk-1.2.14.org/codecs/ilbc/Makefile asterisk-1.2.14/codecs/ilbc/Makefile
+--- asterisk-1.2.14.org/codecs/ilbc/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/codecs/ilbc/Makefile 2006-12-27 09:21:07.000000000 +0100
+@@ -1,5 +1,5 @@
+ ARCH=$(PROC)
+-CFLAGS+=-Wall -O3 -funroll-loops
++CFLAGS+=-Wall $(OPTIMIZE) -funroll-loops
+ ifneq (${OSARCH},CYGWIN)
+ CFLAGS += -fPIC
+ endif
+@@ -15,8 +15,8 @@
+
+
+ $(LIB): $(OBJS)
+- ar cr $(LIB) $(OBJS)
+- ranlib $(LIB)
++ $(AR) cr $(LIB) $(OBJS)
++ $(RANLIB) $(LIB)
+
+ clean:
+ rm -f $(LIB) *.o
--- /dev/null
+diff -Nru asterisk-1.2.14.org/channels/h323/Makefile asterisk-1.2.14/channels/h323/Makefile
+--- asterisk-1.2.14.org/channels/h323/Makefile 2005-11-29 19:24:39.000000000 +0100
++++ asterisk-1.2.14/channels/h323/Makefile 2006-12-27 09:22:09.000000000 +0100
+@@ -30,7 +30,7 @@
+ touch $(SOURCES)
+
+ libchanh323.a: $(OBJS)
+- ar crv $@ $(OBJS)
++ $(AR) crv $@ $(OBJS)
+
+ Makefile.ast: FORCE
+ @echo H323CFLAGS = $(STDCCFLAGS) $(OPTCCFLAGS) $(CFLAGS) >$@.tmp
+diff -Nru asterisk-1.2.14.org/channels/Makefile asterisk-1.2.14/channels/Makefile
+--- asterisk-1.2.14.org/channels/Makefile 2006-08-17 23:57:19.000000000 +0200
++++ asterisk-1.2.14/channels/Makefile 2006-12-27 09:22:09.000000000 +0100
+@@ -15,6 +15,7 @@
+ #
+
+ CHANNEL_LIBS=chan_sip.so chan_agent.so chan_mgcp.so chan_iax2.so chan_local.so chan_skinny.so chan_features.so
++CXXLIBS=-lstdc++
+
+ ifneq (${OSARCH},CYGWIN)
+ # if you really, really want to use these drivers, uncomment the line below
+@@ -228,7 +229,7 @@
+
+ ifeq (${OSARCH},Linux)
+ chan_h323.so: chan_h323.o h323/libchanh323.a h323/Makefile.ast
+- $(CC) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS) -lstdc++
++ $(CC) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS) $(CXXLIBS)
+ else
+ chan_h323.so: chan_h323.o h323/libchanh323.a
+ $(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
+++ /dev/null
-diff -ruN asterisk-1.0.7-old/db1-ast/Makefile asterisk-1.0.7-new/db1-ast/Makefile
---- asterisk-1.0.7-old/db1-ast/Makefile 2004-08-31 18:33:00.000000000 +0200
-+++ asterisk-1.0.7-new/db1-ast/Makefile 2005-03-19 17:38:06.000000000 +0100
-@@ -32,8 +32,8 @@
-
- $(LIBDB): $(OBJS)
- rm -f $@
-- ar cq $@ $(OBJS)
-- ranlib $@
-+ $(AR) cq $@ $(OBJS)
-+ $(RANLIB) $@
-
- $(LIBDBSO): $(SHOBJS)
- $(CC) -Wl,-O1 -Wl,--version-script=libdb.map -Wl,-soname=$(LIBDBSO) -shared -o $@ $^
+++ /dev/null
-diff -ruN asterisk-1.0.7-old/codecs/lpc10/Makefile asterisk-1.0.7-new/codecs/lpc10/Makefile
---- asterisk-1.0.7-old/codecs/lpc10/Makefile 2004-08-31 18:33:00.000000000 +0200
-+++ asterisk-1.0.7-new/codecs/lpc10/Makefile 2005-03-19 17:38:06.000000000 +0100
-@@ -31,6 +31,7 @@
- ifneq ($(PROC),ppc)
- ifneq ($(PROC),x86_64)
- ifneq ($(PROC),alpha)
-+ifneq ($(PROC),mipsel)
- #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only.
- #This works for even old (2.96) versions of gcc and provides a small boost either way.
- #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn.t support it.
-@@ -46,6 +47,7 @@
- endif
- endif
- endif
-+endif
-
- LIB = $(LIB_TARGET_DIR)/liblpc10.a
-
-@@ -62,7 +64,7 @@
-
- $(LIB): $(OBJ)
- $(AR) cr $@ $(OBJ)
-- ranlib $@
-+ $(RANLIB) $@
-
- clean:
- -rm -f *.o $(LIB)
+++ /dev/null
-diff -ruN asterisk-1.0.7-old/stdtime/Makefile asterisk-1.0.7-new/stdtime/Makefile
---- asterisk-1.0.7-old/stdtime/Makefile 2003-11-05 07:19:41.000000000 +0100
-+++ asterisk-1.0.7-new/stdtime/Makefile 2005-03-19 17:38:06.000000000 +0100
-@@ -3,8 +3,8 @@
- all: libtime.a
-
- libtime.a: $(OBJS)
-- ar rv $@ $(OBJS)
-- ranlib $@
-+ $(AR) rv $@ $(OBJS)
-+ $(RANLIB) $@
-
- install:
-
+++ /dev/null
-diff -ruN asterisk-1.0.7-old/channels/chan_iax2.c asterisk-1.0.7-new/channels/chan_iax2.c
---- asterisk-1.0.7-old/channels/chan_iax2.c 2005-10-25 02:06:35.000000000 +0200
-+++ asterisk-1.0.7-new/channels/chan_iax2.c 2005-10-25 04:35:11.000000000 +0200
-@@ -960,7 +960,7 @@
- last++;
- else
- last = s;
-- snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)rand());
-+ snprintf(s2, strlen(s) + 100, "/tmp/%s-%ld", last, (unsigned long)rand());
- res = stat(s, &stbuf);
- if (res < 0) {
- ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
+++ /dev/null
-diff -ruN asterisk-1.0.7-old/dns.c asterisk-1.0.7-new/dns.c
---- asterisk-1.0.7-old/dns.c 2004-06-22 22:11:15.000000000 +0200
-+++ asterisk-1.0.7-new/dns.c 2005-03-19 17:38:06.000000000 +0100
-@@ -153,7 +153,13 @@
-
- #if defined(res_ninit)
- #define HAS_RES_NINIT
--#else
-+#endif
-+
-+#ifdef __UCLIBC__
-+#undef HAS_RES_NINIT
-+#endif
-+
-+#ifndef HAS_RES_NINIT
- AST_MUTEX_DEFINE_STATIC(res_lock);
- #if 0
- #warning "Warning, res_ninit is missing... Could have reentrancy issues"
+++ /dev/null
-diff -ruN asterisk-1.0.9-old/channels/Makefile asterisk-1.0.9-new/channels/Makefile
---- asterisk-1.0.9-old/channels/Makefile 2005-08-22 20:42:22.000000000 +0200
-+++ asterisk-1.0.9-new/channels/Makefile 2005-08-22 21:12:14.000000000 +0200
-@@ -202,6 +202,13 @@
- chan_h323.so: chan_h323.o h323/libchanh323.a
- $(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
-
-+#
-+# Asterisk Bluetooth Support
-+# http://www.crazygreek.co.uk/content/chan_bluetooth
-+#
-+chan_bluetooth.so: chan_bluetooth.o
-+ $(CC) $(SOLINK) -o $@ $< $(EXTRA_LDFLAGS) -lbluetooth
-+
-
- #chan_modem.so : chan_modem.o
- # $(CC) -rdynamic -shared -Xlinker -x -o $@ $<
-diff -ruN asterisk-1.0.9-old/channels/chan_bluetooth.c asterisk-1.0.9-new/channels/chan_bluetooth.c
---- asterisk-1.0.9-old/channels/chan_bluetooth.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.0.9-new/channels/chan_bluetooth.c 2005-09-06 22:51:30.000000000 +0200
-@@ -0,0 +1,3598 @@
-+/*
-+ * Asterisk -- A telephony toolkit for Linux.
-+ *
-+ * Asterisk Bluetooth Channel
-+ *
-+ * Author: Theo Zourzouvillys <theo@adaptive-it.co.uk>
-+ *
-+ * Adaptive Linux Solutions <http://www.adaptive-it.co.uk>
-+ *
-+ * Copyright (C) 2004 Adaptive Linux Solutions
-+ *
-+ * This program is free software, distributed under the terms of
-+ * the GNU General Public License
-+ *
-+ * ******************* NOTE NOTE NOTE NOTE NOTE *********************
-+ *
-+ * This code is not at all tested, and only been developed with a
-+ * HBH-200 headset and a Nokia 6310i right now.
-+ *
-+ * Expect it to crash, dial random numbers, and steal all your money.
-+ *
-+ * PLEASE try any headsets and phones, and let me know the results,
-+ * working or not, along with all debug output!
-+ *
-+ * ------------------------------------------------------------------
-+ *
-+ * Asterisk Bluetooth Support
-+ *
-+ * Well, here we go - Attempt to provide Handsfree profile support in
-+ * both AG and HF modes, AG (AudioGateway) mode support for using
-+ * headsets, and HF (Handsfree) mode for utilising mobile/cell phones
-+ *
-+ * It would be nice to also provide Headset support at some time in
-+ * the future, however, a working Handsfree profile is nice for now,
-+ * and as far as I can see, almost all new HS devices also support HF
-+ *
-+ * ------------------------------------------------------------------
-+ * INSTRUCTIONS
-+ *
-+ * You need to have bluez's bluetooth stack, along with user space
-+ * tools (>=v2.10), and running hcid and sdsp.
-+ *
-+ * See bluetooth.conf for configuration details.
-+ *
-+ * - Ensure bluetooth subsystem is up and running. 'hciconfig'
-+ * should show interface as UP.
-+ *
-+ * - If you're trying to use a headset/HS, start up asterisk, and try
-+ * to pair it as you normally would.
-+ *
-+ * - If you're trying to use a Phone/AG, just make sure bluetooth is
-+ * enabled on your phone, and start up asterisk.
-+ *
-+ * - 'bluetooth show peers' will show all bluetooth devices states.
-+ *
-+ * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS
-+ * with Dial(BLT/DevName)
-+ *
-+ * ------------------------------------------------------------------
-+ * BUGS
-+ *
-+ * - What should happen when an AG is paired with asterisk and
-+ * someone uses the AG dalling a number manually? My test phone
-+ * seems to try to open an SCO link. Perhaps an extension to
-+ * route the call to, or maybe drop the RFCOM link all together?
-+ *
-+ * ------------------------------------------------------------------
-+ * COMPATIBILITY
-+ *
-+ * PLEASE email <theo@adaptive-it.co.uk> with the results of ANY
-+ * device not listed in here (working or not), or if the device is
-+ * listed and it doesn't work! Please also email full debug output
-+ * for any device not working correctly or generating errors in log.
-+ *
-+ * HandsFree Profile:
-+ *
-+ * HS (HeadSet):
-+ * - Ericsson HBH-200
-+ *
-+ * AG (AudioGateway):
-+ * - Nokia 6310i
-+ *
-+ * ------------------------------------------------------------------
-+ *
-+ * Questions, bugs, or (preferably) patches to:
-+ *
-+ * <theo@adaptive-it.co.uk>
-+ *
-+ * ------------------------------------------------------------------
-+ */
-+
-+/* ---------------------------------- */
-+
-+#include <stdio.h>
-+#include <string.h>
-+#include <asterisk/lock.h>
-+#include <asterisk/utils.h>
-+#include <asterisk/channel.h>
-+#include <asterisk/config.h>
-+#include <asterisk/logger.h>
-+#include <asterisk/module.h>
-+#include <asterisk/pbx.h>
-+#include <asterisk/sched.h>
-+#include <asterisk/options.h>
-+#include <asterisk/cli.h>
-+#include <asterisk/callerid.h>
-+#include <sys/socket.h>
-+#include <sys/signal.h>
-+#include <sys/time.h>
-+#include <errno.h>
-+#include <unistd.h>
-+#include <stdlib.h>
-+#include <arpa/inet.h>
-+#include <fcntl.h>
-+#include <sys/ioctl.h>
-+#include <ctype.h>
-+#include <endian.h>
-+
-+#include <bluetooth/bluetooth.h>
-+#include <bluetooth/hci.h>
-+#include <bluetooth/hci_lib.h>
-+#include <bluetooth/sco.h>
-+#include <bluetooth/rfcomm.h>
-+#include <bluetooth/sdp.h>
-+#include <bluetooth/sdp_lib.h>
-+
-+/* --- Data types and definitions --- */
-+
-+#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID
-+# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f
-+#endif
-+#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR
-+#define BLT_CHAN_NAME "BLT"
-+#define BLT_CONFIG_FILE "bluetooth.conf"
-+#define BLT_RDBUFF_MAX 1024
-+#define BLT_DEFAULT_HCI_DEV 0
-+#define BLT_SVN_REVISION "$Rev$"
-+
-+/* ---------------------------------- */
-+
-+typedef enum {
-+ BLT_ROLE_NONE = 0, // Unknown Device
-+ BLT_ROLE_HS = 1, // Device is a Headset
-+ BLT_ROLE_AG = 2, // Device is an Audio Gateway
-+ BLT_ROLE_GUI = 3 // Device is used as an GUI
-+} blt_role_t;
-+
-+/* State when we're in HS mode */
-+
-+typedef enum {
-+ BLT_STATE_WANT_R = 0,
-+ BLT_STATE_WANT_N = 1,
-+ BLT_STATE_WANT_CMD = 2,
-+ BLT_STATE_WANT_N2 = 3,
-+} blt_state_t;
-+
-+typedef enum {
-+ BLT_STATUS_DOWN,
-+ BLT_STATUS_CONNECTING,
-+ BLT_STATUS_NEGOTIATING,
-+ BLT_STATUS_READY,
-+ BLT_STATUS_RINGING,
-+ BLT_STATUS_IN_CALL,
-+} blt_status_t;
-+
-+/* ---------------------------------- */
-+
-+/* Default config settings */
-+
-+#define BLT_DEFAULT_CHANNEL_AG 5
-+#define BLT_DEFAULT_CHANNEL_HS 6
-+#define BLT_DEFAULT_CHANNEL_GUI 1
-+#define BLT_DEFAULT_ROLE BLT_ROLE_HS
-+#define BLT_OBUF_LEN (48 * 25)
-+
-+#define BUFLEN (4800)
-+
-+/* ---------------------------------- */
-+
-+typedef struct blt_dev blt_dev_t;
-+
-+void ag_cgmi_response(blt_dev_t * dev, char * cmd);
-+void ag_unknown_response(blt_dev_t * dev, char * cmd);
-+void ag_cgmi_valid_response(blt_dev_t * dev, char * cmd);
-+void ag_clip_response(blt_dev_t * dev, char * cmd);
-+void ag_cmer_response(blt_dev_t * dev, char * cmd);
-+void ag_cind_status_response(blt_dev_t * dev, char * cmd);
-+void ag_cind_response(blt_dev_t * dev, char * cmd);
-+void ag_brsf_response(blt_dev_t * dev, char * cmd);
-+void remove_sdp_records(void);
-+
-+void gui_easm_response(blt_dev_t * dev, char * cmd);
-+
-+int sock_err(int fd);
-+int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type);
-+int set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len);
-+int get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy);
-+void gui_eaid_response(blt_dev_t * dev, char * cmd);
-+
-+
-+
-+struct blt_ring {
-+ unsigned char buf[BUFLEN];
-+};
-+// XXX:T: Tidy this lot up.
-+struct blt_dev {
-+
-+ blt_status_t status; /* Device Status */
-+
-+ struct ast_channel * owner; /* Channel we belong to, possibly NULL */
-+ blt_dev_t * dev; /* The bluetooth device channel is for */
-+ struct ast_frame fr; /* Recieved frame */
-+
-+ /* SCO Handler */
-+ int sco_pipe[2]; /* SCO alert pipe */
-+ int sco; /* SCO fd */
-+ int sco_handle; /* SCO Handle */
-+ int sco_mtu; /* SCO MTU */
-+ int sco_running; /* 1 when sCO thread should be running */
-+ pthread_t sco_thread; /* SCO thread */
-+ ast_mutex_t sco_lock; /* SCO lock */
-+ int sco_pos_in; /* Reader in position (drain)*/
-+ int sco_pos_inrcv; /* Reader in position (fill) */
-+ int wakeread; /* blt_read() needs to be woken */
-+ int sco_pos_out; /* Reader out position */
-+ int sco_sending; /* Sending SCO packets */
-+ char buf[1200]; /* Incoming data buffer */
-+ int bufpos;
-+ char sco_buf_out[BUFLEN]; /* 24 chunks of 48 */
-+ char sco_buf_in[BUFLEN]; /* 24 chunks of 48 */
-+
-+ char dnid[1024]; /* Outgoi gncall dialed number */
-+ unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */
-+ int obuf_len; /* Output Buffer Position */
-+ int obuf_wpos; /* Buffer Reader */
-+
-+ // device
-+ int autoconnect; /* 1 for autoconnect */
-+ int outgoing_id; /* Outgoing connection scheduler id */
-+ char * name; /* Devices friendly name */
-+ blt_role_t role; /* Device role (HS or AG) */
-+ bdaddr_t bdaddr; /* remote address */
-+ int channel; /* remote channel */
-+ int rd; /* RFCOMM fd */
-+ int tmp_rd; /* RFCOMM fd */
-+ int call_cnt; /* Number of attempted calls */
-+ ast_mutex_t lock; /* RFCOMM socket lock */
-+ char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */
-+ int rd_buff_pos; /* RFCOMM input buffer position */
-+ int ready; /* 1 When ready */
-+ char *context;
-+
-+ /* AG mode */
-+ char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */
-+ int cind; /* Runtime[AG]: Recieved +CIND */
-+ int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */
-+ int call, service, callsetup; /* Runtime[AG]: Values */
-+ char cid_num[AST_MAX_EXTENSION];
-+ char cid_name[AST_MAX_EXTENSION];
-+
-+ /* HS mode */
-+ blt_state_t state; /* Runtime: Device state (AG mode only) */
-+ int ring_timer; /* Runtime:Ring Timer */
-+ char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */
-+ void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */
-+
-+ int brsf; /* Runtime: Bluetooth Retrieve Supported Features */
-+ int bvra; /* Runtime: Bluetooth Voice Recognised Activation */
-+ int gain_speaker; /* Runtime: Gain Of Speaker */
-+ int clip; /* Runtime: Supports CLID */
-+ int colp; /* Runtime: Connected Line ID */
-+ int elip; /* Runtime: (Ericsson) Supports CLID */
-+ int eolp; /* Runtime: (Ericsson) Connected Line ID */
-+ int ringing; /* Runtime: Device is ringing */
-+
-+ blt_dev_t * next; /* Next in linked list */
-+
-+};
-+
-+typedef struct blt_atcb {
-+
-+ /* The command */
-+ char * str;
-+
-+ /* DTE callbacks: */
-+ int (*set)(blt_dev_t * dev, const char * arg, int len);
-+ int (*read)(blt_dev_t * dev);
-+ int (*execute)(blt_dev_t * dev, const char * data);
-+ int (*test)(blt_dev_t * dev);
-+
-+ /* DCE callbacks: */
-+ int (*unsolicited)(blt_dev_t * dev, const char * value);
-+
-+} blt_atcb_t;
-+
-+/* ---------------------------------- */
-+
-+static void rd_close(blt_dev_t * dev, int reconnect, int err);
-+static int send_atcmd(blt_dev_t * device, const char * fmt, ...);
-+static int sco_connect(blt_dev_t * dev);
-+static int sco_start(blt_dev_t * dev, int fd);
-+
-+/* ---------------------------------- */
-+
-+/* RFCOMM channel we listen on*/
-+static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG;
-+static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS;
-+static int rfcomm_channel_gui = BLT_DEFAULT_CHANNEL_GUI;
-+
-+static char* gui_default_sip_number = "";
-+static char* gui_default_sip_address = "";
-+
-+/* Address of local bluetooth interface */
-+static int hcidev_id;
-+static bdaddr_t local_bdaddr;
-+
-+/* All the current sockets */
-+AST_MUTEX_DEFINE_STATIC(iface_lock);
-+static blt_dev_t * iface_head;
-+static int ifcount = 0;
-+
-+static int sdp_record_hs = -1;
-+static int sdp_record_ag = -1;
-+static int sdp_record_gui = -1;
-+
-+/* RFCOMM listen socket */
-+static int rfcomm_sock_ag = -1;
-+static int rfcomm_sock_hs = -1;
-+static int rfcomm_sock_gui = -1;
-+
-+static int sco_socket = -1;
-+
-+static int monitor_pid = -1;
-+
-+/* The socket monitoring thread */
-+static pthread_t monitor_thread = AST_PTHREADT_NULL;
-+AST_MUTEX_DEFINE_STATIC(monitor_lock);
-+
-+/* Count how many times this module is currently in use */
-+static int usecnt = 0;
-+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
-+
-+static struct sched_context * sched = NULL;
-+
-+/* ---------------------------------- */
-+
-+#if ASTERISK_VERSION_NUM <= 010107
-+#include <asterisk/channel_pvt.h>
-+#define tech_pvt pvt->pvt
-+#else /* CVS. FIXME: Version number */
-+static struct ast_channel *blt_request(const char *type, int format, void *data, int *cause);
-+static int blt_hangup(struct ast_channel *c);
-+static int blt_answer(struct ast_channel *c);
-+static struct ast_frame *blt_read(struct ast_channel *chan);
-+static int blt_call(struct ast_channel *c, char *dest, int timeout);
-+static int blt_write(struct ast_channel *chan, struct ast_frame *f);
-+static int blt_indicate(struct ast_channel *chan, int cond);
-+
-+static const struct ast_channel_tech blt_tech = {
-+ .type = BLT_CHAN_NAME,
-+ .description = "Bluetooth Channel Driver",
-+ .capabilities = BLUETOOTH_FORMAT,
-+ .requester = blt_request,
-+ .hangup = blt_hangup,
-+ .answer = blt_answer,
-+ .read = blt_read,
-+ .call = blt_call,
-+ .write = blt_write,
-+ .indicate = blt_indicate,
-+};
-+#endif
-+/* ---------------------------------- */
-+
-+static const char *
-+role2str(blt_role_t role)
-+{
-+ switch (role) {
-+ case BLT_ROLE_HS:
-+ return "HS";
-+ case BLT_ROLE_AG:
-+ return "AG";
-+ case BLT_ROLE_GUI:
-+ return "GUI";
-+ case BLT_ROLE_NONE:
-+ default:
-+ return "??";
-+ }
-+}
-+
-+static const char *
-+status2str(blt_status_t status)
-+{
-+ switch (status) {
-+ case BLT_STATUS_DOWN:
-+ return "Down";
-+ case BLT_STATUS_CONNECTING:
-+ return "Connecting";
-+ case BLT_STATUS_NEGOTIATING:
-+ return "Negotiating";
-+ case BLT_STATUS_READY:
-+ return "Ready";
-+ case BLT_STATUS_RINGING:
-+ return "Ringing";
-+ case BLT_STATUS_IN_CALL:
-+ return "InCall";
-+ };
-+ return "Unknown";
-+}
-+
-+int sock_err(int fd)
-+{
-+ int ret;
-+ int len = sizeof(ret);
-+ getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
-+ return ret;
-+}
-+
-+/* ---------------------------------- */
-+int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type)
-+{
-+ const char *c = str;
-+ const char *start;
-+ int length;
-+ char typestr[256];
-+
-+ memset(number, 0, number_len);
-+ memset(name, 0, name_len);
-+ *type = 0;
-+
-+ number[0] = '\0';
-+ name[0] = '\0';
-+ while(*c && *c != '"')
-+ c++;
-+ c++;
-+ start = c;
-+ while(*c && *c != '"')
-+ c++;
-+ length = c - start < number_len ? c - start : number_len;
-+ strncpy(number, start, length);
-+ number[length] = '\0';
-+ c++;
-+ while(*c && *c != ',')
-+ c++;
-+ c++;
-+ start = c;
-+ while(*c && *c != ',')
-+ c++;
-+ length = c - start < number_len ? c - start : number_len;
-+ strncpy(typestr, start, length);
-+ typestr[length] = '\0';
-+ *type = atoi(typestr);
-+ c++;
-+ while(*c && *c != ',')
-+ c++;
-+ c++;
-+ while(*c && *c != ',')
-+ c++;
-+ c++;
-+ while(*c && *c != '"')
-+ c++;
-+ c++;
-+ start = c;
-+ while(*c && *c != '"')
-+ c++;
-+ length = c - start < number_len ? c - start : number_len;
-+ strncpy(name, start, length);
-+ name[length] = '\0';
-+
-+ return(1);
-+}
-+
-+
-+static const char *
-+parse_cind(const char * str, char * name, int name_len)
-+{
-+ int c = 0;
-+
-+ memset(name, 0, name_len);
-+
-+ while (*str) {
-+ if (*str == '(') {
-+ if (++c == 1 && *(str+1) == '"') {
-+ const char * start = str + 2;
-+ int len = 0;
-+ str += 2;
-+ while (*str && *str != '"') {
-+ len++;
-+ str++;
-+ }
-+ if (len == 0)
-+ return NULL;
-+ strncpy(name, start, (len > name_len) ? name_len : len);
-+ }
-+ } else if (*str == ')')
-+ c--;
-+ else if (c == 0 && *str == ',')
-+ return str + 1;
-+ str++;
-+ }
-+ return NULL;
-+}
-+
-+static void
-+set_cind(blt_dev_t * dev, int indicator, int val)
-+{
-+
-+ ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val);
-+
-+ if (indicator == dev->callsetup_pos) {
-+
-+ // call progress
-+
-+ dev->callsetup = val;
-+
-+ switch (val) {
-+ case 3:
-+ // Outgoing ringing
-+ if ((dev->owner && dev->role == BLT_ROLE_AG) ||
-+ (dev->owner && dev->role == BLT_ROLE_GUI))
-+ ast_queue_control(dev->owner, AST_CONTROL_RINGING);
-+ break;
-+ case 2:
-+ break;
-+ case 1:
-+ break;
-+ case 0:
-+ if ((dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0) ||
-+ (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0))
-+ ast_queue_control(dev->owner, AST_CONTROL_CONGESTION);
-+ break;
-+ }
-+
-+ } else if (indicator == dev->service_pos) {
-+
-+ // Signal
-+
-+ if (val == 0)
-+ ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name);
-+ else if (dev->service == 0 && val > 0)
-+ ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name);
-+
-+ dev->service = val;
-+
-+ } else if (indicator == dev->call_pos) {
-+
-+ // Call
-+
-+ dev->call = val;
-+
-+ if (dev->owner) {
-+ if (val == 1) {
-+ sco_start(dev, -1);
-+ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
-+ } else if (val == 0)
-+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
-+ }
-+
-+ }
-+
-+
-+}
-+
-+/* ---------------------------------- */
-+
-+int
-+set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len)
-+{
-+ int start_pos = *(pos);
-+ int done = 0;
-+ int copy;
-+
-+ while (data_len) {
-+ // Set can_do to the most we can do in this copy.
-+
-+ copy = MIN(circular_len - start_pos, data_len);
-+ memcpy(ring + start_pos, data + done, copy);
-+ done += copy;
-+ start_pos += copy;
-+ data_len -= copy;
-+
-+ if (start_pos == circular_len) {
-+ start_pos = 0;
-+ }
-+ }
-+ *(pos) = start_pos;
-+ return 0;
-+}
-+
-+int
-+get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy)
-+{
-+ int copy;
-+
-+ // |1|2|3|4|5|6|7|8|9|
-+ // |-----|
-+
-+ while (to_copy) {
-+
-+ // Set can_do to the most we can do in this copy.
-+ copy = MIN(ring_size - *head, to_copy);
-+
-+ // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head);
-+#if __BYTE_ORDER == __LITTLE_ENDIAN
-+ memcpy(dst, ring + *head, copy);
-+#else
-+ // memcpy(dst, ring + *head, copy);
-+ ast_swapcopy_samples(dst, ring+*head, copy/2);
-+#endif
-+ memset(ring+*head, 0, copy);
-+ dst += copy;
-+ *head += copy;
-+ to_copy -= copy;
-+
-+ if (*head == ring_size ) {
-+ *head = 0;
-+ }
-+
-+ }
-+
-+ return 0;
-+}
-+
-+/* Handle SCO audio sync.
-+ *
-+ * If we are the MASTER, then we control the timing,
-+ * in 48 byte chunks. If we're the SLAVE, we send
-+ * as and when we recieve a packet.
-+ *
-+ * Because of packet/timing nessecity, we
-+ * start up a thread when we're passing audio, so
-+ * that things are timed exactly right.
-+ *
-+ * sco_thread() is the function that handles it.
-+ *
-+ */
-+
-+static void *
-+sco_thread(void * data)
-+{
-+ blt_dev_t * dev = (blt_dev_t*)data;
-+ int res;
-+ struct pollfd pfd[2];
-+ int in_pos = 0;
-+ int out_pos = 0;
-+ char c = 1;
-+ int sending;
-+ char buf[1024];
-+ int len;
-+
-+ // Avoid deadlock in odd circumstances
-+
-+ ast_log(LOG_WARNING, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid());
-+
-+ if (fcntl(dev->sco_pipe[1], F_SETFL, O_RDWR|O_NONBLOCK)) {
-+ ast_log(LOG_WARNING, "fcntl failed on sco_pipe\n");
-+ }
-+
-+ // dev->status = BLT_STATUS_IN_CALL;
-+ // ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
-+ // Set buffer to silence, just incase.
-+
-+ ast_mutex_lock(&(dev->sco_lock));
-+
-+ memset(dev->sco_buf_in, 0, BUFLEN);
-+ memset(dev->sco_buf_out, 0, BUFLEN);
-+
-+ dev->sco_pos_in = 0;
-+ dev->sco_pos_out = 0;
-+ dev->sco_pos_inrcv = 0;
-+ dev->wakeread = 1;
-+
-+ ast_mutex_unlock(&(dev->sco_lock));
-+
-+ while (1) {
-+
-+ ast_mutex_lock(&(dev->sco_lock));
-+
-+ if (dev->sco_running != 1) {
-+ ast_log(LOG_DEBUG, "SCO stopped.\n");
-+ break;
-+ }
-+
-+ pfd[0].fd = dev->sco;
-+ pfd[0].events = POLLIN;
-+
-+ pfd[1].fd = dev->sco_pipe[1];
-+ pfd[1].events = POLLIN;
-+
-+ ast_mutex_unlock(&(dev->sco_lock));
-+
-+ res = poll(pfd, 2, 50);
-+
-+ if (res == -1 && errno != EINTR) {
-+ ast_log(LOG_DEBUG, "SCO poll() error\n");
-+ break;
-+ }
-+
-+ if (res == 0)
-+ continue;
-+
-+
-+ if (pfd[0].revents & POLLIN) {
-+
-+ len = read(dev->sco, buf, 48);
-+
-+ if (len) {
-+ ast_mutex_lock(&(dev->lock));
-+
-+ if (dev->owner && dev->owner->_state == AST_STATE_UP) {
-+ ast_mutex_lock(&(dev->sco_lock));
-+ set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len);
-+ dev->sco_pos_inrcv = in_pos;
-+
-+ get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len);
-+ if (write(dev->sco, buf, len) != len)
-+ ast_log(LOG_WARNING, "Wrote <48 to sco\n");
-+
-+ if (dev->wakeread) {
-+ /* blt_read has caught up. Kick it */
-+ dev->wakeread = 0;
-+ if(write(dev->sco_pipe[1], &c, 1) != 1)
-+ ast_log(LOG_WARNING, "write to kick sco_pipe failed\n");
-+ }
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ }
-+ ast_mutex_unlock(&(dev->lock));
-+ }
-+
-+ } else if (pfd[0].revents) {
-+
-+ int e = sock_err(pfd[0].fd);
-+ ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e);
-+ break;
-+
-+ } else if (pfd[1].revents & POLLIN) {
-+
-+ int len;
-+
-+ len = read(pfd[1].fd, &c, 1);
-+ sending = (sending) ? 0 : 1;
-+
-+ ast_mutex_unlock(&(dev->sco_lock));
-+
-+ } else if (pfd[1].revents) {
-+
-+ int e = sock_err(pfd[1].fd);
-+ ast_log(LOG_ERROR, "SCO pipe connection event %d on pipe[1]=%d: %s (errno %d)\n", pfd[1].revents, pfd[1].fd, strerror(e), e);
-+ break;
-+
-+ } else {
-+ ast_log(LOG_NOTICE, "Unhandled poll output\n");
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ }
-+
-+ }
-+
-+ ast_mutex_lock(&(dev->lock));
-+ close(dev->sco);
-+ dev->sco = -1;
-+ dev->sco_running = -1;
-+
-+ memset(dev->sco_buf_in, 0, BUFLEN);
-+ memset(dev->sco_buf_out, 0, BUFLEN);
-+
-+ dev->sco_pos_in = 0;
-+ dev->sco_pos_out = 0;
-+ dev->sco_pos_inrcv = 0;
-+
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ if (dev->owner)
-+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
-+ ast_mutex_unlock(&(dev->lock));
-+ ast_log(LOG_DEBUG, "SCO thread stopped\n");
-+ return NULL;
-+}
-+
-+/* Start SCO thread. Must be called with dev->lock */
-+
-+static int
-+sco_start(blt_dev_t * dev, int fd)
-+{
-+
-+ if (dev->sco_pipe[1] <= 0) {
-+ ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]);
-+ return -1;
-+ }
-+
-+ ast_mutex_lock(&(dev->sco_lock));
-+
-+ if (dev->sco_running != -1) {
-+ ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n");
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ return -1;
-+ }
-+
-+ if (dev->sco == -1) {
-+ if (fd > 0) {
-+ dev->sco = fd;
-+ } else if (sco_connect(dev) != 0) {
-+ ast_log(LOG_ERROR, "SCO fd invalid\n");
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ return -1;
-+ }
-+ }
-+
-+ dev->sco_running = 1;
-+
-+ if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) {
-+ ast_log(LOG_ERROR, "Unable to start SCO thread.\n");
-+ dev->sco_running = -1;
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ return -1;
-+ }
-+
-+ ast_mutex_unlock(&(dev->sco_lock));
-+
-+ return 0;
-+}
-+
-+/* Stop SCO thread. Must be called with dev->lock */
-+
-+static int
-+sco_stop(blt_dev_t * dev)
-+{
-+ ast_mutex_lock(&(dev->sco_lock));
-+ if (dev->sco_running == 1)
-+ dev->sco_running = 0;
-+ else
-+ dev->sco_running = -1;
-+ dev->sco_sending = 0;
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ return 0;
-+}
-+
-+/* ---------------------------------- */
-+
-+/* Answer the call. Call with lock held on device */
-+
-+static int
-+answer(blt_dev_t * dev)
-+{
-+
-+ if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) {
-+ ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n",
-+ dev->owner, dev->ready, status2str(dev->status));
-+ return -1;
-+ }
-+
-+ // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0);
-+ // dev->status = BLT_STATUS_IN_CALL;
-+ // dev->owner->fds[0] = dev->sd;
-+ // if we are answering (hitting button):
-+ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
-+ // if asterisk signals us to answer:
-+ // ast_setstate(ast, AST_STATE_UP);
-+
-+ /* Start SCO link */
-+ sco_start(dev, -1);
-+ return 0;
-+}
-+
-+/* ---------------------------------- */
-+
-+static int
-+blt_write(struct ast_channel * ast, struct ast_frame * frame)
-+{
-+ blt_dev_t * dev = ast->tech_pvt;
-+
-+ /* Write a frame of (presumably voice) data */
-+
-+ if (frame->frametype != AST_FRAME_VOICE) {
-+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
-+ return 0;
-+ }
-+
-+ if (!(frame->subclass & BLUETOOTH_FORMAT)) {
-+ static int fish = 5;
-+ if (fish) {
-+ ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass);
-+ fish--;
-+ }
-+ return 0;
-+ }
-+
-+ if (ast->_state != AST_STATE_UP) {
-+ return 0;
-+ }
-+
-+ ast_mutex_lock(&(dev->sco_lock));
-+ set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN));
-+ ast_mutex_unlock(&(dev->sco_lock));
-+
-+ return 0;
-+
-+}
-+
-+static struct ast_frame *
-+blt_read(struct ast_channel * ast)
-+{
-+ blt_dev_t * dev = ast->tech_pvt;
-+ char c = 1;
-+ int len;
-+ static int fish = 0;
-+ /* Some nice norms */
-+
-+ dev->fr.datalen = 0;
-+ dev->fr.samples = 0;
-+ dev->fr.data = NULL;
-+ dev->fr.src = BLT_CHAN_NAME;
-+ dev->fr.offset = 0;
-+ dev->fr.mallocd = AST_MALLOCD_DATA;
-+ dev->fr.delivery.tv_sec = 0;
-+ dev->fr.delivery.tv_usec = 0;
-+ read(dev->sco_pipe[0], &c, 1);
-+ ast_mutex_lock(&(dev->sco_lock));
-+ dev->sco_sending = 1;
-+
-+ if (dev->sco_pos_inrcv < dev->sco_pos_in) {
-+ /* Buffer wrapped. Read only till the end */
-+ len = BUFLEN - dev->sco_pos_in + dev->sco_pos_inrcv;
-+ } else {
-+ len = dev->sco_pos_inrcv - dev->sco_pos_in;
-+ }
-+ dev->fr.data = malloc(AST_FRIENDLY_OFFSET+len) + AST_FRIENDLY_OFFSET;
-+
-+ get_buffer(dev->fr.data, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), len);
-+ dev->wakeread = 1;
-+ ast_mutex_unlock(&(dev->sco_lock));
-+ if (fish) {
-+ unsigned char *x = dev->fr.data;
-+ ast_log(LOG_WARNING, "blt_read %d: %02x %02x %02x %02x %02x %02x\n",
-+ dev->fr.datalen, x[0], x[1], x[2], x[3], x[4], x[5]);
-+ fish--;
-+ }
-+
-+ dev->fr.samples = len / 2;
-+ dev->fr.datalen = len;
-+ dev->fr.frametype = AST_FRAME_VOICE;
-+ dev->fr.subclass = BLUETOOTH_FORMAT;
-+ dev->fr.offset = AST_FRIENDLY_OFFSET;
-+ return &dev->fr;
-+}
-+
-+/* Escape Any '"' in str. Return malloc()ed string */
-+static char *
-+escape_str(char * str)
-+{
-+ char * ptr = str;
-+ char * pret;
-+ char * ret;
-+ int len = 0;
-+
-+ while (*ptr) {
-+ if (*ptr == '"')
-+ len++;
-+ len++;
-+ ptr++;
-+ }
-+
-+ ret = malloc(len + 1);
-+ pret = memset(ret, 0, len + 1);
-+
-+ ptr = str;
-+
-+ while (*ptr) {
-+ if (*ptr == '"')
-+ *pret++ = '\\';
-+ *pret++ = *ptr++;
-+ }
-+
-+ return ret;
-+}
-+
-+static int
-+ring_hs(blt_dev_t * dev)
-+{
-+#if (ASTERISK_VERSION_NUM < 010100)
-+ char tmp[AST_MAX_EXTENSION];
-+ char *name, *num;
-+#endif
-+
-+ ast_mutex_lock(&(dev->lock));
-+
-+ if (dev->owner == NULL) {
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+ }
-+
-+ dev->ringing = 1;
-+ dev->status = BLT_STATUS_RINGING;
-+
-+ send_atcmd(dev, "RING");
-+
-+ dev->owner->rings++;
-+
-+ // XXX:T: '"' needs to be escaped in ELIP.
-+
-+#if (ASTERISK_VERSION_NUM < 010100)
-+
-+ if (dev->owner->callerid) {
-+
-+ memset(tmp, 0, sizeof(tmp));
-+ strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1);
-+
-+ if (!ast_callerid_parse(tmp, &name, &num)) {
-+
-+ if (dev->clip && num)
-+ send_atcmd(dev, "+CLIP: \"%s\",129", num);
-+
-+ if (dev->elip && name) {
-+ char * esc = escape_str(name);
-+ send_atcmd(dev, "*ELIP: \"%s\"", esc);
-+ free(esc);
-+ }
-+ }
-+ }
-+
-+
-+#else
-+
-+ if (dev->clip && dev->owner->cid.cid_num)
-+ send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num);
-+
-+ if (dev->elip && dev->owner->cid.cid_name) {
-+ char * esc = escape_str(dev->owner->cid.cid_name);
-+ send_atcmd(dev, "*ELIP: \"%s\"", esc);
-+ free(esc);
-+ }
-+
-+#endif
-+
-+ ast_mutex_unlock(&(dev->lock));
-+
-+ return 1;
-+}
-+
-+/*
-+ * If the HS is already connected, then just send RING, otherwise, things get a
-+ * little more sticky. We first have to find the channel for HS using SDP,
-+ * then initiate the connection. Once we've done that, we can start the call.
-+ */
-+
-+static int
-+blt_call(struct ast_channel * ast, char * dest, int timeout)
-+{
-+ blt_dev_t * dev = ast->tech_pvt;
-+
-+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
-+ ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name);
-+ return -1;
-+ }
-+
-+ ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout);
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
-+ return -1;
-+ }
-+
-+// ast_mutex_lock(&(dev->lock));
-+
-+ if (dev->ready == 0) {
-+ ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n");
-+ ast_setstate(ast, AST_CONTROL_CONGESTION);
-+// ast_mutex_unlock(&(dev->lock));
-+ ast_mutex_unlock(&iface_lock);
-+ return 0;
-+ }
-+
-+ if (dev->role == BLT_ROLE_HS) {
-+
-+ send_atcmd(dev, "+CIEV: 3,1");
-+
-+ dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev);
-+
-+ ring_hs(dev);
-+
-+ ast_setstate(ast, AST_STATE_RINGING);
-+ ast_queue_control(ast, AST_CONTROL_RINGING);
-+
-+ } else if (dev->role == BLT_ROLE_AG) {
-+
-+ send_atcmd(dev, "ATD%s;", dev->dnid);
-+// it does not seem like we should start the audio until the call is connected
-+// sco_start(dev, -1);
-+ } else if (dev->role == BLT_ROLE_GUI) {
-+
-+ send_atcmd(dev, "ATD%s;", dev->dnid);
-+
-+ } else {
-+
-+ ast_setstate(ast, AST_CONTROL_CONGESTION);
-+ ast_log(LOG_ERROR, "Unknown device role\n");
-+
-+ }
-+
-+// ast_mutex_unlock(&(dev->lock));
-+ ast_mutex_unlock(&iface_lock);
-+
-+ return 0;
-+}
-+
-+static int
-+blt_hangup(struct ast_channel * ast)
-+{
-+ blt_dev_t * dev = ast->tech_pvt;
-+
-+ ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name);
-+
-+ if (!ast->tech_pvt) {
-+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
-+ return 0;
-+ }
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get iface_lock\n");
-+ return 0;
-+ }
-+
-+ ast_mutex_lock(&(dev->lock));
-+
-+ sco_stop(dev);
-+ dev->sco_sending = 0;
-+
-+ if (dev->role == BLT_ROLE_HS) {
-+
-+ if (dev->ringing == 0) {
-+ // Actual call in progress
-+ send_atcmd(dev, "+CIEV: 2,0");
-+ } else {
-+
-+ // Just ringing still
-+
-+ if (dev->role == BLT_ROLE_HS)
-+ send_atcmd(dev, "+CIEV: 3,0");
-+
-+ if (dev->ring_timer >= 0)
-+ ast_sched_del(sched, dev->ring_timer);
-+
-+ dev->ring_timer = -1;
-+ dev->ringing = 0;
-+
-+ }
-+
-+ } else if (dev->role == BLT_ROLE_AG) {
-+
-+ // Cancel call.
-+ send_atcmd(dev, "ATH");
-+ send_atcmd(dev, "AT+CHUP");
-+
-+ }
-+
-+ if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING)
-+ dev->status = BLT_STATUS_READY;
-+
-+ ast->tech_pvt = NULL;
-+ dev->owner = NULL;
-+ ast_mutex_unlock(&(dev->lock));
-+ ast_setstate(ast, AST_STATE_DOWN);
-+ ast_mutex_unlock(&(iface_lock));
-+
-+ return 0;
-+}
-+
-+static int
-+blt_indicate(struct ast_channel * c, int condition)
-+{
-+ ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition);
-+
-+ switch(condition) {
-+ case AST_CONTROL_RINGING:
-+ return -1;
-+ default:
-+ ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition);
-+ break;
-+ }
-+ return -1;
-+}
-+
-+static int
-+blt_answer(struct ast_channel * ast)
-+{
-+ blt_dev_t * dev = ast->tech_pvt;
-+
-+ ast_mutex_lock(&dev->lock);
-+
-+ // if (dev->ring_timer >= 0)
-+ // ast_sched_del(sched, dev->ring_timer);
-+ // dev->ring_timer = -1;
-+
-+ ast_log(LOG_DEBUG, "Answering interface\n");
-+
-+ if (ast->_state != AST_STATE_UP) {
-+ send_atcmd(dev, "+CIEV: 2,1");
-+ send_atcmd(dev, "+CIEV: 3,0");
-+ sco_start(dev, -1);
-+ ast_setstate(ast, AST_STATE_UP);
-+ }
-+
-+ ast_mutex_unlock(&dev->lock);
-+
-+ return 0;
-+}
-+
-+static struct ast_channel *
-+blt_new(blt_dev_t * dev, int state, const char * context, const char * number)
-+{
-+ struct ast_channel * ast;
-+ char c = 0;
-+
-+ if ((ast = ast_channel_alloc(1)) == NULL) {
-+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
-+ return NULL;
-+ }
-+
-+ snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name);
-+
-+ // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-+
-+ ast->nativeformats = BLUETOOTH_FORMAT;
-+ //ast->rawreadformat = BLUETOOTH_FORMAT;
-+ //ast->rawwriteformat = BLUETOOTH_FORMAT;
-+ ast->writeformat = BLUETOOTH_FORMAT;
-+ ast->readformat = BLUETOOTH_FORMAT;
-+
-+ ast_setstate(ast, state);
-+
-+ ast->type = BLT_CHAN_NAME;
-+
-+ ast->tech_pvt = dev;
-+#if ASTERISK_VERSION_NUM > 010107
-+ ast->tech = &blt_tech;
-+#else
-+ ast->pvt->call = blt_call;
-+ ast->pvt->indicate = blt_indicate;
-+ ast->pvt->hangup = blt_hangup;
-+ ast->pvt->read = blt_read;
-+ ast->pvt->write = blt_write;
-+ ast->pvt->answer = blt_answer;
-+#endif
-+ strncpy(ast->context, context, sizeof(ast->context)-1);
-+ strncpy(ast->exten, number, sizeof(ast->exten) - 1);
-+ if(0 == strcmp(number, "s"))
-+ {
-+ //ast_set_callerid(ast, dev->cid_num, dev->cid_name, dev->cid_num);
-+ }
-+
-+ ast->language[0] = '\0';
-+
-+ ast->fds[0] = dev->sco_pipe[0];
-+ write(dev->sco_pipe[1], &c, 1);
-+
-+ dev->owner = ast;
-+
-+ ast_mutex_lock(&usecnt_lock);
-+ usecnt++;
-+ ast_mutex_unlock(&usecnt_lock);
-+
-+ ast_update_use_count();
-+
-+ if (state != AST_STATE_DOWN) {
-+ if (ast_pbx_start(ast)) {
-+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name);
-+ ast_hangup(ast);
-+ }
-+ }
-+
-+ return ast;
-+}
-+
-+static struct ast_channel *
-+#if (ASTERISK_VERSION_NUM < 010100)
-+blt_request(char * type, int format, void * local_data)
-+#elif (ASTERISK_VERSION_NUM <= 010107)
-+blt_request(const char * type, int format, void * local_data)
-+#else
-+blt_request(const char * type, int format, void * local_data, int *cause)
-+#endif
-+{
-+ char * data = (char*)local_data;
-+ int oldformat;
-+ blt_dev_t * dev = NULL;
-+ struct ast_channel * ast = NULL;
-+ char * number = data, * dname;
-+
-+ dname = strsep(&number, "/");
-+
-+ oldformat = format;
-+
-+ format &= BLUETOOTH_FORMAT;
-+
-+ if (!format) {
-+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
-+ return NULL;
-+ }
-+
-+ ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname);
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Unable to lock iface_list\n");
-+ return NULL;
-+ }
-+
-+ dev = iface_head;
-+
-+ while (dev) {
-+ if (strcmp(dev->name, dname) == 0) {
-+ ast_mutex_lock(&(dev->lock));
-+ if (!dev->ready) {
-+ ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name);
-+ ast_mutex_unlock(&(dev->lock));
-+ ast_mutex_unlock(&iface_lock);
-+ return NULL;
-+ }
-+ break;
-+ }
-+ dev = dev->next;
-+ }
-+
-+ ast_mutex_unlock(&iface_lock);
-+
-+ if (!dev) {
-+ ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname);
-+ return NULL;
-+ }
-+
-+ if (number && dev->role != BLT_ROLE_AG) {
-+ ast_log(LOG_WARNING, "Tried to send a call out on non AG\n");
-+ ast_mutex_unlock(&(dev->lock));
-+ return NULL;
-+ }
-+
-+ if (dev->role == BLT_ROLE_AG)
-+ strncpy(dev->dnid, number, sizeof(dev->dnid) - 1);
-+
-+ ast = blt_new(dev, AST_STATE_DOWN, dev->context, "s");
-+
-+ ast_mutex_unlock(&(dev->lock));
-+
-+ return ast;
-+}
-+
-+/* ---------------------------------- */
-+
-+
-+/* ---- AT COMMAND SOCKET STUFF ---- */
-+
-+static int
-+send_atcmd(blt_dev_t * dev, const char * fmt, ...)
-+{
-+ char buf[1024];
-+ va_list ap;
-+ int len;
-+
-+ va_start(ap, fmt);
-+ len = vsnprintf(buf, 1023, fmt, ap);
-+ va_end(ap);
-+
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf);
-+
-+ write(dev->rd, "\r\n", 2);
-+ len = write(dev->rd, buf, len);
-+ write(dev->rd, "\r\n", 2);
-+ return (len) ? 0 : -1;
-+}
-+
-+
-+static int
-+send_atcmd_ok(blt_dev_t * dev, const char * cmd)
-+{
-+ int len;
-+ strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1);
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name);
-+ len = write(dev->rd, "\r\nOK\r\n", 6);
-+ return (len) ? 0 : -1;
-+}
-+
-+static int
-+send_atcmd_error(blt_dev_t * dev)
-+{
-+ int len;
-+
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name);
-+
-+// write(dev->rd, "\r\n", 2);
-+// len = write(dev->rd, dev->last_ok_cmd, 5);
-+ write(dev->rd, "\r\n", 2);
-+ len = write(dev->rd, "ERROR", 5);
-+ write(dev->rd, "\r\n", 2);
-+
-+ return (len) ? 0 : -1;
-+}
-+
-+
-+/* ---------------------------------- */
-+
-+/* -- Handle negotiation when we're an AG -- */
-+
-+/* Bluetooth Support */
-+
-+static int
-+atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ ast_log(LOG_DEBUG, "Device Supports: %s\n", arg);
-+ dev->brsf = atoi(arg);
-+ send_atcmd(dev, "+BRSF: %d", 23);
-+ return 0;
-+}
-+
-+/* Bluetooth Voice Recognition */
-+
-+static int
-+atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n");
-+ return -1;
-+#if 0
-+ // XXX:T: Fix voice recognition somehow!
-+ int action = atoi(arg);
-+ ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED");
-+ if ((action == 0) & (dev->bvra == 1)) {
-+ /* Disable it */
-+ dev->bvra = 0;
-+ // XXX:T: Shutdown any active bvra channel
-+ ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n");
-+ } else if ((action == 1) && (dev->bvra == 0)) {
-+ /* Enable it */
-+ dev->bvra = 1;
-+ // XXX:T: Schedule connection to voice recognition extension/application
-+ ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n");
-+ } else {
-+ ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action);
-+ return -1;
-+ }
-+ return 0;
-+#endif
-+}
-+
-+/* Clock */
-+
-+static int
-+atcmd_cclk_read(blt_dev_t * dev)
-+{
-+ struct tm t, *tp;
-+ const time_t ti = time(0);
-+ tp = localtime_r(&ti, &t);
-+ send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"",
-+ (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday),
-+ tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15));
-+ return 0;
-+}
-+
-+/* CHUP - Hangup Call */
-+
-+static int
-+atcmd_chup_execute(blt_dev_t * dev, const char * data)
-+{
-+ if (!dev->owner) {
-+ ast_log(LOG_ERROR, "Request to hangup call when none in progress\n");
-+ return -1;
-+ }
-+ ast_log(LOG_DEBUG, "Hangup Call\n");
-+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
-+ return 0;
-+}
-+
-+/* CIND - Call Indicator */
-+
-+static int
-+atcmd_cind_read(blt_dev_t * dev)
-+{
-+ send_atcmd(dev, "+CIND: 1,0,0");
-+ return 0;
-+}
-+
-+static int
-+atcmd_cind_test(blt_dev_t * dev)
-+{
-+ send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))");
-+ return 0;
-+}
-+
-+/* Set Language */
-+
-+static int
-+atcmd_clan_read(blt_dev_t * dev)
-+{
-+ send_atcmd(dev, "+CLAN: \"en\"");
-+ return 0;
-+}
-+
-+/* Caller Id Presentation */
-+
-+static int
-+atcmd_clip_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ dev->clip = atoi(arg);
-+ return 0;
-+}
-+
-+/* Connected Line Identification Presentation */
-+
-+static int
-+atcmd_colp_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ dev->colp = atoi(arg);
-+ return 0;
-+}
-+
-+/* CMER - Mobile Equipment Event Reporting */
-+
-+static int
-+atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ dev->ready = 1;
-+ dev->status = BLT_STATUS_READY;
-+ return 0;
-+}
-+
-+/* PhoneBook Types:
-+ *
-+ * - FD - SIM Fixed Dialing Phone Book
-+ * - ME - ME Phone book
-+ * - SM - SIM Phone Book
-+ * - DC - ME dialled-calls list
-+ * - RC - ME recieved-calls lisr
-+ * - MC - ME missed-calls list
-+ * - MV - ME Voice Activated Dialing List
-+ * - HP - Hierachial Phone Book
-+ * - BC - Own Business Card (PIN2 required)
-+ *
-+ */
-+
-+/* Read Phone Book Entry */
-+
-+static int
-+atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ // XXX:T: Fix the phone book!
-+ // * Maybe add res_phonebook or something? */
-+ send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg);
-+ return 0;
-+}
-+
-+/* Select Phone Book */
-+
-+static int
-+atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ // XXX:T: I guess we'll just accept any?
-+ return 0;
-+}
-+
-+static int
-+atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ // XXX:T: Language
-+ return 0;
-+}
-+
-+static int
-+atcmd_eips_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n",
-+ (*(arg) == 49) ? "ELIP" : "EOLP",
-+ (*(arg+2) == 49) ? "ON" : "OFF");
-+
-+ if (*(arg) == 49)
-+ dev->eolp = (*(arg+2) == 49) ? 1 : 0;
-+ else
-+ dev->elip = (*(arg+2) == 49) ? 1 : 0;
-+
-+ return 0;
-+}
-+
-+/* VGS - Speaker Volume Gain */
-+
-+static int
-+atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len)
-+{
-+ dev->gain_speaker = atoi(arg);
-+ return 0;
-+}
-+
-+void
-+gui_eaid_response(blt_dev_t * dev, char * cmd)
-+{
-+ ast_log(LOG_NOTICE, "Submenu displayed.\n");
-+}
-+
-+static int
-+atcmd_eami_execute(blt_dev_t * dev, const char * data)
-+{
-+ char * number = NULL;
-+
-+ number = strndup(data, strlen(data));
-+ int menuitem = atoi(number);
-+
-+ ast_log(LOG_NOTICE, "Menu Item '%d'.\n", menuitem);
-+
-+ dev->cb = gui_eaid_response;
-+
-+ if (menuitem == 1) {
-+ char command[1024] = "";
-+ const char* c1 = "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"";
-+ const char* c2 = "\"";
-+
-+ (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
-+ (void)strncat(command, gui_default_sip_number, sizeof(command) - strlen(command) - 1);
-+ (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
-+
-+ //strcat(command, "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"");
-+ //strcat(command, gui_default_sip_number);
-+ //strcat(command, "\"");
-+ send_atcmd(dev, command);
-+ } else if (menuitem == 2) {
-+ char command[1024] = "";
-+ const char* c1 = "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"";
-+ const char* c2 = "\"";
-+
-+ (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
-+ (void)strncat(command, gui_default_sip_address, sizeof(command) - strlen(command) - 1);
-+ (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
-+
-+ //strcat(command, "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"");
-+ //strcat(command, gui_default_sip_address);
-+ //strcat(command, "\"");
-+ send_atcmd(dev, command);
-+ } else if (menuitem == 0) {
-+ dev->cb = gui_easm_response;
-+// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
-+ send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
-+ } else {
-+ ast_log(LOG_ERROR, "Menu item not implementented.\n");
-+ }
-+ return 0;
-+}
-+
-+static int
-+atcmd_eaii_execute(blt_dev_t * dev, const char * data)
-+{
-+ int pos = 1, len = 0;
-+ char type[128];
-+ char val[128];
-+ const char * start = data;
-+ struct sockaddr_in addr;
-+
-+ while (*data) {
-+ if (*data == ',') {
-+ memset(type, 0, 128);
-+ strncpy(type, start, len);
-+
-+ ast_log(LOG_NOTICE, "Number(8)/Address(11): '%s'.\n", type);
-+
-+ pos++;
-+ len = 0;
-+ data++;
-+ start = data;
-+ continue;
-+ }
-+ len++;
-+ data++;
-+ }
-+
-+ memset(val, 0, 128);
-+ strncpy(val, start, len);
-+
-+ char del[]= "\"";
-+ char* address;
-+ address = strtok(val, del);
-+ int type_int = atoi(type);
-+
-+ if (strcmp(address, " 0") == 0) {
-+ ast_log(LOG_NOTICE, "Spurious EAII:\n");
-+ ast_log(LOG_NOTICE, data);
-+ return 0;
-+ }
-+
-+ if (type_int == 8) {
-+ (void)strncat(address, "@sipgate.de", sizeof(address) - strlen(address) - 1);
-+ }
-+
-+ ast_log(LOG_NOTICE, "SIP number/address: '%i','%s'.\n", type_int, address);
-+
-+ if (type_int == 8 || type_int == 11) {
-+
-+ char messagebox[1024] = "";
-+ const char* mb1 = "AT*EAID=1,1,\"Setting up SIP call to ";
-+ const char* mb2 = "\",30";
-+
-+ (void)strncat(messagebox, mb1, sizeof(messagebox) - strlen(messagebox) - 1);
-+ (void)strncat(messagebox, address, sizeof(messagebox) - strlen(messagebox) - 1);
-+ (void)strncat(messagebox, mb2, sizeof(messagebox) - strlen(messagebox) - 1);
-+
-+ //strcat(messagebox, "AT*EAID=1,1,\"Setting up SIP call to ");
-+ //strcat(messagebox, address);
-+ //strcat(messagebox, "\",30");
-+ send_atcmd(dev, messagebox);
-+
-+ send_atcmd(dev, "AT*ESKS=2");
-+ send_atcmd(dev, "AT*EKSP");
-+ send_atcmd(dev, "AT*ESKS=0");
-+
-+ //Create manager connection to create call
-+ int s = socket(AF_INET,SOCK_STREAM,0);
-+ if (s < 0) {
-+ ast_log(LOG_ERROR, "Manager connection failed.");
-+
-+ dev->cb = ag_cgmi_response;
-+ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
-+ return -1;
-+ }
-+ addr.sin_family = AF_INET;
-+ addr.sin_port = htons(5038);
-+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-+ memset(&(addr.sin_zero), '\0', 8);
-+
-+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_log(LOG_ERROR, "Manager connection failed. (2)");
-+ dev->cb = ag_cgmi_response;
-+ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
-+ return -1;
-+ }
-+ char* command = "Action: login\r\nUsername: markus\r\nSecret: supAEr\r\n\r\n";
-+ if (write(s,command,strlen(command)) < 0) {
-+ ast_log(LOG_ERROR, "Manager connection failed. (3)");
-+ dev->cb = ag_cgmi_response;
-+ send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
-+ return -1;
-+ }
-+
-+ char command3[1024] = "";
-+ const char* action = "Action: Originate\r\nChannel: SIP/";
-+ const char* action2 = "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\nAction: logoff\r\n\r\n";
-+
-+ (void)strncat(command3, action, sizeof(command3) - strlen(command3) - 1);
-+ (void)strncat(command3, address, sizeof(command3) - strlen(command3) - 1);
-+ (void)strncat(command3, action2, sizeof(command3) - strlen(command3) - 1);
-+
-+ //strcat(command3, "Action: Originate\r\nChannel: SIP/");
-+ //strcat(command3, address);
-+ //strcat(command3, "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\n");
-+ ast_log(LOG_NOTICE, command3);
-+
-+ if (write(s,command3,strlen(command3)) < 0) {
-+ ast_log(LOG_ERROR, "Manager connection failed. (5)");
-+ return -1;
-+ }
-+ }
-+ //dev->cb = ag_cgmi_response;
-+ return 0;
-+}
-+
-+/* Dial */
-+static int
-+atcmd_dial_execute(blt_dev_t * dev, const char * data)
-+{
-+ char * number = NULL;
-+
-+ /* Make sure there is a ';' at the end of the line */
-+ if (*(data + (strlen(data) - 1)) != ';') {
-+ ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data);
-+ return -1;
-+ }
-+
-+ number = strndup(data, strlen(data) - 1);
-+ ast_log(LOG_NOTICE, "Dial: [%s]\n", number);
-+
-+ send_atcmd(dev, "+CIEV: 2,1");
-+ send_atcmd(dev, "+CIEV: 3,0");
-+
-+ sco_start(dev, -1);
-+
-+ if (blt_new(dev, AST_STATE_UP, dev->context, number) == NULL) {
-+ sco_stop(dev);
-+ }
-+
-+ free(number);
-+
-+ return 0;
-+}
-+
-+static int atcmd_bldn_execute(blt_dev_t * dev, const char *data)
-+{
-+ return atcmd_dial_execute(dev, "bldn;");
-+}
-+
-+/* Answer */
-+
-+static int
-+atcmd_answer_execute(blt_dev_t * dev, const char * data)
-+{
-+
-+ if (!dev->ringing || !dev->owner) {
-+ ast_log(LOG_WARNING, "Can't answer non existant call\n");
-+ return -1;
-+ }
-+
-+ dev->ringing = 0;
-+
-+ if (dev->ring_timer >= 0)
-+ ast_sched_del(sched, dev->ring_timer);
-+
-+ dev->ring_timer = -1;
-+
-+ send_atcmd(dev, "+CIEV: 2,1");
-+ send_atcmd(dev, "+CIEV: 3,0");
-+
-+ return answer(dev);
-+}
-+
-+static int
-+ag_unsol_ciev(blt_dev_t * dev, const char * data)
-+{
-+ const char * orig = data;
-+ int indicator;
-+ int status;
-+
-+ while (*(data) && *(data) == ' ')
-+ data++;
-+
-+ if (*(data) == 0) {
-+ ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig);
-+ return -1;
-+ }
-+
-+ indicator = *(data++) - 48;
-+
-+ if (*(data++) != ',') {
-+ ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig);
-+ return -1;
-+ }
-+
-+ if (*(data) == 0) {
-+ ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig);
-+ return -1;
-+ }
-+
-+ status = *(data) - 48;
-+
-+ set_cind(dev, indicator, status);
-+
-+ return 0;
-+}
-+
-+static int
-+ag_unsol_cind(blt_dev_t * dev, const char * data)
-+{
-+
-+ while (*(data) && *(data) == ' ')
-+ data++;
-+
-+
-+ if (dev->cind == 0)
-+ {
-+ int pos = 1;
-+ char name[1024];
-+
-+ while ((data = parse_cind(data, name, 1023)) != NULL) {
-+ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
-+ if (strcmp(name, "call") == 0)
-+ dev->call_pos = pos;
-+ else if (strcmp(name, "service") == 0)
-+ dev->service_pos = pos;
-+ else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0)
-+ dev->callsetup_pos = pos;
-+ pos++;
-+ }
-+
-+ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
-+
-+ } else {
-+
-+ int pos = 1, len = 0;
-+ char val[128];
-+ const char * start = data;
-+
-+ while (*data) {
-+ if (*data == ',') {
-+ memset(val, 0, 128);
-+ strncpy(val, start, len);
-+ set_cind(dev, pos, atoi(val));
-+ pos++;
-+ len = 0;
-+ data++;
-+ start = data;
-+ continue;
-+ }
-+ len++;
-+ data++;
-+ }
-+
-+ memset(val, 0, 128);
-+ strncpy(val, start, len);
-+ ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val);
-+
-+
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * handle an incoming call
-+ */
-+static int
-+ag_unsol_clip(blt_dev_t * dev, const char * data)
-+{
-+ const char * orig = data;
-+ char name[256];
-+ char number[64];
-+ int type;
-+
-+ while (*(data) && *(data) == ' ')
-+ data++;
-+
-+ if (*(data) == 0) {
-+ ast_log(LOG_WARNING, "Invalid value[1] for '+CLIP:%s'\n", orig);
-+ return -1;
-+ }
-+
-+ parse_clip(data, number, sizeof(number)-1, name, sizeof(name)-1, &type);
-+ ast_log(LOG_NOTICE, "Parsed '+CLIP: %s' number='%s' type='%d' name='%s'\n", data, number, type, name);
-+
-+ blt_new(dev, AST_STATE_RING, dev->context, "s");
-+
-+ return 0;
-+}
-+
-+
-+
-+static blt_atcb_t
-+atcmd_list[] =
-+{
-+ { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL },
-+ { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL },
-+ { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL },
-+ { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL },
-+ { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL },
-+ { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL },
-+ { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev },
-+ { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind },
-+ { "*EAMI", NULL, NULL, atcmd_eami_execute, NULL, NULL},
-+ { "*EAII", NULL, NULL, atcmd_eaii_execute, NULL, NULL},
-+
-+ { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL },
-+ { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, ag_unsol_clip },
-+ { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL },
-+ { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL },
-+ { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL },
-+ { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL },
-+ { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL },
-+ { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL },
-+ { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL },
-+ { "+BLDN", NULL, NULL, atcmd_bldn_execute, NULL, NULL },
-+};
-+
-+#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t))
-+
-+/* ---------------------------------- */
-+
-+/* -- Handle negotiation when we're a HS -- */
-+
-+void
-+ag_unknown_response(blt_dev_t * dev, char * cmd)
-+{
-+ ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd);
-+
-+ // DELAYED
-+ // NO CARRIER
-+
-+}
-+
-+void
-+gui_easm_response(blt_dev_t * dev, char * cmd)
-+{
-+ ast_log(LOG_NOTICE, "Menu displayed.\n");
-+}
-+
-+void
-+ag_cgmi_response(blt_dev_t * dev, char * cmd)
-+{
-+ // CGMM - Phone Model
-+ // CGMR - Phone Revision
-+ // CGSN - IMEI
-+ // AT*
-+ // VTS - send tone
-+ // CREG
-+ // CBC - BATTERY
-+ // CSQ - SIGANL
-+ // CSMS - SMS STUFFS
-+ // CMGL
-+ // CMGR
-+ // CMGS
-+ // CSCA - sms CENTER NUMBER
-+ // CNMI - SMS INDICATION
-+ // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd);
-+
-+ if (dev->role == BLT_ROLE_GUI) {
-+ ast_log(LOG_NOTICE, "Displaying Menu.\n");
-+ dev->cb = gui_easm_response;
-+// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
-+ send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
-+ } else {
-+ dev->cb = ag_unknown_response;
-+ }
-+}
-+
-+void
-+ag_cgmi_valid_response(blt_dev_t * dev, char * cmd)
-+{
-+ // send_atcmd(dev, "AT+WS46?");
-+ // send_atcmd(dev, "AT+CRC=1");
-+ // send_atcmd(dev, "AT+CNUM");
-+
-+ if (strcmp(cmd, "OK") == 0) {
-+ send_atcmd(dev, "AT+CGMI");
-+ dev->cb = ag_cgmi_response;
-+ } else {
-+ dev->cb = ag_unknown_response;
-+ }
-+}
-+
-+void
-+ag_clip_response(blt_dev_t * dev, char * cmd)
-+{
-+ send_atcmd(dev, "AT+CGMI=?");
-+ dev->cb = ag_cgmi_valid_response;
-+}
-+
-+void
-+ag_cmer_response(blt_dev_t * dev, char * cmd)
-+{
-+ dev->cb = ag_clip_response;
-+ dev->ready = 1;
-+ dev->status = BLT_STATUS_READY;
-+ send_atcmd(dev, "AT+CLIP=1");
-+}
-+
-+void
-+ag_cind_status_response(blt_dev_t * dev, char * cmd)
-+{
-+ // XXX:T: Handle response.
-+ dev->cb = ag_cmer_response;
-+ send_atcmd(dev, "AT+CMER=3,0,0,1");
-+ // Initiliase SCO link!
-+}
-+
-+void
-+ag_cind_response(blt_dev_t * dev, char * cmd)
-+{
-+ dev->cb = ag_cind_status_response;
-+ dev->cind = 1;
-+ send_atcmd(dev, "AT+CIND?");
-+}
-+
-+void
-+ag_brsf_response(blt_dev_t * dev, char * cmd)
-+{
-+ dev->cb = ag_cind_response;
-+ ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd);
-+ dev->cind = 0;
-+ send_atcmd(dev, "AT+CIND=?");
-+}
-+
-+/* ---------------------------------- */
-+
-+static int
-+sdp_register(sdp_session_t * session)
-+{
-+ // XXX:T: Fix this horrible function so it makes some sense and is extensible!
-+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
-+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
-+ sdp_profile_desc_t profile;
-+ sdp_list_t *aproto, *proto[2];
-+ sdp_record_t record;
-+ uint8_t u8 = rfcomm_channel_ag;
-+ uint8_t u8_hs = rfcomm_channel_hs;
-+ sdp_data_t *channel;
-+ int ret = 0;
-+
-+ memset((void *)&record, 0, sizeof(sdp_record_t));
-+ record.handle = 0xffffffff;
-+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-+ root = sdp_list_append(0, &root_uuid);
-+ sdp_set_browse_groups(&record, root);
-+
-+ // Register as an AG
-+
-+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
-+ svclass_id = sdp_list_append(0, &svclass_uuid);
-+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
-+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
-+ sdp_set_service_classes(&record, svclass_id);
-+ sdp_uuid16_create(&profile.uuid, 0x111f);
-+ profile.version = 0x0100;
-+ pfseq = sdp_list_append(0, &profile);
-+
-+ sdp_set_profile_descs(&record, pfseq);
-+
-+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
-+ proto[0] = sdp_list_append(0, &l2cap_uuid);
-+ apseq = sdp_list_append(0, proto[0]);
-+
-+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
-+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
-+ channel = sdp_data_alloc(SDP_UINT8, &u8);
-+ proto[1] = sdp_list_append(proto[1], channel);
-+ apseq = sdp_list_append(apseq, proto[1]);
-+
-+ aproto = sdp_list_append(0, apseq);
-+ sdp_set_access_protos(&record, aproto);
-+
-+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
-+
-+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
-+ ast_log(LOG_ERROR, "Service Record registration failed\n");
-+ ret = -1;
-+ goto end;
-+ }
-+
-+ sdp_record_ag = record.handle;
-+ sdp_record_gui = record.handle;
-+
-+ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
-+
-+ sdp_data_free(channel);
-+ sdp_list_free(proto[0], 0);
-+ sdp_list_free(proto[1], 0);
-+ sdp_list_free(apseq, 0);
-+ sdp_list_free(aproto, 0);
-+
-+ // -------------
-+
-+ memset((void *)&record, 0, sizeof(sdp_record_t));
-+ record.handle = 0xffffffff;
-+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-+ root = sdp_list_append(0, &root_uuid);
-+ sdp_set_browse_groups(&record, root);
-+
-+ // Register as an HS
-+
-+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
-+ svclass_id = sdp_list_append(0, &svclass_uuid);
-+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
-+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
-+ sdp_set_service_classes(&record, svclass_id);
-+ sdp_uuid16_create(&profile.uuid, 0x111e);
-+ profile.version = 0x0100;
-+ pfseq = sdp_list_append(0, &profile);
-+ sdp_set_profile_descs(&record, pfseq);
-+
-+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
-+ proto[0] = sdp_list_append(0, &l2cap_uuid);
-+ apseq = sdp_list_append(0, proto[0]);
-+
-+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
-+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
-+ channel = sdp_data_alloc(SDP_UINT8, &u8_hs);
-+ proto[1] = sdp_list_append(proto[1], channel);
-+ apseq = sdp_list_append(apseq, proto[1]);
-+
-+ aproto = sdp_list_append(0, apseq);
-+ sdp_set_access_protos(&record, aproto);
-+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
-+
-+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
-+ ast_log(LOG_ERROR, "Service Record registration failed\n");
-+ ret = -1;
-+ goto end;
-+ }
-+
-+ sdp_record_hs = record.handle;
-+
-+ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
-+
-+end:
-+ sdp_data_free(channel);
-+ sdp_list_free(proto[0], 0);
-+ sdp_list_free(proto[1], 0);
-+ sdp_list_free(apseq, 0);
-+ sdp_list_free(aproto, 0);
-+
-+ return ret;
-+}
-+
-+static int
-+rfcomm_listen(bdaddr_t * bdaddr, int channel)
-+{
-+
-+ int sock = -1;
-+ struct sockaddr_rc loc_addr;
-+ int on = 1;
-+
-+ if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
-+ ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno);
-+ return -1;
-+ }
-+
-+ loc_addr.rc_family = AF_BLUETOOTH;
-+
-+ /* Local Interface Address */
-+ bacpy(&loc_addr.rc_bdaddr, bdaddr);
-+
-+ /* Channel */
-+ loc_addr.rc_channel = channel;
-+
-+ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
-+ ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno);
-+ close(sock);
-+ return -1;
-+ }
-+
-+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
-+ ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
-+ close(sock);
-+ return -1;
-+ }
-+
-+ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
-+ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
-+
-+ if (listen(sock, 10) < 0) {
-+ ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno);
-+ close(sock);
-+ return -1;
-+ }
-+
-+ ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock);
-+
-+ return sock;
-+}
-+
-+
-+static int
-+sco_listen(bdaddr_t * bdaddr)
-+{
-+ int sock = -1;
-+ int on = 1;
-+ struct sockaddr_sco loc_addr;
-+
-+ memset(&loc_addr, 0, sizeof(loc_addr));
-+
-+ if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
-+ ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno);
-+ return -1;
-+ }
-+
-+ loc_addr.sco_family = AF_BLUETOOTH;
-+ bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY);
-+
-+ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
-+ ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno);
-+ close(sock);
-+ return -1;
-+ }
-+
-+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
-+ ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
-+ close(sock);
-+ return -1;
-+ }
-+
-+ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
-+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
-+
-+ if (listen(sock, 10) < 0) {
-+ ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno);
-+ close(sock);
-+ return -1;
-+ }
-+
-+ ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock);
-+
-+ return sock;
-+}
-+
-+static int
-+rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio)
-+{
-+ struct sockaddr_rc addr;
-+ int s;
-+
-+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
-+ return -1;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.rc_family = AF_BLUETOOTH;
-+ bacpy(&addr.rc_bdaddr, src);
-+ addr.rc_channel = 0;
-+
-+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ close(s);
-+ return -1;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.rc_family = AF_BLUETOOTH;
-+ bacpy(&addr.rc_bdaddr, dst);
-+ addr.rc_channel = channel;
-+
-+ if (nbio) {
-+ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
-+ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
-+ }
-+
-+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) {
-+ close(s);
-+ return -1;
-+ }
-+
-+ return s;
-+}
-+
-+/* Must be called with dev->lock held */
-+
-+static int
-+sco_connect(blt_dev_t * dev)
-+{
-+ struct sockaddr_sco addr;
-+ // struct sco_conninfo conn;
-+ // struct sco_options opts;
-+ // int size;
-+ // bdaddr_t * src = &local_bdaddr;
-+
-+ int s;
-+ bdaddr_t * dst = &(dev->bdaddr);
-+
-+ if (dev->sco != -1) {
-+ ast_log(LOG_ERROR, "SCO fd already open.\n");
-+ return -1;
-+ }
-+
-+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
-+ ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno));
-+ return -1;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+
-+ addr.sco_family = AF_BLUETOOTH;
-+ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
-+
-+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno));
-+ close(s);
-+ return -1;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.sco_family = AF_BLUETOOTH;
-+ bacpy(&addr.sco_bdaddr, dst);
-+
-+ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
-+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
-+
-+ if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) {
-+ ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno);
-+ close(s);
-+ return -1;
-+ }
-+
-+ //size = sizeof(conn);
-+
-+
-+/* XXX:T: HERE, fix getting SCO conninfo.
-+
-+ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
-+ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
-+ close(s);
-+ return -1;
-+ }
-+
-+ size = sizeof(opts);
-+
-+ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
-+ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
-+ close(s);
-+ return -1;
-+ }
-+
-+ dev->sco_handle = conn.hci_handle;
-+ dev->sco_mtu = opts.mtu;
-+
-+*/
-+
-+ ast_log(LOG_DEBUG, "SCO: %d\n", s);
-+
-+ dev->sco = s;
-+
-+ return 0;
-+}
-+
-+
-+/* ---------------------------------- */
-+
-+/* Non blocking (async) outgoing bluetooth connection */
-+
-+static int
-+try_connect(blt_dev_t * dev)
-+{
-+ int fd;
-+ ast_mutex_lock(&(dev->lock));
-+
-+ if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) {
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+ }
-+
-+ if (dev->rd != -1) {
-+
-+ int ret;
-+ struct pollfd pfd;
-+
-+ if (dev->status != BLT_STATUS_CONNECTING) {
-+ ast_mutex_unlock(&(dev->lock));
-+ dev->outgoing_id = -1;
-+ return 0;
-+ }
-+
-+ // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); //
-+
-+ pfd.fd = dev->rd;
-+ pfd.events = POLLIN | POLLOUT;
-+
-+ ret = poll(&pfd, 1, 0);
-+
-+ if (ret == -1) {
-+ close(dev->rd);
-+ dev->rd = -1;
-+ dev->status = BLT_STATUS_DOWN;
-+ dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev);
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+ }
-+
-+ if (ret > 0) {
-+
-+ int len = sizeof(ret);
-+ getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len);
-+
-+ if (ret == 0) {
-+
-+ ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name);
-+
-+#if 0
-+ {
-+ struct hci_conn_info_req * cr;
-+ int dd;
-+ char name[248];
-+
-+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
-+ dd = hci_open_dev(hcidev_id);
-+ cr->type = ACL_LINK;
-+ bacpy(&cr->bdaddr, &(dev->bdaddr));
-+
-+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) {
-+ ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno));
-+ } else {
-+ ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle);
-+ }
-+
-+ if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0)
-+ ast_log(LOG_DEBUG, "Remote Name: %s\n", name);
-+ free(cr);
-+ }
-+#endif
-+
-+ dev->status = BLT_STATUS_NEGOTIATING;
-+
-+ /* If this device is an AG/GUI, we initiate the negotiation. */
-+
-+ if (dev->role == BLT_ROLE_AG ||
-+ dev->role == BLT_ROLE_GUI) {
-+ dev->cb = ag_brsf_response;
-+ send_atcmd(dev, "AT+BRSF=23");
-+ }
-+
-+ dev->outgoing_id = -1;
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+
-+ } else {
-+
-+ if (ret != EHOSTDOWN)
-+ ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret);
-+
-+ close(dev->rd);
-+ dev->rd = -1;
-+ dev->status = BLT_STATUS_DOWN;
-+ dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev);
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+
-+ }
-+
-+ }
-+
-+ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+ }
-+
-+ ast_log(LOG_NOTICE, "RFCOMM connect start.\n");
-+ fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1);
-+ ast_log(LOG_NOTICE, "RFCOMM connect done.\n");
-+
-+ if (fd == -1) {
-+ ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno));
-+ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+ }
-+
-+ dev->rd = fd;
-+ dev->status = BLT_STATUS_CONNECTING;
-+ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
-+ ast_mutex_unlock(&(dev->lock));
-+ return 0;
-+}
-+
-+
-+/* Called whenever a new command is received while we're the AG */
-+
-+
-+static int
-+process_rfcomm_cmd(blt_dev_t * dev, char * cmd)
-+{
-+ int i;
-+ char * fullcmd = cmd;
-+
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd);
-+
-+ /* Read the 'AT' from the start of the string */
-+ if (strncmp(cmd, "AT", 2)) {
-+ ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd);
-+ send_atcmd_error(dev);
-+ return 0;
-+ }
-+
-+ cmd += 2;
-+
-+ // Don't forget 'AT' on its own is OK.
-+
-+ if (strlen(cmd) == 0) {
-+ send_atcmd_ok(dev, fullcmd);
-+ return 0;
-+ }
-+
-+ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
-+ if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) {
-+ char * pos = (cmd + strlen(atcmd_list[i].str));
-+ if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) {
-+ /* TEST command */
-+ if (atcmd_list[i].test) {
-+ if (atcmd_list[i].test(dev) == 0)
-+ send_atcmd_ok(dev, fullcmd);
-+ else
-+ send_atcmd_error(dev);
-+ } else {
-+ send_atcmd_ok(dev, fullcmd);
-+ }
-+ } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) {
-+ /* READ command */
-+ if (atcmd_list[i].read) {
-+ if (atcmd_list[i].read(dev) == 0)
-+ send_atcmd_ok(dev, fullcmd);
-+ else
-+ send_atcmd_error(dev);
-+ } else {
-+ ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd);
-+ send_atcmd_error(dev);
-+ }
-+ } else if (strncmp(pos, "=", 1) == 0) {
-+ /* SET command */
-+ if (atcmd_list[i].set) {
-+ if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0)
-+ send_atcmd_ok(dev, fullcmd);
-+ else
-+ send_atcmd_error(dev);
-+ } else {
-+ ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd);
-+ send_atcmd_error(dev);
-+ }
-+ } else {
-+ /* EXECUTE command */
-+ if (atcmd_list[i].execute) {
-+ if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0)
-+ send_atcmd_ok(dev, fullcmd);
-+ else
-+ send_atcmd_error(dev);
-+ } else {
-+ ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd);
-+ send_atcmd_error(dev);
-+ }
-+ }
-+ return 0;
-+ }
-+ }
-+
-+ ast_log(LOG_NOTICE, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd);
-+ send_atcmd_error(dev);
-+
-+ return 0;
-+}
-+
-+/* Called when a socket is incoming */
-+
-+static void
-+handle_incoming(int fd, blt_role_t role)
-+{
-+ blt_dev_t * dev;
-+ struct sockaddr_rc addr;
-+ int len = sizeof(addr);
-+
-+ // Got a new incoming socket.
-+ ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n");
-+
-+ ast_mutex_lock(&iface_lock);
-+
-+ fd = accept(fd, (struct sockaddr*)&addr, &len);
-+
-+ dev = iface_head;
-+ while (dev) {
-+ if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) {
-+ ast_log(LOG_DEBUG, "Connect from %s\n", dev->name);
-+ ast_mutex_lock(&(dev->lock));
-+ /* Kill any outstanding connect attempt. */
-+ if (dev->outgoing_id > -1) {
-+ ast_sched_del(sched, dev->outgoing_id);
-+ dev->outgoing_id = -1;
-+ }
-+
-+ rd_close(dev, 0, 0);
-+
-+ dev->status = BLT_STATUS_NEGOTIATING;
-+ dev->rd = fd;
-+
-+ if (dev->role == BLT_ROLE_AG ||
-+ dev->role == BLT_ROLE_GUI) {
-+ dev->cb = ag_brsf_response;
-+ send_atcmd(dev, "AT+BRSF=23");
-+ }
-+ ast_mutex_unlock(&(dev->lock));
-+ break;
-+ }
-+ dev = dev->next;
-+ }
-+
-+ if (dev == NULL) {
-+ ast_log(LOG_WARNING, "Connect from unknown device\n");
-+ close(fd);
-+ }
-+ ast_mutex_unlock(&iface_lock);
-+
-+ return;
-+}
-+
-+static void
-+handle_incoming_sco(int master)
-+{
-+
-+ blt_dev_t * dev;
-+ struct sockaddr_sco addr;
-+ struct sco_conninfo conn;
-+ struct sco_options opts;
-+ int len = sizeof(addr);
-+ int fd;
-+
-+ ast_log(LOG_DEBUG, "Incoming SCO socket\n");
-+
-+ fd = accept(master, (struct sockaddr*)&addr, &len);
-+
-+ if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) {
-+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
-+ close(fd);
-+ return;
-+ }
-+
-+ len = sizeof(conn);
-+
-+ if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) {
-+ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
-+ close(fd);
-+ return;
-+ }
-+
-+ len = sizeof(opts);
-+
-+ if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) {
-+ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
-+ close(fd);
-+ return;
-+ }
-+
-+ ast_mutex_lock(&iface_lock);
-+ dev = iface_head;
-+ while (dev) {
-+ if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) {
-+ ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name);
-+ ast_mutex_lock(&(dev->lock));
-+ if (dev->sco_running != -1) {
-+ ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n");
-+ } else {
-+ sco_start(dev, fd);
-+ }
-+ ast_mutex_unlock(&(dev->lock));
-+ break;
-+ }
-+ dev = dev->next;
-+ }
-+
-+ ast_mutex_unlock(&iface_lock);
-+
-+ if (dev == NULL) {
-+ ast_log(LOG_WARNING, "SCO Connect from unknown device\n");
-+ close(fd);
-+ } else {
-+ // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress.
-+ ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu);
-+ }
-+
-+
-+
-+ return;
-+}
-+
-+/* Called when there is data waiting on a socket */
-+
-+static int
-+handle_rd_data(blt_dev_t * dev)
-+{
-+ char c;
-+ int ret;
-+
-+ while ((ret = read(dev->rd, &c, 1)) == 1) {
-+
-+ // log_buf[i++] = c;
-+
-+ if (dev->role == BLT_ROLE_HS) {
-+
-+ if (c == '\r') {
-+ ret = process_rfcomm_cmd(dev, dev->rd_buff);
-+ dev->rd_buff_pos = 0;
-+ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
-+ return ret;
-+ }
-+
-+ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX)
-+ return 0;
-+
-+ dev->rd_buff[dev->rd_buff_pos++] = c;
-+
-+ } else if (dev->role == BLT_ROLE_AG ||
-+ dev->role == BLT_ROLE_GUI) {
-+
-+ //ast_log(LOG_ERROR, "%s: %c\n", dev->name, c);
-+
-+ switch (dev->state) {
-+ case BLT_STATE_WANT_R:
-+ if (c == '\r' || c == 10) {
-+ dev->state = BLT_STATE_WANT_N;
-+ } else if (c == '+') {
-+ dev->state = BLT_STATE_WANT_CMD;
-+ dev->rd_buff[dev->rd_buff_pos++] = '+';
-+ } else {
-+ ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c);
-+ return -1;
-+ }
-+ break;
-+
-+ case BLT_STATE_WANT_N:
-+ if (c == '\n' || c == 13)
-+ dev->state = BLT_STATE_WANT_CMD;
-+ else {
-+ ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c);
-+ return -1;
-+ }
-+ break;
-+
-+ case BLT_STATE_WANT_CMD:
-+ if (c == '\r' || c == 10)
-+ dev->state = BLT_STATE_WANT_N2;
-+ else {
-+ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) {
-+ ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name);
-+ return -1;
-+ }
-+ dev->rd_buff[dev->rd_buff_pos++] = c;
-+ }
-+ break;
-+
-+ case BLT_STATE_WANT_N2:
-+ if (c == '\n' || c == 13) {
-+
-+ dev->state = BLT_STATE_WANT_R;
-+
-+ if (dev->rd_buff[0] == '+') {
-+ int i;
-+ // find unsolicited
-+ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
-+ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
-+ if (atcmd_list[i].unsolicited)
-+ atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
-+ else
-+ ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff);
-+ break;
-+ }
-+ }
-+
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
-+
-+ if (i == ATCMD_LIST_LEN)
-+ ast_log(LOG_NOTICE, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff);
-+
-+ } else if (dev->rd_buff[0] == '*') {
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s]* %*s > %s\n", role2str(dev->role), 9, dev->name, dev->rd_buff);
-+
-+ int i;
-+ // find execute
-+ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
-+ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
-+ if (atcmd_list[i].execute)
-+ atcmd_list[i].execute(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
-+ else
-+ ast_log(LOG_ERROR, "Device %s: Unhandled Execute: %s\n", dev->name, dev->rd_buff);
-+ break;
-+ }
-+ }
-+
-+
-+ } else {
-+
-+ if (
-+ strcmp(dev->rd_buff, "OK") != 0 &&
-+ strcmp(dev->rd_buff, "CONNECT") != 0 &&
-+ strcmp(dev->rd_buff, "RING") != 0 &&
-+ strcmp(dev->rd_buff, "NO CARRIER") != 0 &&
-+ strcmp(dev->rd_buff, "ERROR") != 0 &&
-+ strcmp(dev->rd_buff, "NO DIALTONE") != 0 &&
-+ strcmp(dev->rd_buff, "BUSY") != 0 &&
-+ strcmp(dev->rd_buff, "NO ANSWER") != 0 &&
-+ strcmp(dev->rd_buff, "DELAYED") != 0
-+ ){
-+ // It must be a multiline error
-+ strncpy(dev->last_err_cmd, dev->rd_buff, 1023);
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
-+ } else if (dev->cb) {
-+ if (option_verbose)
-+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
-+ dev->cb(dev, dev->rd_buff);
-+ } else {
-+ ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name);
-+ }
-+
-+ }
-+
-+ dev->rd_buff_pos = 0;
-+ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
-+ } else {
-+
-+ ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c);
-+ return -1;
-+
-+ }
-+
-+ break;
-+
-+ default:
-+ ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state);
-+ return -1;
-+
-+ }
-+
-+ }
-+
-+ }
-+
-+ return 0;
-+}
-+
-+/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */
-+
-+static void
-+rd_close(blt_dev_t * dev, int reconnect, int e)
-+{
-+ dev->ready = 0;
-+
-+ if (dev->rd)
-+ close(dev->rd);
-+
-+ dev->rd = -1;
-+
-+ dev->status = BLT_STATUS_DOWN;
-+
-+ sco_stop(dev);
-+
-+ if (dev->owner) {
-+ ast_setstate(dev->owner, AST_STATE_DOWN);
-+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
-+ }
-+
-+ /* Schedule a reconnect */
-+ if (reconnect && dev->autoconnect) {
-+ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
-+
-+ if (monitor_thread == pthread_self()) {
-+ // Because we're not the monitor thread, we needd to inturrupt poll().
-+ pthread_kill(monitor_thread, SIGURG);
-+ }
-+
-+ if (e)
-+ ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e);
-+ } else if (e) {
-+ ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e);
-+ }
-+
-+ return;
-+}
-+
-+/*
-+ * Remember that we can only add to the scheduler from
-+ * the do_monitor thread, as it calculates time to next one from
-+ * this loop.
-+ */
-+
-+static void *
-+do_monitor(void * data)
-+{
-+#define SRV_SOCK_CNT 4
-+
-+ int res = 0;
-+ blt_dev_t * dev;
-+ struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT));
-+
-+ /* -- We start off by trying to connect all of our devices (non blocking) -- */
-+
-+ monitor_pid = getpid();
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
-+ return NULL;
-+ }
-+
-+ dev = iface_head;
-+ while (dev) {
-+
-+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) {
-+ ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno);
-+ ast_mutex_unlock(&iface_lock);
-+ return NULL;
-+ }
-+
-+ if (dev->autoconnect && dev->status == BLT_STATUS_DOWN)
-+ dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev);
-+ dev = dev->next;
-+ }
-+ ast_mutex_unlock(&iface_lock);
-+
-+ /* -- Now, Scan all sockets, and service scheduler -- */
-+
-+ pfds[0].fd = rfcomm_sock_ag;
-+ pfds[0].events = POLLIN;
-+
-+ pfds[1].fd = rfcomm_sock_hs;
-+ pfds[1].events = POLLIN;
-+
-+ pfds[2].fd = rfcomm_sock_gui;
-+ pfds[2].events = POLLIN;
-+
-+ pfds[3].fd = sco_socket;
-+ pfds[3].events = POLLIN;
-+
-+ while (1) {
-+ int cnt = SRV_SOCK_CNT;
-+ int i;
-+
-+ /* -- Build pfds -- */
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
-+ return NULL;
-+ }
-+ dev = iface_head;
-+ while (dev) {
-+ ast_mutex_lock(&(dev->lock));
-+ if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) {
-+ pfds[cnt].fd = dev->rd;
-+ pfds[cnt].events = POLLIN;
-+ cnt++;
-+ }
-+ ast_mutex_unlock(&(dev->lock));
-+ dev = dev->next;
-+ }
-+ ast_mutex_unlock(&iface_lock);
-+
-+ /* -- End Build pfds -- */
-+
-+ res = ast_sched_wait(sched);
-+ res = poll(pfds, cnt, MAX(100, MIN(100, res)));
-+
-+ if (res == 0)
-+ ast_sched_runq(sched);
-+
-+ if (pfds[0].revents) {
-+ handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG);
-+ res--;
-+ }
-+
-+ if (pfds[1].revents) {
-+ handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS);
-+ res--;
-+ }
-+
-+ if (pfds[2].revents) {
-+ handle_incoming(rfcomm_sock_gui, BLT_ROLE_GUI);
-+ res--;
-+ }
-+
-+ if (pfds[3].revents) {
-+ handle_incoming_sco(sco_socket);
-+ res--;
-+ }
-+
-+ if (res == 0)
-+ continue;
-+
-+ for (i = SRV_SOCK_CNT ; i < cnt ; i++) {
-+
-+ /* Optimise a little bit */
-+ if (res == 0)
-+ break;
-+ else if (pfds[i].revents == 0)
-+ continue;
-+
-+ /* -- Find the socket that has activity -- */
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
-+ return NULL;
-+ }
-+
-+ dev = iface_head;
-+
-+ while (dev) {
-+ if (pfds[i].fd == dev->rd) {
-+ ast_mutex_lock(&(dev->lock));
-+ if (pfds[i].revents & POLLIN) {
-+ if (handle_rd_data(dev) == -1) {
-+ rd_close(dev, 0, 0);
-+ }
-+ } else {
-+ rd_close(dev, 1, sock_err(dev->rd));
-+ }
-+ ast_mutex_unlock(&(dev->lock));
-+ res--;
-+ break;
-+ }
-+ dev = dev->next;
-+ }
-+
-+ if (dev == NULL) {
-+ ast_log(LOG_ERROR, "Unhandled fd from poll()\n");
-+ close(pfds[i].fd);
-+ }
-+
-+ ast_mutex_unlock(&iface_lock);
-+
-+ /* -- End find socket with activity -- */
-+
-+ }
-+
-+ }
-+
-+ return NULL;
-+}
-+
-+static int
-+restart_monitor(void)
-+{
-+
-+ if (monitor_thread == AST_PTHREADT_STOP)
-+ return 0;
-+
-+ if (ast_mutex_lock(&monitor_lock)) {
-+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
-+ return -1;
-+ }
-+
-+ if (monitor_thread == pthread_self()) {
-+ ast_mutex_unlock(&monitor_lock);
-+ ast_log(LOG_WARNING, "Cannot kill myself\n");
-+ return -1;
-+ }
-+
-+ if (monitor_thread != AST_PTHREADT_NULL) {
-+
-+ /* Just signal it to be sure it wakes up */
-+ pthread_cancel(monitor_thread);
-+ pthread_kill(monitor_thread, SIGURG);
-+ ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n");
-+ pthread_join(monitor_thread, NULL);
-+ ast_log(LOG_DEBUG, "joined\n");
-+
-+ } else {
-+
-+ /* Start a new monitor */
-+ if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
-+ ast_mutex_unlock(&monitor_lock);
-+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
-+ return -1;
-+ }
-+
-+ }
-+
-+ ast_mutex_unlock(&monitor_lock);
-+ return 0;
-+}
-+
-+static int
-+blt_parse_config(void)
-+{
-+ struct ast_config * cfg;
-+ struct ast_variable * v;
-+ char * cat;
-+
-+ cfg = ast_load(BLT_CONFIG_FILE);
-+
-+ if (!cfg) {
-+ ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
-+ return -1;
-+ }
-+
-+ v = ast_variable_browse(cfg, "general");
-+
-+ while (v) {
-+ if (!strcasecmp(v->name, "rfchannel_ag")) {
-+ rfcomm_channel_ag = atoi(v->value);
-+ } else if (!strcasecmp(v->name, "rfchannel_hs")) {
-+ rfcomm_channel_hs = atoi(v->value);
-+ } else if (!strcasecmp(v->name, "rfchannel_gui")) {
-+ rfcomm_channel_gui = atoi(v->value);
-+ } else if (!strcasecmp(v->name, "interface")) {
-+ hcidev_id = atoi(v->value);
-+ } else if (!strcasecmp(v->name, "gui_default_sip_number")) {
-+ gui_default_sip_number = v->value;
-+ } else if (!strcasecmp(v->name, "gui_default_sip_address")) {
-+ gui_default_sip_address = v->value;
-+ } else {
-+ ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name);
-+ }
-+ v = v->next;
-+ }
-+ cat = ast_category_browse(cfg, NULL);
-+
-+ while(cat) {
-+
-+ char * str;
-+
-+ if (strcasecmp(cat, "general")) {
-+ blt_dev_t * device = malloc(sizeof(blt_dev_t));
-+ memset(device, 0, sizeof(blt_dev_t));
-+ device->sco_running = -1;
-+ device->sco = -1;
-+ device->rd = -1;
-+ device->outgoing_id = -1;
-+ device->status = BLT_STATUS_DOWN;
-+ str2ba(cat, &(device->bdaddr));
-+ device->name = ast_variable_retrieve(cfg, cat, "name");
-+
-+ str = ast_variable_retrieve(cfg, cat, "type");
-+
-+ if (str == NULL) {
-+ ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=<HS/AG>\n", cat);
-+ return -1;
-+ } else if (strcasecmp(str, "HS") == 0) {
-+ device->role = BLT_ROLE_HS;
-+ } else if (strcasecmp(str, "AG") == 0) {
-+ device->role = BLT_ROLE_AG;
-+ } else if (strcasecmp(str, "GUI") == 0) {
-+ device->role = BLT_ROLE_GUI;
-+ } else {
-+ ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str);
-+ return -1;
-+ }
-+
-+ /* XXX:T: Find channel to use using SDP.
-+ * However, this needs to be non blocking, and I can't see
-+ * anything in sdp_lib.h that will allow non blocking calls.
-+ */
-+
-+ device->channel = 1;
-+
-+ if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL)
-+ device->channel = atoi(str);
-+
-+ if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL)
-+ device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0;
-+
-+ if ((str = ast_variable_retrieve(cfg, cat, "context")) != NULL)
-+ device->context = str;
-+ else
-+ device->context = "bluetooth";
-+
-+ device->next = iface_head;
-+ iface_head = device;
-+ ifcount++;
-+ }
-+
-+ cat = ast_category_browse(cfg, cat);
-+ }
-+ return 0;
-+}
-+
-+
-+static int
-+blt_show_peers(int fd, int argc, char *argv[])
-+{
-+ blt_dev_t * dev;
-+
-+ if (ast_mutex_lock(&iface_lock)) {
-+ ast_log(LOG_ERROR, "Failed to get Iface lock\n");
-+ ast_cli(fd, "Failed to get iface lock\n");
-+ return RESULT_FAILURE;
-+ }
-+
-+ dev = iface_head;
-+
-+ ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n");
-+ ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n");
-+
-+ while (dev) {
-+ char b1[18];
-+ ba2str(&(dev->bdaddr), b1);
-+ ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n",
-+ b1, dev->name,
-+// (dev->role == BLT_ROLE_HS) ? "HS" : "AG",
-+ (dev->role == BLT_ROLE_HS) ? "HS" : (dev->role == BLT_ROLE_AG) ? "AG" : "GUI",
-+ status2str(dev->status),
-+ (dev->autoconnect) ? "Yes" : "No",
-+ dev->sco_running,
-+ dev->sco,
-+ dev->sco_thread,
-+ (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A"
-+ );
-+ dev = dev->next;
-+ }
-+
-+ ast_mutex_unlock(&iface_lock);
-+ return RESULT_SUCCESS;
-+}
-+
-+static int
-+blt_show_information(int fd, int argc, char *argv[])
-+{
-+ char b1[18];
-+ ba2str(&local_bdaddr, b1);
-+ ast_cli(fd, "-------------------------------------------\n");
-+ ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION);
-+ ast_cli(fd, " Monitor PID : %d\n", monitor_pid);
-+ ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag);
-+ ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs);
-+ ast_cli(fd, " RFCOMM GUI : Channel %d, FD %d\n", rfcomm_channel_gui, rfcomm_sock_gui);
-+ ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1);
-+ ast_cli(fd, "-------------------------------------------\n");
-+ return RESULT_SUCCESS;
-+}
-+
-+static int
-+blt_ag_sendcmd(int fd, int argc, char *argv[])
-+{
-+ blt_dev_t * dev;
-+
-+ if (argc != 4)
-+ return RESULT_SHOWUSAGE;
-+
-+ ast_mutex_lock(&iface_lock);
-+ dev = iface_head;
-+ while (dev) {
-+ if (!strcasecmp(argv[2], dev->name))
-+ break;
-+ dev = dev->next;
-+ }
-+ ast_mutex_unlock(&iface_lock);
-+
-+ if (!dev) {
-+ ast_cli(fd, "Device '%s' does not exist\n", argv[2]);
-+ return RESULT_FAILURE;
-+ }
-+
-+ if ((dev->role != BLT_ROLE_AG) && (dev->role != BLT_ROLE_GUI)) {
-+ ast_cli(fd, "Device '%s' is not an AG or GUI\n", argv[2]);
-+ return RESULT_FAILURE;
-+ }
-+
-+ if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) {
-+ ast_cli(fd, "Device '%s' is not connected\n", argv[2]);
-+ return RESULT_FAILURE;
-+ }
-+
-+ if (*(argv[3] + strlen(argv[3]) - 1) == '.')
-+ *(argv[3] + strlen(argv[3]) - 1) = '?';
-+
-+ ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]);
-+
-+ ast_mutex_lock(&(dev->lock));
-+ send_atcmd(dev, argv[3]);
-+ ast_mutex_unlock(&(dev->lock));
-+
-+ return RESULT_SUCCESS;
-+}
-+
-+static char *
-+complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role)
-+{
-+ blt_dev_t * dev;
-+ int which = 0;
-+ char *ret;
-+
-+ if (pos != rpos)
-+ return NULL;
-+
-+ ast_mutex_lock(&iface_lock);
-+
-+ dev = iface_head;
-+
-+ while (dev) {
-+
-+ if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) {
-+ if (++which > state)
-+ break;
-+ }
-+
-+ dev = dev->next;
-+ }
-+
-+ if (dev)
-+ ret = strdup(dev->name);
-+ else
-+ ret = NULL;
-+
-+ ast_mutex_unlock(&iface_lock);
-+
-+ return ret;
-+}
-+
-+static char *
-+complete_device_2_ag_gui(char * line, char * word, int pos, int state)
-+{
-+ return complete_device(line, word, pos, state, 2, BLT_ROLE_AG);
-+}
-+
-+static char show_peers_usage[] =
-+"Usage: bluetooth show peers\n"
-+" List all bluetooth peers and their status\n";
-+
-+static struct ast_cli_entry
-+cli_show_peers =
-+ { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage };
-+
-+
-+static char ag_sendcmd[] =
-+"Usage: bluetooth <device> sendcmd <cmd>\n"
-+" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n";
-+
-+static struct ast_cli_entry
-+cli_ag_sendcmd =
-+ { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG/GUI an AT command", ag_sendcmd, complete_device_2_ag_gui };
-+
-+static char show_information[] =
-+"Usage: bluetooth show information\n"
-+" Lists information about the bluetooth subsystem\n";
-+
-+static struct ast_cli_entry
-+cli_show_information =
-+ { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information };
-+
-+void
-+remove_sdp_records(void)
-+{
-+
-+ sdp_session_t * sdp;
-+ sdp_list_t * attr;
-+ sdp_record_t * rec;
-+ int res = -1;
-+ uint32_t range = 0x0000ffff;
-+
-+ if (sdp_record_ag == -1 || sdp_record_gui == -1 || sdp_record_hs == -1)
-+ return;
-+
-+ ast_log(LOG_DEBUG, "Removing SDP records\n");
-+
-+ sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
-+
-+ if (!sdp)
-+ return;
-+
-+ attr = sdp_list_append(0, &range);
-+ rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr);
-+ sdp_list_free(attr, 0);
-+
-+ if (rec)
-+ if (sdp_record_unregister(sdp, rec) == 0)
-+ res = 0;
-+
-+ rec = sdp_service_attr_req(sdp, sdp_record_gui, SDP_ATTR_REQ_RANGE, attr);
-+ sdp_list_free(attr, 0);
-+
-+ if (rec)
-+ if (sdp_record_unregister(sdp, rec) == 0)
-+ res = 0;
-+
-+ attr = sdp_list_append(0, &range);
-+ rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr);
-+ sdp_list_free(attr, 0);
-+
-+ if (rec)
-+ if (sdp_record_unregister(sdp, rec) == 0)
-+ res = 0;
-+
-+ sdp_close(sdp);
-+
-+ if (res == 0)
-+ ast_log(LOG_NOTICE, "Removed SDP records\n");
-+ else
-+ ast_log(LOG_ERROR, "Failed to remove SDP records\n");
-+
-+}
-+
-+static int
-+__unload_module(void)
-+{
-+
-+#if ASTERISK_VERSION_NUM <= 010107
-+ ast_channel_unregister(BLT_CHAN_NAME);
-+#else
-+ ast_channel_unregister(&blt_tech);
-+#endif
-+
-+ if (monitor_thread != AST_PTHREADT_NULL) {
-+
-+ if (ast_mutex_lock(&monitor_lock)) {
-+
-+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
-+ pthread_cancel(monitor_thread);
-+ pthread_kill(monitor_thread, SIGURG);
-+ fprintf(stderr, "Waiting for monitor thread to join...\n");
-+ pthread_join(monitor_thread, NULL);
-+ fprintf(stderr, "joined\n");
-+ }
-+ monitor_thread = AST_PTHREADT_STOP;
-+ ast_mutex_unlock(&monitor_lock);
-+
-+ } else {
-+
-+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
-+ return -1;
-+
-+ }
-+
-+ }
-+
-+ ast_unregister_atexit(remove_sdp_records);
-+ remove_sdp_records();
-+ return 0;
-+}
-+
-+int
-+load_module()
-+{
-+ sdp_session_t * sess;
-+ int dd;
-+ uint16_t vs;
-+
-+ hcidev_id = BLT_DEFAULT_HCI_DEV;
-+
-+ if (blt_parse_config() != 0) {
-+ ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n");
-+ return unload_module();
-+ }
-+
-+ dd = hci_open_dev(hcidev_id);
-+ if (dd == -1) {
-+ ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno));
-+ return -1;
-+ }
-+
-+ hci_read_voice_setting(dd, &vs, 1000);
-+ vs = htobs(vs);
-+ close(dd);
-+
-+ if (vs != 0x0060) {
-+ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs);
-+ unload_module();
-+ return 0;
-+ }
-+
-+ if ((sched = sched_context_create()) == NULL) {
-+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
-+ return -1;
-+ }
-+
-+ memset(&local_bdaddr, 0, sizeof(local_bdaddr));
-+
-+ hci_devba(hcidev_id, &local_bdaddr);
-+
-+ /* --- Add SDP record --- */
-+
-+ sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
-+
-+ if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) {
-+ return -1;
-+ }
-+
-+ if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0)
-+ return -1;
-+
-+ if ((rfcomm_sock_gui = rfcomm_listen(&local_bdaddr, rfcomm_channel_gui)) < 0)
-+ return -1;
-+
-+ if ((sco_socket = sco_listen(&local_bdaddr)) < 0)
-+ return -1;
-+
-+ if (!sess) {
-+ ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno));
-+ return -1;
-+ }
-+
-+ if (sdp_register(sess) != 0) {
-+ ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n");
-+ return -1;
-+ }
-+
-+ sdp_close(sess);
-+
-+ if (restart_monitor() != 0)
-+ return -1;
-+
-+#if ASTERISK_VERSION_NUM <= 010107
-+ if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) {
-+#else
-+ if (ast_channel_register(&blt_tech)) {
-+#endif
-+ ast_log(LOG_ERROR, "Unable to register channel class BTL\n");
-+ __unload_module();
-+ return -1;
-+ }
-+
-+ ast_cli_register(&cli_show_information);
-+ ast_cli_register(&cli_show_peers);
-+ ast_cli_register(&cli_ag_sendcmd);
-+
-+ ast_register_atexit(remove_sdp_records);
-+
-+ ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1);
-+
-+ return 0;
-+}
-+
-+int
-+unload_module(void)
-+{
-+ ast_cli_unregister(&cli_ag_sendcmd);
-+ ast_cli_unregister(&cli_show_peers);
-+ ast_cli_unregister(&cli_show_information);
-+ return __unload_module();
-+}
-+
-+int
-+usecount()
-+{
-+ int res;
-+ ast_mutex_lock(&usecnt_lock);
-+ res = usecnt;
-+ ast_mutex_unlock(&usecnt_lock);
-+ return res;
-+}
-+
-+char *description()
-+{
-+ return "Bluetooth Channel Driver";
-+}
-+
-+char *
-+key()
-+{
-+ return ASTERISK_GPL_KEY;
-+}
-+
-+
-diff -ruN asterisk-1.0.9-old/configs/bluetooth.conf asterisk-1.0.9-new/configs/bluetooth.conf
---- asterisk-1.0.9-old/configs/bluetooth.conf 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.0.9-new/configs/bluetooth.conf 2005-09-06 22:51:38.000000000 +0200
-@@ -0,0 +1,46 @@
-+[general]
-+; Channel we listen on as a HS (Headset)
-+rfchannel_hs = 2
-+; Channel we listen on as an AG (AudioGateway)
-+rfchannel_ag = 3
-+; Channel we listen on as GUI
-+rfchannel_gui = 4
-+; hci interface to use (number - e.g '0')
-+interface = 0
-+
-+; RFCOMM channel to connect to. For a HandsSet:
-+; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E
-+; or,for an AudioGateway (Phone):
-+; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F
-+;
-+; Find the 'channel' value under RFCOMM.
-+;
-+;channel = 6
-+; Automatically connect?
-+;autoconnect = yes
-+
-+;example for a SonyEricsson mobile as a GUI device
-+[00:0F:DE:6E:77:6B]
-+name = T610
-+type = GUI
-+channel = 6
-+;channel = 1
-+autoconnect = yes
-+
-+;[00:0E:6D:1A:3D:86]
-+;name = Nokia
-+;type = AG
-+;channel = 13
-+;autoconnect = yes
-+
-+[00:0E:A1:01:49:AE]
-+name = AutoBlue
-+type = HS
-+channel = 2
-+autoconnect = yes
-+
-+;[00:0A:D9:EB:FD:D8]
-+;name = P900
-+;type = AG
-+;channel = 8
-+;autoconnect = no
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/apps/Makefile asterisk-1.2.0-new/apps/Makefile
---- asterisk-1.2.0-old/apps/Makefile 2005-11-11 01:32:45.000000000 +0100
-+++ asterisk-1.2.0-new/apps/Makefile 2005-12-04 19:26:20.000000000 +0100
-@@ -83,6 +83,9 @@
- #CFLAGS+=-DEXTENDED_ODBC_STORAGE
- # See doc/README.odbcstorage for more information
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+APPS += $(EXTRA_APP_MODULES)
-+
- all: $(APPS)
-
- clean:
-@@ -102,14 +105,17 @@
- app_curl.so: app_curl.o
- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(CURLLIBS)
-
-+app_sql_mysql.so: app_sql_mysql.o
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient
-+
- app_sql_postgres.o: app_sql_postgres.c
-- $(CC) -pipe -I/usr/local/pgsql/include $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c
-+ $(CC) -pipe $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c
-
- app_sql_postgres.so: app_sql_postgres.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -L/usr/local/pgsql/lib -lpq
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq
-
- app_sql_odbc.so: app_sql_odbc.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc
-
- look: look.c
- $(CC) -pipe -O6 -g look.c -o look -lncurses
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/cdr/Makefile asterisk-1.2.0-new/cdr/Makefile
---- asterisk-1.2.0-old/cdr/Makefile 2005-11-14 01:45:07.000000000 +0100
-+++ asterisk-1.2.0-new/cdr/Makefile 2005-12-04 22:22:43.000000000 +0100
-@@ -107,6 +107,9 @@
- MODS+=cdr_sqlite.so
- endif
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+MODS += $(EXTRA_CDR_MODULES)
-+
- all: depend $(MODS)
-
- install: all
-@@ -123,16 +126,19 @@
- endif
-
- cdr_odbc.so: cdr_odbc.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lodbc $(MLFLAGS)
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lodbc $(MLFLAGS)
-
- cdr_tds.so: cdr_tds.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -ltds $(MLFLAGS)
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -ltds $(MLFLAGS)
-+
-+cdr_mysql.so: cdr_mysql.o
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz $(MLFLAGS)
-
- cdr_pgsql.so: cdr_pgsql.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lpq -lz $(MLFLAGS)
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lpq -lz $(MLFLAGS)
-
- cdr_sqlite.so: cdr_sqlite.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lsqlite $(MLFLAGS)
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite $(MLFLAGS)
-
- depend: .depend
-
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/channels/Makefile asterisk-1.2.0-new/channels/Makefile
---- asterisk-1.2.0-old/channels/Makefile 2005-12-04 04:48:40.000000000 +0100
-+++ asterisk-1.2.0-new/channels/Makefile 2005-12-04 23:30:19.000000000 +0100
-@@ -155,6 +155,9 @@
-
- #CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)")
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+CHANNEL_LIBS += $(EXTRA_CHAN_MODULES)
-+
- all: depend $(CHANNEL_LIBS)
-
- clean:
-@@ -162,7 +165,7 @@
- rm -f busy.h ringtone.h gentone gentone-ulaw
-
- %.so : %.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${LIBS}
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB}
-
- ifneq ($(wildcard .depend),)
- include .depend
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/codecs/gsm/Makefile asterisk-1.2.0-new/codecs/gsm/Makefile
---- asterisk-1.2.0-old/codecs/gsm/Makefile 2005-11-08 04:31:45.000000000 +0100
-+++ asterisk-1.2.0-new/codecs/gsm/Makefile 2005-12-04 13:31:50.000000000 +0100
-@@ -236,6 +236,8 @@
- ifneq ($(shell uname -m),armv4l)
- ifneq ($(shell uname -m),sparc64)
- ifneq (${PROC},arm)
-+ifneq (${PROC},mipsel)
-+ifneq (${PROC},mips)
- GSM_SOURCES+= $(SRC)/k6opt.s
- endif
- endif
-@@ -246,6 +247,8 @@
- endif
- endif
- endif
-+endif
-+endif
-
- TOAST_SOURCES = $(SRC)/toast.c \
- $(SRC)/toast_lin.c \
-@@ -299,6 +301,8 @@
- ifneq ($(shell uname -m), alpha)
- ifneq ($(shell uname -m), sparc64)
- ifneq ($(shell uname -m), armv4l)
-+ifneq (${PROC}, mipsel)
-+ifneq (${PROC}, mips)
- GSM_OBJECTS+= $(SRC)/k6opt.o
- endif
- endif
-@@ -308,6 +311,8 @@
- endif
- endif
- endif
-+endif
-+endif
-
- TOAST_OBJECTS = $(SRC)/toast.o \
- $(SRC)/toast_lin.o \
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/codecs/Makefile asterisk-1.2.0-new/codecs/Makefile
---- asterisk-1.2.0-old/codecs/Makefile 2005-11-08 05:13:18.000000000 +0100
-+++ asterisk-1.2.0-new/codecs/Makefile 2005-12-04 19:24:53.000000000 +0100
-@@ -72,6 +72,9 @@
- codec_adpcm.so codec_ulaw.so codec_alaw.so codec_a_mu.so \
- codec_g726.so
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+CODECS += $(EXTRA_CODEC_MODULES)
-+
- all: depend $(CODECS)
-
- clean:
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/pbx/Makefile asterisk-1.2.0-new/pbx/Makefile
---- asterisk-1.2.0-old/pbx/Makefile 2005-11-01 22:53:30.000000000 +0100
-+++ asterisk-1.2.0-new/pbx/Makefile 2005-12-04 19:23:48.000000000 +0100
-@@ -38,6 +38,9 @@
-
- KDE_CONSOLE_OBJS=pbx_kdeconsole_main.o pbx_kdeconsole.o
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+PBX_LIBS += $(EXTRA_PBX_MODULES)
-+
- all: depend $(PBX_LIBS)
-
- clean:
-@@ -59,7 +62,7 @@
- $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
-
- pbx_dundi.so: dundi-parser.o pbx_dundi.o
-- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o -lz ${CYGSOLIB}
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} pbx_dundi.o dundi-parser.o $(EXTRA_LDFLAGS) -lz ${CYGSOLIB}
-
- %.moc : %.h
- $(MOC) $< -o $@
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/res/Makefile asterisk-1.2.0-new/res/Makefile
---- asterisk-1.2.0-old/res/Makefile 2005-11-16 21:49:44.000000000 +0100
-+++ asterisk-1.2.0-new/res/Makefile 2005-12-04 19:18:15.000000000 +0100
-@@ -69,6 +69,9 @@
- CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC
- endif
-
-+CFLAGS += $(EXTRA_CFLAGS)
-+MODS += $(EXTRA_RES_MODULES)
-+
- all: depend $(MODS)
-
- install: all
-@@ -112,6 +112,12 @@
- res_config_odbc.so: res_config_odbc.o
- $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} ${CYG_RES_CONFIG_ODBC_LIB}
-
-+res_config_mysql.so: res_config_mysql.o
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lmysqlclient -lz
-+
-+res_sqlite.so: res_sqlite.o
-+ $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} $(EXTRA_LDFLAGS) -lsqlite
-+
- ifneq ($(wildcard .depend),)
- include .depend
- endif
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/Makefile asterisk-1.2.0-new/Makefile
---- asterisk-1.2.0-old/Makefile 2005-11-16 21:23:53.000000000 +0100
-+++ asterisk-1.2.0-new/Makefile 2005-12-04 23:01:16.000000000 +0100
-@@ -341,16 +339,6 @@
- netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
- cryptostub.o
-
--ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),)
-- OBJS+= poll.o
-- ASTCFLAGS+=-DPOLLCOMPAT
--endif
--
--ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/dlfcn.h),)
-- OBJS+= dlfcn.o
-- ASTCFLAGS+=-DDLFCNCOMPAT
--endif
--
- ifeq ($(OSARCH),Linux)
- LIBS+=-ldl -lpthread -lncurses -lm -lresolv #-lnjamd
- else
-@@ -401,7 +389,9 @@
- HAVEDOT=no
- endif
-
-+ifneq ($(NOCRYPTO),yes)
- LIBS+=-lssl
-+endif
-
- INSTALL=install
-
-@@ -430,12 +420,12 @@
- cd editline && unset CFLAGS LIBS && ./configure ; \
-
- editline/libedit.a: FORCE
-- cd editline && unset CFLAGS LIBS && test -f config.h || ./configure
-+ cd editline && unset CFLAGS LIBS && test -f config.h || CFLAGS="$(OPTIMIZE) $(EXTRA_CFLAGS)" LDFLAGS="$(EXTRA_LDFLAGS)" ./configure
- $(MAKE) -C editline libedit.a
-
- db1-ast/libdb1.a: FORCE
- @if [ -d db1-ast ]; then \
-- $(MAKE) -C db1-ast libdb1.a ; \
-+ $(MAKE) OORG="$(OPTIMIZE)" -C db1-ast libdb1.a ; \
- else \
- echo "You need to do a cvs update -d not just cvs update"; \
- exit 1; \
-@@ -513,7 +503,7 @@
- fi
- rm -f include/asterisk/build.h.tmp
- $(CC) -c -o buildinfo.o $(CFLAGS) buildinfo.c
-- $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(LIBS)
-+ $(CC) $(DEBUG) $(ASTOBJ) $(ASTLINK) $(OBJS) buildinfo.o $(LIBEDIT) db1-ast/libdb1.a stdtime/libtime.a $(EXTRA_LDFLAGS) $(LIBS)
-
- muted: muted.o
- $(CC) $(AUDIO_LIBS) -o muted muted.o
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/apps/app_sql_mysql.c asterisk-1.2.0-new/apps/app_sql_mysql.c
---- asterisk-1.2.0-old/apps/app_sql_mysql.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.2.0-new/apps/app_sql_mysql.c 2005-06-07 18:36:28.000000000 +0200
-@@ -0,0 +1,445 @@
-+/*
-+ * Asterisk -- A telephony toolkit for Linux.
-+ *
-+ * Connect to MySQL
-+ *
-+ * Copyright (C) 2004, Constantine Filin and Christos Ricudis
-+ *
-+ * Christos Ricudis <ricudis@itc.auth.gr>
-+ * Constantine Filin <cf@intermedia.net>
-+ *
-+ * This program is free software, distributed under the terms of
-+ * the GNU General Public License
-+ */
-+
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <string.h>
-+#include <stdlib.h>
-+#include <sys/types.h>
-+#include <stdio.h>
-+#include <unistd.h>
-+
-+#include <mysql.h>
-+
-+#include <asterisk/file.h>
-+#include <asterisk/logger.h>
-+#include <asterisk/channel.h>
-+#include <asterisk/pbx.h>
-+#include <asterisk/module.h>
-+#include <asterisk/linkedlists.h>
-+#include <asterisk/chanvars.h>
-+#include <asterisk/lock.h>
-+
-+#define EXTRA_LOG 0
-+
-+static char *tdesc = "Simple Mysql Interface";
-+
-+static char *app = "MYSQL";
-+
-+static char *synopsis = "Do several mySQLy things";
-+
-+static char *descrip =
-+"MYSQL(): Do several mySQLy things\n"
-+"Syntax:\n"
-+" MYSQL(Connect connid dhhost dbuser dbpass dbname)\n"
-+" Connects to a database. Arguments contain standard MySQL parameters\n"
-+" passed to function mysql_real_connect. Connection identifer returned\n"
-+" in ${var}\n"
-+" MYSQL(Query resultid ${connid} query-string)\n"
-+" Executes standard MySQL query contained in query-string using established\n"
-+" connection identified by ${connection_identifier}. Result of query is\n"
-+" is stored in ${var}.\n"
-+" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n"
-+" Fetches a single row from a result set contained in ${result_identifier}.\n"
-+" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n"
-+" if additional rows exist in result set.\n"
-+" MYSQL(Clear ${resultid})\n"
-+" Frees memory and datastructures associated with result set.\n"
-+" MYSQL(Disconnect ${connid})\n"
-+" Disconnects from named connection to MySQL.\n"
-+" On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n";
-+
-+/*
-+EXAMPLES OF USE :
-+
-+exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit)
-+exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM})
-+exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2)
-+exten => s,5,GotoIf(${fetchid}?6:8)
-+exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.")
-+exten => s,7,Goto(s,4)
-+exten => s,8,MYSQL(Clear ${resultid})
-+exten => s,9,MYSQL(Disconnect ${connid})
-+*/
-+
-+STANDARD_LOCAL_USER;
-+LOCAL_USER_DECL;
-+
-+AST_MUTEX_DEFINE_STATIC(_mysql_mutex);
-+
-+#define AST_MYSQL_ID_DUMMY 0
-+#define AST_MYSQL_ID_CONNID 1
-+#define AST_MYSQL_ID_RESID 2
-+#define AST_MYSQL_ID_FETCHID 3
-+
-+struct ast_MYSQL_id {
-+ int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
-+ int identifier;
-+ void *data;
-+ AST_LIST_ENTRY(ast_MYSQL_id) entries;
-+} *ast_MYSQL_id;
-+
-+AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head;
-+
-+/* helpful procs */
-+static void *find_identifier(int identifier,int identifier_type) {
-+ struct MYSQLidshead *headp;
-+ struct ast_MYSQL_id *i;
-+ void *res=NULL;
-+ int found=0;
-+
-+ headp=&_mysql_ids_head;
-+
-+ if (AST_LIST_LOCK(headp)) {
-+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
-+ } else {
-+ AST_LIST_TRAVERSE(headp,i,entries) {
-+ if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) {
-+ found=1;
-+ res=i->data;
-+ break;
-+ }
-+ }
-+ if (!found) {
-+ ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type);
-+ }
-+ AST_LIST_UNLOCK(headp);
-+ }
-+
-+ return res;
-+}
-+
-+static int add_identifier(int identifier_type,void *data) {
-+ struct ast_MYSQL_id *i,*j;
-+ struct MYSQLidshead *headp;
-+ int maxidentifier=0;
-+
-+ headp=&_mysql_ids_head;
-+ i=NULL;
-+ j=NULL;
-+
-+ if (AST_LIST_LOCK(headp)) {
-+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
-+ return(-1);
-+ } else {
-+ i=malloc(sizeof(struct ast_MYSQL_id));
-+ AST_LIST_TRAVERSE(headp,j,entries) {
-+ if (j->identifier>maxidentifier) {
-+ maxidentifier=j->identifier;
-+ }
-+ }
-+ i->identifier=maxidentifier+1;
-+ i->identifier_type=identifier_type;
-+ i->data=data;
-+ AST_LIST_INSERT_HEAD(headp,i,entries);
-+ AST_LIST_UNLOCK(headp);
-+ }
-+ return i->identifier;
-+}
-+
-+static int del_identifier(int identifier,int identifier_type) {
-+ struct ast_MYSQL_id *i;
-+ struct MYSQLidshead *headp;
-+ int found=0;
-+
-+ headp=&_mysql_ids_head;
-+
-+ if (AST_LIST_LOCK(headp)) {
-+ ast_log(LOG_WARNING,"Unable to lock identifiers list\n");
-+ } else {
-+ AST_LIST_TRAVERSE(headp,i,entries) {
-+ if ((i->identifier==identifier) &&
-+ (i->identifier_type==identifier_type)) {
-+ AST_LIST_REMOVE(headp,i,entries);
-+ free(i);
-+ found=1;
-+ break;
-+ }
-+ }
-+ AST_LIST_UNLOCK(headp);
-+ }
-+
-+ if (found==0) {
-+ ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type);
-+ return(-1);
-+ } else {
-+ return(0);
-+ }
-+}
-+
-+static int set_asterisk_int(struct ast_channel *chan, char *varname, int id) {
-+ if( id>=0 ) {
-+ char s[100] = "";
-+ snprintf(s, sizeof(s)-1, "%d", id);
-+#if EXTRA_LOG
-+ ast_log(LOG_WARNING,"MYSQL: setting var '%s' to value '%s'\n",varname,s);
-+#endif
-+ pbx_builtin_setvar_helper(chan,varname,s);
-+ }
-+ return id;
-+}
-+
-+static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data) {
-+ return set_asterisk_int(chan,varname,add_identifier(identifier_type,data));
-+}
-+
-+static int safe_scan_int( char** data, char* delim, int def ) {
-+ char* end;
-+ int res = def;
-+ char* s = strsep(data,delim);
-+ if( s ) {
-+ res = strtol(s,&end,10);
-+ if (*end) res = def; /* not an integer */
-+ }
-+ return res;
-+}
-+
-+/* MYSQL operations */
-+static int aMYSQL_connect(struct ast_channel *chan, char *data) {
-+
-+ MYSQL *mysql;
-+
-+ char *connid_var;
-+ char *dbhost;
-+ char *dbuser;
-+ char *dbpass;
-+ char *dbname;
-+
-+ strsep(&data," "); // eat the first token, we already know it :P
-+
-+ connid_var=strsep(&data," ");
-+ dbhost=strsep(&data," ");
-+ dbuser=strsep(&data," ");
-+ dbpass=strsep(&data," ");
-+ dbname=strsep(&data,"\n");
-+
-+ if( connid_var && dbhost && dbuser && dbpass && dbname ) {
-+ mysql = mysql_init(NULL);
-+ if (mysql) {
-+ if (mysql_real_connect(mysql,dbhost,dbuser,dbpass,dbname,0,NULL,0)) {
-+ add_identifier_and_set_asterisk_int(chan,connid_var,AST_MYSQL_ID_CONNID,mysql);
-+ return 0;
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed\n",dbhost,dbuser,dbname);
-+ }
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"myslq_init returned NULL\n");
-+ }
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"MYSQL(connect is missing some arguments\n");
-+ }
-+
-+ return -1;
-+}
-+
-+static int aMYSQL_query(struct ast_channel *chan, char *data) {
-+
-+ MYSQL *mysql;
-+ MYSQL_RES *mysqlres;
-+
-+ char *resultid_var;
-+ int connid;
-+ char *querystring;
-+
-+ strsep(&data," "); // eat the first token, we already know it :P
-+
-+ resultid_var = strsep(&data," ");
-+ connid = safe_scan_int(&data," ",-1);
-+ querystring = strsep(&data,"\n");
-+
-+ if (resultid_var && (connid>=0) && querystring) {
-+ if ((mysql=find_identifier(connid,AST_MYSQL_ID_CONNID))!=NULL) {
-+ mysql_query(mysql,querystring);
-+ if ((mysqlres=mysql_use_result(mysql))!=NULL) {
-+ add_identifier_and_set_asterisk_int(chan,resultid_var,AST_MYSQL_ID_RESID,mysqlres);
-+ return 0;
-+ }
-+ else if( mysql_field_count(mysql)==0 ) {
-+ return 0; // See http://dev.mysql.com/doc/mysql/en/mysql_field_count.html
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"aMYSQL_query: mysql_store_result() failed on query %s\n",querystring);
-+ }
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"aMYSQL_query: Invalid connection identifier %d passed in aMYSQL_query\n",connid);
-+ }
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"aMYSQL_query: missing some arguments\n");
-+ }
-+
-+ return -1;
-+}
-+
-+
-+static int aMYSQL_fetch(struct ast_channel *chan, char *data) {
-+
-+ MYSQL_RES *mysqlres;
-+ MYSQL_ROW mysqlrow;
-+
-+ char *fetchid_var,*s5,*s6;
-+ int resultid,numFields,j;
-+
-+ strsep(&data," "); // eat the first token, we already know it :P
-+
-+ fetchid_var = strsep(&data," ");
-+ resultid = safe_scan_int(&data," ",-1);
-+
-+ if (fetchid_var && (resultid>=0) ) {
-+ if ((mysqlres=find_identifier(resultid,AST_MYSQL_ID_RESID))!=NULL) {
-+ /* Grab the next row */
-+ if ((mysqlrow=mysql_fetch_row(mysqlres))!=NULL) {
-+ numFields=mysql_num_fields(mysqlres);
-+ for (j=0;j<numFields;j++) {
-+ s5=strsep(&data," ");
-+ if (s5==NULL) {
-+ ast_log(LOG_WARNING,"ast_MYSQL_fetch: More fields (%d) than variables (%d)\n",numFields,j);
-+ break;
-+ }
-+ s6=mysqlrow[j];
-+ pbx_builtin_setvar_helper(chan,s5, s6 ? s6 : "NULL");
-+ }
-+#ifdef EXTRA_LOG
-+ ast_log(LOG_WARNING,"ast_MYSQL_fetch: numFields=%d\n",numFields);
-+#endif
-+ set_asterisk_int(chan,fetchid_var,1); // try more rows
-+ } else {
-+#if EXTRA_LOG
-+ ast_log(LOG_WARNING,"ast_MYSQL_fetch : EOF\n");
-+#endif
-+ set_asterisk_int(chan,fetchid_var,0); // no more rows
-+ }
-+ return 0;
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"aMYSQL_fetch: Invalid result identifier %d passed\n",resultid);
-+ }
-+ }
-+ else {
-+ ast_log(LOG_WARNING,"aMYSQL_fetch: missing some arguments\n");
-+ }
-+
-+ return -1;
-+}
-+
-+static int aMYSQL_clear(struct ast_channel *chan, char *data) {
-+
-+ MYSQL_RES *mysqlres;
-+
-+ int id;
-+ strsep(&data," "); // eat the first token, we already know it :P
-+ id = safe_scan_int(&data," \n",-1);
-+ if ((mysqlres=find_identifier(id,AST_MYSQL_ID_RESID))==NULL) {
-+ ast_log(LOG_WARNING,"Invalid result identifier %d passed in aMYSQL_clear\n",id);
-+ } else {
-+ mysql_free_result(mysqlres);
-+ del_identifier(id,AST_MYSQL_ID_RESID);
-+ }
-+
-+ return 0;
-+}
-+
-+static int aMYSQL_disconnect(struct ast_channel *chan, char *data) {
-+
-+ MYSQL *mysql;
-+ int id;
-+ strsep(&data," "); // eat the first token, we already know it :P
-+
-+ id = safe_scan_int(&data," \n",-1);
-+ if ((mysql=find_identifier(id,AST_MYSQL_ID_CONNID))==NULL) {
-+ ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aMYSQL_disconnect\n",id);
-+ } else {
-+ mysql_close(mysql);
-+ del_identifier(id,AST_MYSQL_ID_CONNID);
-+ }
-+
-+ return 0;
-+}
-+
-+static int MYSQL_exec(struct ast_channel *chan, void *data)
-+{
-+ struct localuser *u;
-+ int result;
-+ char sresult[10];
-+
-+#if EXTRA_LOG
-+ fprintf(stderr,"MYSQL_exec: data=%s\n",(char*)data);
-+#endif
-+
-+ if (!data) {
-+ ast_log(LOG_WARNING, "APP_MYSQL requires an argument (see manual)\n");
-+ return -1;
-+ }
-+
-+ LOCAL_USER_ADD(u);
-+ result=0;
-+
-+ ast_mutex_lock(&_mysql_mutex);
-+
-+ if (strncasecmp("connect",data,strlen("connect"))==0) {
-+ result=aMYSQL_connect(chan,ast_strdupa(data));
-+ } else if (strncasecmp("query",data,strlen("query"))==0) {
-+ result=aMYSQL_query(chan,ast_strdupa(data));
-+ } else if (strncasecmp("fetch",data,strlen("fetch"))==0) {
-+ result=aMYSQL_fetch(chan,ast_strdupa(data));
-+ } else if (strncasecmp("clear",data,strlen("clear"))==0) {
-+ result=aMYSQL_clear(chan,ast_strdupa(data));
-+ } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) {
-+ result=aMYSQL_disconnect(chan,ast_strdupa(data));
-+ } else {
-+ ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n",(char *)data);
-+ result=-1;
-+ }
-+
-+ ast_mutex_unlock(&_mysql_mutex);
-+
-+ LOCAL_USER_REMOVE(u);
-+ snprintf(sresult, sizeof(sresult), "%d", result);
-+ pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult);
-+ return 0;
-+}
-+
-+int unload_module(void)
-+{
-+ STANDARD_HANGUP_LOCALUSERS;
-+ return ast_unregister_application(app);
-+}
-+
-+int load_module(void)
-+{
-+ struct MYSQLidshead *headp = &_mysql_ids_head;
-+ AST_LIST_HEAD_INIT(headp);
-+ return ast_register_application(app, MYSQL_exec, synopsis, descrip);
-+}
-+
-+char *description(void)
-+{
-+ return tdesc;
-+}
-+
-+int usecount(void)
-+{
-+ int res;
-+ STANDARD_USECOUNT(res);
-+ return res;
-+}
-+
-+char *key()
-+{
-+ return ASTERISK_GPL_KEY;
-+}
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/configs/cdr_mysql.conf.sample asterisk-1.2.0-new/configs/cdr_mysql.conf.sample
---- asterisk-1.2.0-old/configs/cdr_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.2.0-new/configs/cdr_mysql.conf.sample 2005-01-21 02:43:20.000000000 +0100
-@@ -0,0 +1,21 @@
-+;
-+; Note - if the database server is hosted on the same machine as the
-+; asterisk server, you can achieve a local Unix socket connection by
-+; setting hostname=localhost
-+;
-+; port and sock are both optional parameters. If hostname is specified
-+; and is not "localhost", then cdr_mysql will attempt to connect to the
-+; port specified or use the default port. If hostname is not specified
-+; or if hostname is "localhost", then cdr_mysql will attempt to connect
-+; to the socket file specified by sock or otherwise use the default socket
-+; file.
-+;
-+;[global]
-+;hostname=database.host.name
-+;dbname=asteriskcdrdb
-+;table=cdr
-+;password=password
-+;user=asteriskcdruser
-+;port=3306
-+;sock=/tmp/mysql.sock
-+;userfield=1
-diff -ruN asterisk-1.2.0-old/cdr/cdr_mysql.c asterisk-1.2.0-new/cdr/cdr_mysql.c
---- asterisk-1.2.0-old/cdr/cdr_mysql.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.2.0-new/cdr/cdr_mysql.c 2005-12-04 20:10:59.000000000 +0100
-@@ -0,0 +1,493 @@
-+/*
-+ * Asterisk -- A telephony toolkit for Linux.
-+ *
-+ * MySQL CDR logger
-+ *
-+ * James Sharp <jsharp@psychoses.org>
-+ *
-+ * Modified August 2003
-+ * Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com>
-+ *
-+ * Modified August 6, 2005
-+ * Joseph Benden <joe@thrallingpenguin.com>
-+ * Added mysql connection timeout parameter
-+ * Added an automatic reconnect as to not lose a cdr record
-+ * Cleaned up the original code to match the coding guidelines
-+ *
-+ * This program is free software, distributed under the terms of
-+ * the GNU General Public License.
-+ *
-+ */
-+
-+#include <sys/types.h>
-+
-+#include <stdio.h>
-+#include <string.h>
-+
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <time.h>
-+
-+#include <mysql.h>
-+#include <errmsg.h>
-+
-+#include <sys/stat.h>
-+#include <sys/types.h>
-+#include <errno.h>
-+
-+#include <asterisk/config.h>
-+#include <asterisk/options.h>
-+#include <asterisk/channel.h>
-+#include <asterisk/cdr.h>
-+#include <asterisk/module.h>
-+#include <asterisk/logger.h>
-+#include <asterisk/cli.h>
-+
-+#define DATE_FORMAT "%Y-%m-%d %T"
-+
-+static char *desc = "MySQL CDR Backend";
-+static char *name = "mysql";
-+static char *config = "cdr_mysql.conf";
-+static char *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL;
-+static int hostname_alloc = 0, dbname_alloc = 0, dbuser_alloc = 0, password_alloc = 0, dbsock_alloc = 0, dbtable_alloc = 0;
-+static int dbport = 0;
-+static int connected = 0;
-+static time_t connect_time = 0;
-+static int records = 0;
-+static int totalrecords = 0;
-+static int userfield = 0;
-+static unsigned int timeout = 0;
-+
-+AST_MUTEX_DEFINE_STATIC(mysql_lock);
-+
-+static MYSQL mysql;
-+
-+static char cdr_mysql_status_help[] =
-+"Usage: cdr mysql status\n"
-+" Shows current connection status for cdr_mysql\n";
-+
-+static int handle_cdr_mysql_status(int fd, int argc, char *argv[])
-+{
-+ if (connected) {
-+ char status[256], status2[100] = "";
-+ int ctime = time(NULL) - connect_time;
-+ if (dbport)
-+ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, hostname, dbport);
-+ else if (dbsock)
-+ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
-+ else
-+ snprintf(status, 255, "Connected to %s@%s", dbname, hostname);
-+
-+ if (dbuser && *dbuser)
-+ snprintf(status2, 99, " with username %s", dbuser);
-+ if (dbtable && *dbtable)
-+ snprintf(status2, 99, " using table %s", dbtable);
-+ if (ctime > 31536000) {
-+ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 86400) {
-+ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 3600) {
-+ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 60) {
-+ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
-+ } else {
-+ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
-+ }
-+ if (records == totalrecords)
-+ ast_cli(fd, " Wrote %d records since last restart.\n", totalrecords);
-+ else
-+ ast_cli(fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
-+ return RESULT_SUCCESS;
-+ } else {
-+ ast_cli(fd, "Not currently connected to a MySQL server.\n");
-+ return RESULT_FAILURE;
-+ }
-+}
-+
-+static struct ast_cli_entry cdr_mysql_status_cli =
-+ { { "cdr", "mysql", "status", NULL },
-+ handle_cdr_mysql_status, "Show connection status of cdr_mysql",
-+ cdr_mysql_status_help, NULL };
-+
-+static int mysql_log(struct ast_cdr *cdr)
-+{
-+ struct tm tm;
-+ struct timeval tv;
-+ struct localuser *u;
-+ char *userfielddata = NULL;
-+ char sqlcmd[2048], timestr[128];
-+ char *clid=NULL, *dcontext=NULL, *channel=NULL, *dstchannel=NULL, *lastapp=NULL, *lastdata=NULL;
-+ int retries = 5;
-+#ifdef MYSQL_LOGUNIQUEID
-+ char *uniqueid = NULL;
-+#endif
-+
-+ ast_mutex_lock(&mysql_lock);
-+
-+ memset(sqlcmd, 0, 2048);
-+
-+ localtime_r(&cdr->start.tv_sec, &tm);
-+ strftime(timestr, 128, DATE_FORMAT, &tm);
-+
-+db_reconnect:
-+ if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
-+ /* Attempt to connect */
-+ mysql_init(&mysql);
-+ /* Add option to quickly timeout the connection */
-+ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
-+ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
-+ }
-+ if (mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
-+ connected = 1;
-+ connect_time = time(NULL);
-+ records = 0;
-+ } else {
-+ ast_log(LOG_ERROR, "cdr_mysql: cannot connect to database server %s.\n", hostname);
-+ connected = 0;
-+ }
-+ } else {
-+ /* Long connection - ping the server */
-+ int error;
-+ if ((error = mysql_ping(&mysql))) {
-+ connected = 0;
-+ records = 0;
-+ switch (error) {
-+ case CR_SERVER_GONE_ERROR:
-+ case CR_SERVER_LOST:
-+ ast_log(LOG_ERROR, "cdr_mysql: Server has gone away. Attempting to reconnect.\n");
-+ break;
-+ default:
-+ ast_log(LOG_ERROR, "cdr_mysql: Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
-+ }
-+ retries--;
-+ if (retries)
-+ goto db_reconnect;
-+ else
-+ ast_log(LOG_ERROR, "cdr_mysql: Retried to connect fives times, giving up.\n");
-+ }
-+ }
-+
-+ /* Maximum space needed would be if all characters needed to be escaped, plus a trailing NULL */
-+ /* WARNING: This code previously used mysql_real_escape_string, but the use of said function
-+ requires an active connection to a database. If we are not connected, then this function
-+ cannot be used. This is a problem since we need to store off the SQL statement into our
-+ spool file for later restoration.
-+ So the question is, what's the best way to handle this? This works for now.
-+ */
-+ if ((clid = alloca(strlen(cdr->clid) * 2 + 1)) != NULL)
-+ mysql_escape_string(clid, cdr->clid, strlen(cdr->clid));
-+ if ((dcontext = alloca(strlen(cdr->dcontext) * 2 + 1)) != NULL)
-+ mysql_escape_string(dcontext, cdr->dcontext, strlen(cdr->dcontext));
-+ if ((channel = alloca(strlen(cdr->channel) * 2 + 1)) != NULL)
-+ mysql_escape_string(channel, cdr->channel, strlen(cdr->channel));
-+ if ((dstchannel = alloca(strlen(cdr->dstchannel) * 2 + 1)) != NULL)
-+ mysql_escape_string(dstchannel, cdr->dstchannel, strlen(cdr->dstchannel));
-+ if ((lastapp = alloca(strlen(cdr->lastapp) * 2 + 1)) != NULL)
-+ mysql_escape_string(lastapp, cdr->lastapp, strlen(cdr->lastapp));
-+ if ((lastdata = alloca(strlen(cdr->lastdata) * 2 + 1)) != NULL)
-+ mysql_escape_string(lastdata, cdr->lastdata, strlen(cdr->lastdata));
-+#ifdef MYSQL_LOGUNIQUEID
-+ if ((uniqueid = alloca(strlen(cdr->uniqueid) * 2 + 1)) != NULL)
-+ mysql_escape_string(uniqueid, cdr->uniqueid, strlen(cdr->uniqueid));
-+#endif
-+ if (userfield && ((userfielddata = alloca(strlen(cdr->userfield) * 2 + 1)) != NULL))
-+ mysql_escape_string(userfielddata, cdr->userfield, strlen(cdr->userfield));
-+
-+ /* Check for all alloca failures above at once */
-+#ifdef MYSQL_LOGUNIQUEID
-+ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata) || (!uniqueid)) {
-+#else
-+ if ((!clid) || (!dcontext) || (!channel) || (!dstchannel) || (!lastapp) || (!lastdata)) {
-+#endif
-+ ast_log(LOG_ERROR, "cdr_mysql: Out of memory error (insert fails)\n");
-+ ast_mutex_unlock(&mysql_lock);
-+ return -1;
-+ }
-+
-+ ast_log(LOG_DEBUG, "cdr_mysql: inserting a CDR record.\n");
-+
-+ if (userfield && userfielddata) {
-+#ifdef MYSQL_LOGUNIQUEID
-+ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid, userfielddata);
-+#else
-+ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,userfield) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, userfielddata);
-+#endif
-+ } else {
-+#ifdef MYSQL_LOGUNIQUEID
-+ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s','%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext,channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode, uniqueid);
-+#else
-+ sprintf(sqlcmd, "INSERT INTO %s (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp,lastdata,duration,billsec,disposition,amaflags,accountcode) VALUES ('%s','%s','%s','%s','%s', '%s','%s','%s','%s',%i,%i,'%s',%i,'%s')", dbtable, timestr, clid, cdr->src, cdr->dst, dcontext, channel, dstchannel, lastapp, lastdata, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), cdr->amaflags, cdr->accountcode);
-+#endif
-+ }
-+
-+ ast_log(LOG_DEBUG, "cdr_mysql: SQL command as follows: %s\n", sqlcmd);
-+
-+ if (connected) {
-+ if (mysql_real_query(&mysql, sqlcmd, strlen(sqlcmd))) {
-+ ast_log(LOG_ERROR, "mysql_cdr: Failed to insert into database: (%d) %s", mysql_errno(&mysql), mysql_error(&mysql));
-+ connected = 0;
-+ } else {
-+ records++;
-+ totalrecords++;
-+ }
-+ }
-+ ast_mutex_unlock(&mysql_lock);
-+ return 0;
-+}
-+
-+char *description(void)
-+{
-+ return desc;
-+}
-+
-+static int my_unload_module(void)
-+{
-+ ast_cli_unregister(&cdr_mysql_status_cli);
-+ if (connected) {
-+ mysql_close(&mysql);
-+ connected = 0;
-+ records = 0;
-+ }
-+ if (hostname && hostname_alloc) {
-+ free(hostname);
-+ hostname = NULL;
-+ hostname_alloc = 0;
-+ }
-+ if (dbname && dbname_alloc) {
-+ free(dbname);
-+ dbname = NULL;
-+ dbname_alloc = 0;
-+ }
-+ if (dbuser && dbuser_alloc) {
-+ free(dbuser);
-+ dbuser = NULL;
-+ dbuser_alloc = 0;
-+ }
-+ if (dbsock && dbsock_alloc) {
-+ free(dbsock);
-+ dbsock = NULL;
-+ dbsock_alloc = 0;
-+ }
-+ if (dbtable && dbtable_alloc) {
-+ free(dbtable);
-+ dbtable = NULL;
-+ dbtable_alloc = 0;
-+ }
-+ if (password && password_alloc) {
-+ free(password);
-+ password = NULL;
-+ password_alloc = 0;
-+ }
-+ dbport = 0;
-+ ast_cdr_unregister(name);
-+ return 0;
-+}
-+
-+static int my_load_module(void)
-+{
-+ int res;
-+ struct ast_config *cfg;
-+ struct ast_variable *var;
-+ char *tmp;
-+
-+ cfg = ast_config_load(config);
-+ if (!cfg) {
-+ ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
-+ return 0;
-+ }
-+
-+ var = ast_variable_browse(cfg, "global");
-+ if (!var) {
-+ /* nothing configured */
-+ return 0;
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "hostname");
-+ if (tmp) {
-+ hostname = malloc(strlen(tmp) + 1);
-+ if (hostname != NULL) {
-+ hostname_alloc = 1;
-+ strcpy(hostname, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL server hostname not specified. Assuming localhost\n");
-+ hostname = "localhost";
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "dbname");
-+ if (tmp) {
-+ dbname = malloc(strlen(tmp) + 1);
-+ if (dbname != NULL) {
-+ dbname_alloc = 1;
-+ strcpy(dbname, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL database not specified. Assuming asteriskcdrdb\n");
-+ dbname = "asteriskcdrdb";
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "user");
-+ if (tmp) {
-+ dbuser = malloc(strlen(tmp) + 1);
-+ if (dbuser != NULL) {
-+ dbuser_alloc = 1;
-+ strcpy(dbuser, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL database user not specified. Assuming root\n");
-+ dbuser = "root";
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "sock");
-+ if (tmp) {
-+ dbsock = malloc(strlen(tmp) + 1);
-+ if (dbsock != NULL) {
-+ dbsock_alloc = 1;
-+ strcpy(dbsock, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL database sock file not specified. Using default\n");
-+ dbsock = NULL;
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "table");
-+ if (tmp) {
-+ dbtable = malloc(strlen(tmp) + 1);
-+ if (dbtable != NULL) {
-+ dbtable_alloc = 1;
-+ strcpy(dbtable, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_NOTICE, "MySQL database table not specified. Assuming \"cdr\"\n");
-+ dbtable = "cdr";
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "password");
-+ if (tmp) {
-+ password = malloc(strlen(tmp) + 1);
-+ if (password != NULL) {
-+ password_alloc = 1;
-+ strcpy(password, tmp);
-+ } else {
-+ ast_log(LOG_ERROR, "Out of memory error.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL database password not specified. Assuming blank\n");
-+ password = "";
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "port");
-+ if (tmp) {
-+ if (sscanf(tmp, "%d", &dbport) < 1) {
-+ ast_log(LOG_WARNING, "Invalid MySQL port number. Using default\n");
-+ dbport = 0;
-+ }
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "timeout");
-+ if (tmp) {
-+ if (sscanf(tmp,"%d", &timeout) < 1) {
-+ ast_log(LOG_WARNING, "Invalid MySQL timeout number. Using default\n");
-+ timeout = 0;
-+ }
-+ }
-+
-+ tmp = ast_variable_retrieve(cfg, "global", "userfield");
-+ if (tmp) {
-+ if (sscanf(tmp, "%d", &userfield) < 1) {
-+ ast_log(LOG_WARNING, "Invalid MySQL configurtation file\n");
-+ userfield = 0;
-+ }
-+ }
-+
-+ ast_config_destroy(cfg);
-+
-+ ast_log(LOG_DEBUG, "cdr_mysql: got hostname of %s\n", hostname);
-+ ast_log(LOG_DEBUG, "cdr_mysql: got port of %d\n", dbport);
-+ ast_log(LOG_DEBUG, "cdr_mysql: got a timeout of %d\n", timeout);
-+ if (dbsock)
-+ ast_log(LOG_DEBUG, "cdr_mysql: got sock file of %s\n", dbsock);
-+ ast_log(LOG_DEBUG, "cdr_mysql: got user of %s\n", dbuser);
-+ ast_log(LOG_DEBUG, "cdr_mysql: got dbname of %s\n", dbname);
-+ ast_log(LOG_DEBUG, "cdr_mysql: got password of %s\n", password);
-+
-+ mysql_init(&mysql);
-+
-+ if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout)!=0) {
-+ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
-+ }
-+
-+ if (!mysql_real_connect(&mysql, hostname, dbuser, password, dbname, dbport, dbsock, 0)) {
-+ ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", dbname, hostname);
-+ connected = 0;
-+ records = 0;
-+ } else {
-+ ast_log(LOG_DEBUG, "Successfully connected to MySQL database.\n");
-+ connected = 1;
-+ records = 0;
-+ connect_time = time(NULL);
-+ }
-+
-+ res = ast_cdr_register(name, desc, mysql_log);
-+ if (res) {
-+ ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
-+ } else {
-+ res = ast_cli_register(&cdr_mysql_status_cli);
-+ }
-+
-+ return res;
-+}
-+
-+int load_module(void)
-+{
-+ return my_load_module();
-+}
-+
-+int unload_module(void)
-+{
-+ return my_unload_module();
-+}
-+
-+int reload(void)
-+{
-+ int ret;
-+
-+ ast_mutex_lock(&mysql_lock);
-+ my_unload_module();
-+ ret = my_load_module();
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ return ret;
-+}
-+
-+int usecount(void)
-+{
-+ /* Simplistic use count */
-+ if (ast_mutex_trylock(&mysql_lock)) {
-+ return 1;
-+ } else {
-+ ast_mutex_unlock(&mysql_lock);
-+ return 0;
-+ }
-+}
-+
-+char *key()
-+{
-+ return ASTERISK_GPL_KEY;
-+}
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/channels/chan_bluetooth.c asterisk-1.2.0-new/channels/chan_bluetooth.c
---- asterisk-1.2.0-old/channels/chan_bluetooth.c 2005-12-04 04:48:40.000000000 +0100
-+++ asterisk-1.2.0-new/channels/chan_bluetooth.c 2005-12-04 23:07:19.000000000 +0100
-@@ -104,6 +104,7 @@
- #include <asterisk/options.h>
- #include <asterisk/cli.h>
- #include <asterisk/callerid.h>
-+#include <asterisk/version.h>
- #include <sys/socket.h>
- #include <sys/signal.h>
- #include <sys/time.h>
-@@ -3133,7 +3133,7 @@
- struct ast_variable * v;
- char * cat;
-
-- cfg = ast_load(BLT_CONFIG_FILE);
-+ cfg = ast_config_load(BLT_CONFIG_FILE);
-
- if (!cfg) {
- ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/include/asterisk/compat.h asterisk-1.2.0-new/include/asterisk/compat.h
---- asterisk-1.2.0-old/include/asterisk/compat.h 2005-11-08 05:13:19.000000000 +0100
-+++ asterisk-1.2.0-new/include/asterisk/compat.h 2005-12-04 05:32:31.000000000 +0100
-@@ -75,7 +75,9 @@
- #define HAVE_STRTOQ
-
- #ifdef _BSD_SOURCE
-+#ifndef __UCLIBC__
- #define HAVE_GETLOADAVG
-+#endif /* __UCLIBC__ */
- #endif
-
- #ifdef __linux__
+++ /dev/null
-diff -ruN asterisk-1.2.0-old/configs/res_mysql.conf.sample asterisk-1.2.0-new/configs/res_mysql.conf.sample
---- asterisk-1.2.0-old/configs/res_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.2.0-new/configs/res_mysql.conf.sample 2004-12-03 15:33:44.000000000 +0100
-@@ -0,0 +1,15 @@
-+;
-+; Sample configuration for res_config_mysql.c
-+;
-+; The value of dbhost may be either a hostname or an IP address.
-+; If dbhost is commented out or the string "localhost", a connection
-+; to the local host is assumed and dbsock is used instead of TCP/IP
-+; to connect to the server.
-+;
-+[general]
-+;dbhost = 127.0.0.1
-+;dbname = asterisk
-+;dbuser = myuser
-+;dbpass = mypass
-+;dbport = 3306
-+;dbsock = /tmp/mysql.sock
-diff -ruN asterisk-1.2.0-old/res/res_config_mysql.c asterisk-1.2.0-new/res/res_config_mysql.c
---- asterisk-1.2.0-old/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-1.2.0-new/res/res_config_mysql.c 2005-10-13 21:43:54.000000000 +0200
-@@ -0,0 +1,675 @@
-+/*
-+ * Asterisk -- A telephony toolkit for Linux.
-+ *
-+ * Copyright (C) 1999-2005, Digium, Inc.
-+ *
-+ * Mark Spencer <markster@digium.com> - Asterisk Author
-+ * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
-+ *
-+ * res_config_mysql.c <mysql plugin for RealTime configuration engine>
-+ *
-+ * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
-+ *
-+ * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
-+ * in extconfig.conf (bug #4973)
-+ *
-+ * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
-+ * what really happened.
-+ *
-+ * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
-+ * in realtime_multi_mysql function which caused segfault.
-+ *
-+ * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
-+ *
-+ * v1.5.1 - (01-26-05) - Added better(?) locking stuff
-+ *
-+ * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
-+ * - Added in extra locking provided by georg (bug #3248)
-+ *
-+ * v1.4 - (12-02-04) - Added realtime_multi_mysql function
-+ * This function will return an ast_config with categories,
-+ * unlike standard realtime_mysql which only returns
-+ * a linked list of ast_variables
-+ *
-+ * v1.3 - (12-01-04) - Added support other operators
-+ * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
-+ *
-+ * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
-+ * Code beautification (doc/CODING-GUIDELINES)
-+ */
-+
-+#include <asterisk/channel.h>
-+#include <asterisk/logger.h>
-+#include <asterisk/config.h>
-+#include <asterisk/module.h>
-+#include <asterisk/lock.h>
-+#include <asterisk/options.h>
-+#include <asterisk/cli.h>
-+#include <asterisk/utils.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <mysql.h>
-+#include <mysql_version.h>
-+#include <errmsg.h>
-+
-+static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
-+
-+AST_MUTEX_DEFINE_STATIC(mysql_lock);
-+#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
-+MYSQL mysql;
-+static char dbhost[50];
-+static char dbuser[50];
-+static char dbpass[50];
-+static char dbname[50];
-+static char dbsock[50];
-+static int dbport;
-+static int connected;
-+static time_t connect_time;
-+
-+static int parse_config(void);
-+static int mysql_reconnect(const char *database);
-+static int realtime_mysql_status(int fd, int argc, char **argv);
-+
-+STANDARD_LOCAL_USER;
-+
-+LOCAL_USER_DECL;
-+
-+static char cli_realtime_mysql_status_usage[] =
-+"Usage: realtime mysql status\n"
-+" Shows connection information for the MySQL RealTime driver\n";
-+
-+static struct ast_cli_entry cli_realtime_mysql_status = {
-+ { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
-+ "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
-+
-+static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
-+{
-+ MYSQL_RES *result;
-+ MYSQL_ROW row;
-+ MYSQL_FIELD *fields;
-+ int numFields, i;
-+ char sql[256];
-+ char *stringp;
-+ char *chunk;
-+ char *op;
-+ const char *newparam, *newval;
-+ struct ast_variable *var=NULL, *prev=NULL;
-+
-+ if(!table) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
-+ return NULL;
-+ }
-+
-+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
-+ newparam = va_arg(ap, const char *);
-+ newval = va_arg(ap, const char *);
-+ if(!newparam || !newval) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
-+ mysql_close(&mysql);
-+ return NULL;
-+ }
-+
-+ /* Create the first part of the query using the first parameter/value pairs we just extracted
-+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
-+
-+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
-+
-+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
-+ while((newparam = va_arg(ap, const char *))) {
-+ newval = va_arg(ap, const char *);
-+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
-+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
-+ }
-+ va_end(ap);
-+
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
-+
-+ /* We now have our complete statement; Lets connect to the server and execute it. */
-+ ast_mutex_lock(&mysql_lock);
-+ if(!mysql_reconnect(database)) {
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if(mysql_real_query(&mysql, sql, strlen(sql))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if((result = mysql_store_result(&mysql))) {
-+ numFields = mysql_num_fields(result);
-+ fields = mysql_fetch_fields(result);
-+
-+ while((row = mysql_fetch_row(result))) {
-+ for(i = 0; i < numFields; i++) {
-+ stringp = row[i];
-+ while(stringp) {
-+ chunk = strsep(&stringp, ";");
-+ if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
-+ if(prev) {
-+ prev->next = ast_variable_new(fields[i].name, chunk);
-+ if (prev->next) {
-+ prev = prev->next;
-+ }
-+ } else {
-+ prev = var = ast_variable_new(fields[i].name, chunk);
-+ }
-+ }
-+ }
-+ }
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
-+ }
-+
-+ ast_mutex_unlock(&mysql_lock);
-+ mysql_free_result(result);
-+
-+ return var;
-+}
-+
-+static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
-+{
-+ MYSQL_RES *result;
-+ MYSQL_ROW row;
-+ MYSQL_FIELD *fields;
-+ int numFields, i;
-+ char sql[256];
-+ const char *initfield = NULL;
-+ char *stringp;
-+ char *chunk;
-+ char *op;
-+ const char *newparam, *newval;
-+ struct ast_realloca ra;
-+ struct ast_variable *var=NULL;
-+ struct ast_config *cfg = NULL;
-+ struct ast_category *cat = NULL;
-+
-+ if(!table) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
-+ return NULL;
-+ }
-+
-+ memset(&ra, 0, sizeof(ra));
-+
-+ cfg = ast_config_new();
-+ if (!cfg) {
-+ /* If I can't alloc memory at this point, why bother doing anything else? */
-+ ast_log(LOG_WARNING, "Out of memory!\n");
-+ return NULL;
-+ }
-+
-+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
-+ newparam = va_arg(ap, const char *);
-+ newval = va_arg(ap, const char *);
-+ if(!newparam || !newval) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
-+ mysql_close(&mysql);
-+ return NULL;
-+ }
-+
-+ initfield = ast_strdupa(newparam);
-+ if(initfield && (op = strchr(initfield, ' '))) {
-+ *op = '\0';
-+ }
-+
-+ /* Create the first part of the query using the first parameter/value pairs we just extracted
-+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
-+
-+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
-+
-+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
-+ while((newparam = va_arg(ap, const char *))) {
-+ newval = va_arg(ap, const char *);
-+ if(!strchr(newparam, ' ')) op = " ="; else op = "";
-+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
-+ }
-+
-+ if(initfield) {
-+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
-+ }
-+
-+ va_end(ap);
-+
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
-+
-+ /* We now have our complete statement; Lets connect to the server and execute it. */
-+ ast_mutex_lock(&mysql_lock);
-+ if(!mysql_reconnect(database)) {
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if(mysql_real_query(&mysql, sql, strlen(sql))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if((result = mysql_store_result(&mysql))) {
-+ numFields = mysql_num_fields(result);
-+ fields = mysql_fetch_fields(result);
-+
-+ while((row = mysql_fetch_row(result))) {
-+ var = NULL;
-+ cat = ast_category_new("");
-+ if(!cat) {
-+ ast_log(LOG_WARNING, "Out of memory!\n");
-+ continue;
-+ }
-+ for(i = 0; i < numFields; i++) {
-+ stringp = row[i];
-+ while(stringp) {
-+ chunk = strsep(&stringp, ";");
-+ if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
-+ if(initfield && !strcmp(initfield, fields[i].name)) {
-+ ast_category_rename(cat, chunk);
-+ }
-+ var = ast_variable_new(fields[i].name, chunk);
-+ ast_variable_append(cat, var);
-+ }
-+ }
-+ }
-+ ast_category_append(cfg, cat);
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
-+ }
-+
-+ ast_mutex_unlock(&mysql_lock);
-+ mysql_free_result(result);
-+
-+ return cfg;
-+}
-+
-+static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
-+{
-+ my_ulonglong numrows;
-+ char sql[256];
-+ const char *newparam, *newval;
-+
-+ if(!table) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
-+ return -1;
-+ }
-+
-+ /* Get the first parameter and first value in our list of passed paramater/value pairs */
-+ newparam = va_arg(ap, const char *);
-+ newval = va_arg(ap, const char *);
-+ if(!newparam || !newval) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
-+ mysql_close(&mysql);
-+ return -1;
-+ }
-+
-+ /* Create the first part of the query using the first parameter/value pairs we just extracted
-+ If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
-+
-+ snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
-+ while((newparam = va_arg(ap, const char *))) {
-+ newval = va_arg(ap, const char *);
-+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
-+ }
-+ va_end(ap);
-+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
-+
-+ ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
-+
-+ /* We now have our complete statement; Lets connect to the server and execute it. */
-+ ast_mutex_lock(&mysql_lock);
-+ if(!mysql_reconnect(database)) {
-+ ast_mutex_unlock(&mysql_lock);
-+ return -1;
-+ }
-+
-+ if(mysql_real_query(&mysql, sql, strlen(sql))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
-+ ast_mutex_unlock(&mysql_lock);
-+ return -1;
-+ }
-+
-+ numrows = mysql_affected_rows(&mysql);
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
-+
-+ /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
-+ * An integer greater than zero indicates the number of rows affected
-+ * Zero indicates that no records were updated
-+ * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
-+ */
-+
-+ if(numrows >= 0)
-+ return (int)numrows;
-+
-+ return -1;
-+}
-+
-+static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
-+{
-+ MYSQL_RES *result;
-+ MYSQL_ROW row;
-+ my_ulonglong num_rows;
-+ struct ast_config *new;
-+ struct ast_variable *cur_v, *new_v;
-+ struct ast_category *cur_cat, *new_cat;
-+ char sql[250] = "";
-+ char last[80] = "";
-+ int cat_started = 0;
-+ int var_started = 0;
-+ int last_cat_metric = 0;
-+
-+ last[0] = '\0';
-+
-+ if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
-+ return NULL;
-+ }
-+
-+ snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
-+
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
-+
-+ /* We now have our complete statement; Lets connect to the server and execute it. */
-+ ast_mutex_lock(&mysql_lock);
-+ if(!mysql_reconnect(database)) {
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if(mysql_real_query(&mysql, sql, strlen(sql))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+
-+ if((result = mysql_store_result(&mysql))) {
-+ num_rows = mysql_num_rows(result);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
-+
-+ /* There might exist a better way to access the column names other than counting,
-+ but I believe that would require another loop that we don't need. */
-+
-+ while((row = mysql_fetch_row(result))) {
-+ if(!strcmp(row[1], "#include")) {
-+ if (!ast_config_internal_load(row[2], cfg)) {
-+ mysql_free_result(result);
-+ ast_mutex_unlock(&mysql_lock);
-+ return NULL;
-+ }
-+ continue;
-+ }
-+
-+ if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
-+ cur_cat = ast_category_new(row[0]);
-+ if (!cur_cat) {
-+ ast_log(LOG_WARNING, "Out of memory!\n");
-+ break;
-+ }
-+ strcpy(last, row[0]);
-+ last_cat_metric = atoi(row[3]);
-+ ast_category_append(cfg, cur_cat);
-+ }
-+ new_v = ast_variable_new(row[1], row[2]);
-+ ast_variable_append(cur_cat, new_v);
-+ }
-+ } else {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
-+ }
-+
-+ mysql_free_result(result);
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ return cfg;
-+}
-+
-+static struct ast_config_engine mysql_engine = {
-+ .name = "mysql",
-+ .load_func = config_mysql,
-+ .realtime_func = realtime_mysql,
-+ .realtime_multi_func = realtime_multi_mysql,
-+ .update_func = update_mysql
-+};
-+
-+int load_module (void)
-+{
-+ parse_config();
-+
-+ ast_mutex_lock(&mysql_lock);
-+
-+ if(!mysql_reconnect(NULL)) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
-+ }
-+
-+ ast_config_engine_register(&mysql_engine);
-+ if(option_verbose) {
-+ ast_verbose("MySQL RealTime driver loaded.\n");
-+ }
-+ ast_cli_register(&cli_realtime_mysql_status);
-+
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ return 0;
-+}
-+
-+int unload_module (void)
-+{
-+ /* Aquire control before doing anything to the module itself. */
-+ ast_mutex_lock(&mysql_lock);
-+
-+ mysql_close(&mysql);
-+ ast_cli_unregister(&cli_realtime_mysql_status);
-+ ast_config_engine_deregister(&mysql_engine);
-+ if(option_verbose) {
-+ ast_verbose("MySQL RealTime unloaded.\n");
-+ }
-+
-+ STANDARD_HANGUP_LOCALUSERS;
-+
-+ /* Unlock so something else can destroy the lock. */
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ return 0;
-+}
-+
-+int reload (void)
-+{
-+ /* Aquire control before doing anything to the module itself. */
-+ ast_mutex_lock(&mysql_lock);
-+
-+ mysql_close(&mysql);
-+ connected = 0;
-+ parse_config();
-+
-+ if(!mysql_reconnect(NULL)) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
-+ }
-+
-+ ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
-+
-+ /* Done reloading. Release lock so others can now use driver. */
-+ ast_mutex_unlock(&mysql_lock);
-+
-+ return 0;
-+}
-+
-+int parse_config (void)
-+{
-+ struct ast_config *config;
-+ char *s;
-+
-+ config = ast_config_load(RES_CONFIG_MYSQL_CONF);
-+
-+ if(config) {
-+ if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
-+ strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
-+ } else {
-+ strncpy(dbuser, s, sizeof(dbuser) - 1);
-+ }
-+
-+ if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
-+ strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
-+ } else {
-+ strncpy(dbpass, s, sizeof(dbpass) - 1);
-+ }
-+
-+ if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
-+ dbhost[0] = '\0';
-+ } else {
-+ strncpy(dbhost, s, sizeof(dbhost) - 1);
-+ }
-+
-+ if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
-+ strncpy(dbname, "asterisk", sizeof(dbname) - 1);
-+ } else {
-+ strncpy(dbname, s, sizeof(dbname) - 1);
-+ }
-+
-+ if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
-+ dbport = 3306;
-+ } else {
-+ dbport = atoi(s);
-+ }
-+
-+ if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
-+ strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
-+ } else {
-+ strncpy(dbsock, s, sizeof(dbsock) - 1);
-+ }
-+ }
-+ ast_config_destroy(config);
-+
-+ if(dbhost) {
-+ ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
-+ ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
-+ } else {
-+ ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
-+ }
-+ ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
-+ ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
-+
-+ return 1;
-+}
-+
-+char *description (void)
-+{
-+ return res_config_mysql_desc;
-+}
-+
-+int usecount (void)
-+{
-+ /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */
-+ if(ast_mutex_trylock(&mysql_lock)) {
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n");
-+ return 1;
-+ }
-+ ast_mutex_unlock(&mysql_lock);
-+ return 0;
-+}
-+
-+char *key ()
-+{
-+ return ASTERISK_GPL_KEY;
-+}
-+
-+static int mysql_reconnect(const char *database)
-+{
-+ char my_database[50];
-+
-+ if(!database || ast_strlen_zero(database))
-+ ast_copy_string(my_database, dbname, sizeof(my_database));
-+ else
-+ ast_copy_string(my_database, database, sizeof(my_database));
-+
-+ /* mutex lock should have been locked before calling this function. */
-+
-+ if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
-+ if(!mysql_init(&mysql)) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
-+ connected = 0;
-+ return 0;
-+ }
-+ if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
-+ connected = 1;
-+ connect_time = time(NULL);
-+ return 1;
-+ } else {
-+ ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
-+ connected = 0;
-+ return 0;
-+ }
-+ } else {
-+ if(mysql_ping(&mysql) != 0) {
-+ connected = 0;
-+ ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n");
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql));
-+ return 0;
-+ }
-+
-+ connected = 1;
-+
-+ if(mysql_select_db(&mysql, my_database) != 0) {
-+ ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database);
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql));
-+ return 0;
-+ }
-+
-+ ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
-+ return 1;
-+ }
-+}
-+
-+static int realtime_mysql_status(int fd, int argc, char **argv)
-+{
-+ char status[256], status2[100] = "";
-+ int ctime = time(NULL) - connect_time;
-+
-+ if(mysql_reconnect(NULL)) {
-+ if(dbhost) {
-+ snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
-+ } else if(dbsock) {
-+ snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
-+ } else {
-+ snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
-+ }
-+
-+ if(dbuser && *dbuser) {
-+ snprintf(status2, 99, " with username %s", dbuser);
-+ }
-+
-+ if (ctime > 31536000) {
-+ ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 86400) {
-+ ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 3600) {
-+ ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
-+ } else if (ctime > 60) {
-+ ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
-+ } else {
-+ ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
-+ }
-+
-+ return RESULT_SUCCESS;
-+ } else {
-+ return RESULT_FAILURE;
-+ }
-+}
+++ /dev/null
-diff -ruN asterisk-1.2.1-old/codecs/ilbc/Makefile asterisk-1.2.1-new/codecs/ilbc/Makefile
---- asterisk-1.2.1-old/codecs/ilbc/Makefile 2005-11-29 19:24:39.000000000 +0100
-+++ asterisk-1.2.1-new/codecs/ilbc/Makefile 2005-12-16 19:24:36.000000000 +0100
-@@ -1,5 +1,5 @@
- ARCH=$(PROC)
--CFLAGS+=-Wall -O3 -funroll-loops
-+CFLAGS+=-Wall $(OPTIMIZE) -funroll-loops
- ifneq (${OSARCH},CYGWIN)
- CFLAGS += -fPIC
- endif
-@@ -15,8 +15,8 @@
-
-
- $(LIB): $(OBJS)
-- ar cr $(LIB) $(OBJS)
-- ranlib $(LIB)
-+ $(AR) cr $(LIB) $(OBJS)
-+ $(RANLIB) $(LIB)
-
- clean:
- rm -f $(LIB) *.o
+++ /dev/null
---- asterisk-1.2.1/Makefile.orig 2005-12-09 10:22:06.000000000 +0100
-+++ asterisk-1.2.1/Makefile 2005-12-09 10:28:46.000000000 +0100
-@@ -322,8 +322,6 @@
- ifneq ($(wildcard .svn),)
- ASTERISKVERSIONNUM=999999
- ASTERISKVERSION=SVN-$(shell build_tools/make_svn_branch_name)
-- else
-- ASTERISKVERSIONNUM=000000
- endif
- endif
-
+++ /dev/null
-diff -ruN asterisk-1.2.1-old/channels/Makefile asterisk-1.2.1-new/channels/Makefile
---- asterisk-1.2.1-old/channels/Makefile 2006-05-07 13:06:06.000000000 +0200
-+++ asterisk-1.2.1-new/channels/Makefile 2006-05-07 15:26:00.000000000 +0200
-@@ -15,6 +15,7 @@
- #
-
- CHANNEL_LIBS=chan_sip.so chan_agent.so chan_mgcp.so chan_iax2.so chan_local.so chan_skinny.so chan_features.so
-+CXXLIBS=-lstdc++
-
- ifneq (${OSARCH},CYGWIN)
- # if you really, really want to use these drivers, uncomment the line below
-@@ -230,7 +231,7 @@
-
- ifeq (${OSARCH},Linux)
- chan_h323.so: chan_h323.o h323/libchanh323.a h323/Makefile.ast
-- $(CC) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS) -lstdc++
-+ $(CC) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS) $(CXXLIBS)
- else
- chan_h323.so: chan_h323.o h323/libchanh323.a
- $(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
-diff -ruN asterisk-1.2.1-old/channels/h323/Makefile asterisk-1.2.1-new/channels/h323/Makefile
---- asterisk-1.2.1-old/channels/h323/Makefile 2005-11-29 19:24:39.000000000 +0100
-+++ asterisk-1.2.1-new/channels/h323/Makefile 2006-05-07 13:18:46.000000000 +0200
-@@ -30,7 +30,7 @@
- touch $(SOURCES)
-
- libchanh323.a: $(OBJS)
-- ar crv $@ $(OBJS)
-+ $(AR) crv $@ $(OBJS)
-
- Makefile.ast: FORCE
- @echo H323CFLAGS = $(STDCCFLAGS) $(OPTCCFLAGS) $(CFLAGS) >$@.tmp