aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README-addons.txt27
-rw-r--r--UPGRADE.txt5
-rw-r--r--addons/Makefile43
-rw-r--r--addons/app_addon_sql_mysql.c611
-rw-r--r--addons/app_saycountpl.c136
-rw-r--r--addons/cdr_addon_mysql.c641
-rw-r--r--addons/chan_mobile.c4266
-rw-r--r--addons/chan_ooh323.c3162
-rw-r--r--addons/chan_ooh323.h113
-rw-r--r--addons/format_mp3.c336
-rw-r--r--addons/mp3/MPGLIB_README39
-rw-r--r--addons/mp3/MPGLIB_TODO2
-rw-r--r--addons/mp3/Makefile24
-rw-r--r--addons/mp3/README24
-rw-r--r--addons/mp3/common.c267
-rw-r--r--addons/mp3/dct64_i386.c335
-rw-r--r--addons/mp3/decode_i386.c153
-rw-r--r--addons/mp3/decode_ntom.c219
-rw-r--r--addons/mp3/huffman.h332
-rw-r--r--addons/mp3/interface.c323
-rw-r--r--addons/mp3/layer3.c2029
-rw-r--r--addons/mp3/mpg123.h132
-rw-r--r--addons/mp3/mpglib.h75
-rw-r--r--addons/mp3/tabinit.c81
-rw-r--r--addons/ooh323c/COPYING341
-rw-r--r--addons/ooh323c/README95
-rw-r--r--addons/ooh323c/src/Makefile.in564
-rw-r--r--addons/ooh323c/src/context.c140
-rw-r--r--addons/ooh323c/src/decode.c1050
-rw-r--r--addons/ooh323c/src/dlist.c256
-rw-r--r--addons/ooh323c/src/dlist.h204
-rw-r--r--addons/ooh323c/src/encode.c1103
-rw-r--r--addons/ooh323c/src/errmgmt.c261
-rw-r--r--addons/ooh323c/src/eventHandler.c122
-rw-r--r--addons/ooh323c/src/eventHandler.h284
-rw-r--r--addons/ooh323c/src/h323/H235-SECURITY-MESSAGES.h690
-rw-r--r--addons/ooh323c/src/h323/H235-SECURITY-MESSAGESDec.c1782
-rw-r--r--addons/ooh323c/src/h323/H235-SECURITY-MESSAGESEnc.c1269
-rw-r--r--addons/ooh323c/src/h323/H323-MESSAGES.c112
-rw-r--r--addons/ooh323c/src/h323/H323-MESSAGES.h6510
-rw-r--r--addons/ooh323c/src/h323/H323-MESSAGESDec.c27199
-rw-r--r--addons/ooh323c/src/h323/H323-MESSAGESEnc.c22596
-rw-r--r--addons/ooh323c/src/h323/MULTIMEDIA-SYSTEM-CONTROL.c40
-rw-r--r--addons/ooh323c/src/h323/MULTIMEDIA-SYSTEM-CONTROL.h10937
-rw-r--r--addons/ooh323c/src/h323/MULTIMEDIA-SYSTEM-CONTROLDec.c42170
-rw-r--r--addons/ooh323c/src/h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c26599
-rw-r--r--addons/ooh323c/src/memheap.c1331
-rw-r--r--addons/ooh323c/src/memheap.h61
-rw-r--r--addons/ooh323c/src/ooCalls.c820
-rw-r--r--addons/ooh323c/src/ooCalls.h822
-rw-r--r--addons/ooh323c/src/ooCapability.c2349
-rw-r--r--addons/ooh323c/src/ooCapability.h726
-rw-r--r--addons/ooh323c/src/ooCmdChannel.c193
-rw-r--r--addons/ooh323c/src/ooCmdChannel.h90
-rw-r--r--addons/ooh323c/src/ooCommon.h124
-rw-r--r--addons/ooh323c/src/ooDateTime.c175
-rw-r--r--addons/ooh323c/src/ooDateTime.h54
-rw-r--r--addons/ooh323c/src/ooGkClient.c2443
-rw-r--r--addons/ooh323c/src/ooGkClient.h560
-rw-r--r--addons/ooh323c/src/ooLogChan.c372
-rw-r--r--addons/ooh323c/src/ooLogChan.h190
-rw-r--r--addons/ooh323c/src/ooSocket.c723
-rw-r--r--addons/ooh323c/src/ooSocket.h403
-rw-r--r--addons/ooh323c/src/ooStackCmds.c339
-rw-r--r--addons/ooh323c/src/ooStackCmds.h171
-rw-r--r--addons/ooh323c/src/ooTimer.c193
-rw-r--r--addons/ooh323c/src/ooTimer.h128
-rw-r--r--addons/ooh323c/src/ooUtils.c40
-rw-r--r--addons/ooh323c/src/ooUtils.h53
-rw-r--r--addons/ooh323c/src/ooasn1.h1814
-rw-r--r--addons/ooh323c/src/oochannels.c1785
-rw-r--r--addons/ooh323c/src/oochannels.h216
-rw-r--r--addons/ooh323c/src/ooh245.c3686
-rw-r--r--addons/ooh323c/src/ooh245.h636
-rw-r--r--addons/ooh323c/src/ooh323.c1843
-rw-r--r--addons/ooh323c/src/ooh323.h182
-rw-r--r--addons/ooh323c/src/ooh323ep.c789
-rw-r--r--addons/ooh323c/src/ooh323ep.h634
-rw-r--r--addons/ooh323c/src/oohdr.h15
-rw-r--r--addons/ooh323c/src/ooper.h22
-rw-r--r--addons/ooh323c/src/ooports.c106
-rw-r--r--addons/ooh323c/src/ooports.h91
-rw-r--r--addons/ooh323c/src/ooq931.c3448
-rw-r--r--addons/ooh323c/src/ooq931.h766
-rw-r--r--addons/ooh323c/src/ootrace.c131
-rw-r--r--addons/ooh323c/src/ootrace.h152
-rw-r--r--addons/ooh323c/src/ootypes.h256
-rw-r--r--addons/ooh323c/src/perutil.c287
-rw-r--r--addons/ooh323c/src/printHandler.c273
-rw-r--r--addons/ooh323c/src/printHandler.h47
-rw-r--r--addons/ooh323c/src/rtctype.c168
-rw-r--r--addons/ooh323c/src/rtctype.h86
-rw-r--r--addons/ooh323cDriver.c361
-rw-r--r--addons/ooh323cDriver.h39
-rw-r--r--addons/res_config_mysql.c1752
-rw-r--r--autoconf/ast_ext_tool_check.m42
-rw-r--r--build_tools/menuselect-deps.in2
-rw-r--r--configs/cdr_mysql.conf.sample58
-rw-r--r--configs/mobile.conf.sample68
-rw-r--r--configs/mysql.conf.sample24
-rw-r--r--configs/ooh323.conf.sample173
-rw-r--r--configs/res_mysql.conf.sample40
-rwxr-xr-xconfigure567
-rw-r--r--configure.ac18
-rw-r--r--doc/tex/Makefile2
-rw-r--r--doc/tex/asterisk.tex4
-rw-r--r--doc/tex/cdrdriver.tex32
-rw-r--r--doc/tex/chan_mobile.tex262
-rw-r--r--include/asterisk/autoconfig.h.in9
-rw-r--r--include/asterisk/mod_format.h1
-rw-r--r--main/file.c25
-rw-r--r--makeopts.in6
113 files changed, 190907 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index a74b95b9d..0d57d651d 100644
--- a/Makefile
+++ b/Makefile
@@ -293,7 +293,7 @@ endif
# value directly to ASTCFLAGS
ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS)
-MOD_SUBDIRS:=channels pbx apps codecs formats cdr cel bridges funcs tests main res $(LOCAL_MOD_SUBDIRS)
+MOD_SUBDIRS:=addons channels pbx apps codecs formats cdr cel bridges funcs tests main res $(LOCAL_MOD_SUBDIRS)
OTHER_SUBDIRS:=utils agi
SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
diff --git a/README-addons.txt b/README-addons.txt
new file mode 100644
index 000000000..4d5e1986f
--- /dev/null
+++ b/README-addons.txt
@@ -0,0 +1,27 @@
+===============================================================================
+=== Asterisk Add-on Modules ===
+===============================================================================
+
+ This document pertains to the modules that reside in the addons/
+subdirectory of the source tree. By default, these modules are not compiled
+and installed. If you choose to enable them, you must be aware of what
+potential licensing and/or patent implications that has on your usage and
+distribution of Asterisk.
+
+ Even though Asterisk is released as open source under the terms of the
+GPLv2 (see LICENSE for details), no core functionality in Asterisk has any
+dependencies on libraries that are licensed under the GPL. One reason a module
+may be in the add-ons category is that it may have a GPL dependency. Since
+these dependencies are not compatible with dual licensing of Asterisk, the
+dependant modules are set aside to make it clear that they may not be used
+with commercial versions of Asterisk, unless other licensing arrangements are
+made with the copyright holders of those dependencies.
+
+ Another reason that modules may be set aside is that there may be
+additional restrictions on the usage of the code imposed by the license or
+related patents. The MySQL and MP3 modules are examples of this.
+
+ If you have any questions, contact your lawyer.
+
+===============================================================================
+===============================================================================
diff --git a/UPGRADE.txt b/UPGRADE.txt
index d670e414d..651ea18e0 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -20,6 +20,11 @@
From 1.6.2 to 1.6.3:
+* Asterisk-addons no longer exists as an independent package. Those modules
+ now live in the addons directory of the main Asterisk source tree. They
+ are not enabled by default. For more information about why modules live in
+ addons, see README-addons.txt.
+
* The rarely used 'event_log' and LOG_EVENT channel have been removed; the few
users of this channel in the tree have been converted to LOG_NOTICE or removed
(in cases where the same message was already generated to another channel).
diff --git a/addons/Makefile b/addons/Makefile
new file mode 100644
index 000000000..c93759702
--- /dev/null
+++ b/addons/Makefile
@@ -0,0 +1,43 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for Add-on Modules
+#
+# Copyright (C) 2009, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=app bridge cdr cel chan codec format func pbx res test
+MENUSELECT_CATEGORY=Addons
+MENUSELECT_DESCRIPTION=Add-ons (See README-addons.txt)
+
+H323OBJS:=ooCmdChannel.o ooLogChan.o ooUtils.o ooGkClient.o context.o \
+ ooDateTime.o decode.o dlist.o encode.o errmgmt.o \
+ memheap.o ootrace.o oochannels.o ooh245.o ooports.o \
+ ooq931.o ooCapability.o ooSocket.o perutil.o eventHandler.o \
+ ooCalls.o ooStackCmds.o ooh323.o ooh323ep.o printHandler.o \
+ rtctype.o ooTimer.o h323/H235-SECURITY-MESSAGESDec.o \
+ h323/H235-SECURITY-MESSAGESEnc.o h323/H323-MESSAGES.o h323/H323-MESSAGESDec.o \
+ h323/H323-MESSAGESEnc.o h323/MULTIMEDIA-SYSTEM-CONTROL.o \
+ h323/MULTIMEDIA-SYSTEM-CONTROLDec.o h323/MULTIMEDIA-SYSTEM-CONTROLEnc.o
+
+H323CFLAGS:=-Iooh323c/src -Iooh323c/src/h323
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+clean::
+ $(MAKE) -C mp3 clean
+ rm -f $(addprefix ooh323c/src/,$(H323OBJS))
+
+$(if $(filter format_mp3,$(EMBEDDED_MODS)),modules.link,format_mp3.so): mp3/common.o mp3/dct64_i386.o mp3/decode_ntom.o mp3/layer3.o mp3/tabinit.o mp3/interface.o
+
+chan_ooh323.o: ASTCFLAGS+=$(H323CFLAGS)
+
+$(if $(filter chan_ooh323,$(EMBEDDED_MODS)),modules.link,chan_ooh323.so): ASTCFLAGS+=$(H323CFLAGS)
+$(if $(filter chan_ooh323,$(EMBEDDED_MODS)),modules.link,chan_ooh323.so): $(addprefix ooh323c/src/,$(H323OBJS)) chan_ooh323.o ooh323cDriver.o
diff --git a/addons/app_addon_sql_mysql.c b/addons/app_addon_sql_mysql.c
new file mode 100644
index 000000000..db33c11cb
--- /dev/null
+++ b/addons/app_addon_sql_mysql.c
@@ -0,0 +1,611 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004, Constantine Filin and Christos Ricudis
+ *
+ * Christos Ricudis <ricudis@itc.auth.gr>
+ * Constantine Filin <cf@intermedia.net>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief MYSQL dialplan application
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>mysqlclient</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+#include <mysql/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"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+
+#define EXTRA_LOG 0
+
+enum { NULLSTRING, NULLVALUE, EMPTYSTRING } nullvalue = NULLSTRING;
+
+static const char app[] = "MYSQL";
+
+static const char synopsis[] = "Do several mySQLy things";
+
+static const char descrip[] =
+"MYSQL(): Do several mySQLy things\n"
+"Syntax:\n"
+" MYSQL(Set timeout <num>)\n"
+" Set the connection timeout, in seconds.\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 ${connid}\n"
+" MYSQL(Query resultid ${connid} query-string)\n"
+" Executes standard MySQL query contained in query-string using established\n"
+" connection identified by ${connid}. Result of query is stored in ${resultid}.\n"
+" MYSQL(Nextresult resultid ${connid}\n"
+" If last query returned more than one result set, it stores the next\n"
+" result set in ${resultid}. It's useful with stored procedures\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})
+*/
+
+AST_MUTEX_DEFINE_STATIC(_mysql_mutex);
+
+#define MYSQL_CONFIG "mysql.conf"
+#define AST_MYSQL_ID_DUMMY 0
+#define AST_MYSQL_ID_CONNID 1
+#define AST_MYSQL_ID_RESID 2
+#define AST_MYSQL_ID_FETCHID 3
+
+static int autoclear = 0;
+
+static void mysql_ds_destroy(void *data);
+static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static struct ast_datastore_info mysql_ds_info = {
+ .type = "APP_ADDON_SQL_MYSQL",
+ .destroy = mysql_ds_destroy,
+ .chan_fixup = mysql_ds_fixup,
+};
+
+struct ast_MYSQL_id {
+ struct ast_channel *owner;
+ 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;
+
+static void mysql_ds_destroy(void *data)
+{
+ /* Destroy any IDs owned by the channel */
+ struct ast_MYSQL_id *i;
+ if (AST_LIST_LOCK(&_mysql_ids_head)) {
+ ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
+ } else {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
+ if (i->owner == data) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ if (i->identifier_type == AST_MYSQL_ID_CONNID) {
+ /* Drop connection */
+ mysql_close(i->data);
+ } else if (i->identifier_type == AST_MYSQL_ID_RESID) {
+ /* Drop result */
+ mysql_free_result(i->data);
+ }
+ ast_free(i);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&_mysql_ids_head);
+ }
+}
+
+static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ /* Destroy any IDs owned by the channel */
+ struct ast_MYSQL_id *i;
+ if (AST_LIST_LOCK(&_mysql_ids_head)) {
+ ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
+ } else {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
+ if (i->owner == data) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ if (i->identifier_type == AST_MYSQL_ID_CONNID) {
+ /* Drop connection */
+ mysql_close(i->data);
+ } else if (i->identifier_type == AST_MYSQL_ID_RESID) {
+ /* Drop result */
+ mysql_free_result(i->data);
+ }
+ ast_free(i);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&_mysql_ids_head);
+ }
+}
+
+/* helpful procs */
+static void *find_identifier(int identifier, int identifier_type)
+{
+ struct MYSQLidshead *headp = &_mysql_ids_head;
+ struct ast_MYSQL_id *i;
+ void *res=NULL;
+ int found=0;
+
+ 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(struct ast_channel *chan, int identifier_type, void *data)
+{
+ struct ast_MYSQL_id *i = NULL, *j = NULL;
+ struct MYSQLidshead *headp = &_mysql_ids_head;
+ int maxidentifier = 0;
+
+ if (AST_LIST_LOCK(headp)) {
+ ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
+ return -1;
+ } else {
+ i = malloc(sizeof(*i));
+ 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;
+ i->owner = chan;
+ 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 = &_mysql_ids_head;
+ int found = 0;
+
+ 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[12] = "";
+ snprintf(s, sizeof(s), "%d", id);
+ ast_debug(5, "MYSQL: setting var '%s' to value '%s'\n", varname, s);
+ 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(chan, 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;
+}
+
+static int aMYSQL_set(struct ast_channel *chan, char *data)
+{
+ char *var, *tmp;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(set);
+ AST_APP_ARG(variable);
+ AST_APP_ARG(value);
+ );
+
+ AST_NONSTANDARD_APP_ARGS(args, data, ' ');
+
+ if (args.argc == 3) {
+ var = alloca(6 + strlen(args.variable) + 1);
+ sprintf(var, "MYSQL_%s", args.variable);
+
+ /* Make the parameter case-insensitive */
+ for (tmp = var + 6; *tmp; tmp++)
+ *tmp = toupper(*tmp);
+
+ pbx_builtin_setvar_helper(chan, var, args.value);
+ }
+ return 0;
+}
+
+/* MYSQL operations */
+static int aMYSQL_connect(struct ast_channel *chan, char *data)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(connect);
+ AST_APP_ARG(connid);
+ AST_APP_ARG(dbhost);
+ AST_APP_ARG(dbuser);
+ AST_APP_ARG(dbpass);
+ AST_APP_ARG(dbname);
+ );
+ MYSQL *mysql;
+ int timeout;
+ const char *ctimeout;
+
+ AST_NONSTANDARD_APP_ARGS(args, data, ' ');
+
+ if (args.argc != 6) {
+ ast_log(LOG_WARNING, "MYSQL_connect is missing some arguments\n");
+ return -1;
+ }
+
+ if (!(mysql = mysql_init(NULL))) {
+ ast_log(LOG_WARNING, "mysql_init returned NULL\n");
+ return -1;
+ }
+
+ ctimeout = pbx_builtin_getvar_helper(chan, "MYSQL_TIMEOUT");
+ if (ctimeout && sscanf(ctimeout, "%d", &timeout) == 1) {
+ mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout);
+ }
+
+ if (! mysql_real_connect(mysql, args.dbhost, args.dbuser, args.dbpass, args.dbname, 0, NULL,
+#ifdef CLIENT_MULTI_STATEMENTS
+ CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS
+#elif defined(CLIENT_MULTI_QUERIES)
+ CLIENT_MULTI_QUERIES
+#else
+ 0
+#endif
+ )) {
+ ast_log(LOG_WARNING, "mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed(%d): %s\n",
+ args.dbhost, args.dbuser, args.dbname, mysql_errno(mysql), mysql_error(mysql));
+ return -1;
+ }
+
+ add_identifier_and_set_asterisk_int(chan, args.connid, AST_MYSQL_ID_CONNID, mysql);
+ return 0;
+}
+
+static int aMYSQL_query(struct ast_channel *chan, char *data)
+{
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(query);
+ AST_APP_ARG(resultid);
+ AST_APP_ARG(connid);
+ AST_APP_ARG(sql);
+ );
+ MYSQL *mysql;
+ MYSQL_RES *mysqlres;
+ int connid;
+ int mysql_query_res;
+
+ AST_NONSTANDARD_APP_ARGS(args, data, ' ');
+
+ if (args.argc != 4 || (connid = atoi(args.connid)) == 0) {
+ ast_log(LOG_WARNING, "missing some arguments\n");
+ return -1;
+ }
+
+ if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
+ ast_log(LOG_WARNING, "Invalid connection identifier %s passed in aMYSQL_query\n", args.connid);
+ return -1;
+ }
+
+ if ((mysql_query_res = mysql_query(mysql, args.sql)) != 0) {
+ ast_log(LOG_WARNING, "aMYSQL_query: mysql_query failed. Error: %s\n", mysql_error(mysql));
+ return -1;
+ }
+
+ if ((mysqlres = mysql_store_result(mysql))) {
+ add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
+ return 0;
+ } else if (!mysql_field_count(mysql)) {
+ return 0;
+ } else
+ ast_log(LOG_WARNING, "mysql_store_result() failed on query %s\n", args.sql);
+
+ return -1;
+}
+
+static int aMYSQL_nextresult(struct ast_channel *chan, char *data)
+{
+ MYSQL *mysql;
+ MYSQL_RES *mysqlres;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(nextresult);
+ AST_APP_ARG(resultid);
+ AST_APP_ARG(connid);
+ );
+ int connid = -1;
+
+ AST_NONSTANDARD_APP_ARGS(args, data, ' ');
+ sscanf(args.connid, "%d", &connid);
+
+ if (args.argc != 3 || connid <= 0) {
+ ast_log(LOG_WARNING, "missing some arguments\n");
+ return -1;
+ }
+
+ if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
+ ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_query\n", connid);
+ return -1;
+ }
+
+ if (mysql_more_results(mysql)) {
+ mysql_next_result(mysql);
+ if ((mysqlres = mysql_store_result(mysql))) {
+ add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
+ return 0;
+ } else if (!mysql_field_count(mysql)) {
+ return 0;
+ } else
+ ast_log(LOG_WARNING, "mysql_store_result() failed on storing next_result\n");
+ } else
+ ast_log(LOG_WARNING, "mysql_more_results() result set has no more results\n");
+
+ return 0;
+}
+
+
+static int aMYSQL_fetch(struct ast_channel *chan, char *data)
+{
+ MYSQL_RES *mysqlres;
+ MYSQL_ROW mysqlrow;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(fetch);
+ AST_APP_ARG(resultvar);
+ AST_APP_ARG(fetchid);
+ AST_APP_ARG(vars);
+ );
+ char *s5, *parse;
+ int resultid = -1, numFields, j;
+
+ parse = ast_strdupa(data);
+ AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
+ sscanf(args.fetchid, "%d", &resultid);
+
+ if (args.resultvar && (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(&args.vars, " ");
+ if (s5 == NULL) {
+ ast_log(LOG_WARNING, "ast_MYSQL_fetch: More fields (%d) than variables (%d)\n", numFields, j);
+ break;
+ }
+
+ pbx_builtin_setvar_helper(chan, s5, mysqlrow[j] ? mysqlrow[j] :
+ nullvalue == NULLSTRING ? "NULL" :
+ nullvalue == EMPTYSTRING ? "" :
+ NULL);
+ }
+ ast_debug(5, "ast_MYSQL_fetch: numFields=%d\n", numFields);
+ set_asterisk_int(chan, args.resultvar, 1); /* try more rows */
+ } else {
+ ast_debug(5, "ast_MYSQL_fetch : EOF\n");
+ set_asterisk_int(chan, args.resultvar, 0); /* no more rows */
+ }
+ return 0;
+ } else {
+ set_asterisk_int(chan, args.resultvar, 0);
+ 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, const char *data)
+{
+ int result;
+ char sresult[10];
+
+ ast_debug(5, "MYSQL: data=%s\n", data);
+
+ if (!data) {
+ ast_log(LOG_WARNING, "MYSQL requires an argument (see manual)\n");
+ return -1;
+ }
+
+ result = 0;
+
+ if (autoclear) {
+ struct ast_datastore *mysql_store = ast_channel_datastore_find(chan, &mysql_ds_info, NULL);
+ if (!mysql_store) {
+ if (!(mysql_store = ast_datastore_alloc(&mysql_ds_info, NULL))) {
+ ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
+ } else {
+ mysql_store->data = chan;
+ ast_channel_datastore_add(chan, mysql_store);
+ }
+ }
+ }
+ 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("nextresult", data, strlen("nextresult")) == 0) {
+ result = aMYSQL_nextresult(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 if (strncasecmp("set", data, 3) == 0) {
+ result = aMYSQL_set(chan, ast_strdupa(data));
+ } else {
+ ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n", data);
+ result = -1;
+ }
+
+ ast_mutex_unlock(&_mysql_mutex);
+
+ snprintf(sresult, sizeof(sresult), "%d", result);
+ pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ struct MYSQLidshead *headp = &_mysql_ids_head;
+ struct ast_flags config_flags = { 0 };
+ struct ast_config *cfg = ast_config_load(MYSQL_CONFIG, config_flags);
+ const char *temp;
+
+ if (cfg) {
+ if ((temp = ast_variable_retrieve(cfg, "general", "nullvalue"))) {
+ if (!strcasecmp(temp, "nullstring")) {
+ nullvalue = NULLSTRING;
+ } else if (!strcasecmp(temp, "emptystring")) {
+ nullvalue = EMPTYSTRING;
+ } else if (!strcasecmp(temp, "null")) {
+ nullvalue = NULLVALUE;
+ } else {
+ ast_log(LOG_WARNING, "Illegal value for 'nullvalue': '%s' (must be 'nullstring', 'null', or 'emptystring')\n", temp);
+ }
+ }
+ if ((temp = ast_variable_retrieve(cfg, "general", "autoclear")) && ast_true(temp)) {
+ autoclear = 1;
+ }
+ ast_config_destroy(cfg);
+ }
+
+ AST_LIST_HEAD_INIT(headp);
+ return ast_register_application(app, MYSQL_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Mysql Interface");
diff --git a/addons/app_saycountpl.c b/addons/app_saycountpl.c
new file mode 100644
index 000000000..fd921de1a
--- /dev/null
+++ b/addons/app_saycountpl.c
@@ -0,0 +1,136 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004, Andy Powell & TAAN Softworks Corp.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Say Polish counting words
+ * \author Andy Powell
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+/*** DOCUMENTATION
+ <application name="SayCountPL" language="en_US">
+ <synopsis>
+ Say Polish counting words.
+ </synopsis>
+ <syntax>
+ <parameter name="word1" required="true" />
+ <parameter name="word2" required="true" />
+ <parameter name="word5" required="true" />
+ <parameter name="number" required="true" />
+ </syntax>
+ <description>
+ <para>Polish grammar has some funny rules for counting words. for example 1 zloty,
+ 2 zlote, 5 zlotych. This application will take the words for 1, 2-4 and 5 and
+ decide based on grammar rules which one to use with the number you pass to it.</para>
+ <para>Example: SayCountPL(zloty,zlote,zlotych,122) will give: zlote</para>
+ </description>
+ </application>
+
+ ***/
+static const char app[] = "SayCountPL";
+
+static int saywords(struct ast_channel *chan, char *word1, char *word2, char *word5, int num)
+{
+ /* Put this in a separate proc because it's bound to change */
+ int d = 0;
+
+ if (num > 0) {
+ if (num % 1000 == 1) {
+ ast_streamfile(chan, word1, chan->language);
+ d = ast_waitstream(chan,"");
+ } else if (((num % 10) >= 2) && ((num % 10) <= 4 ) && ((num % 100) < 10 || (num % 100) > 20)) {
+ ast_streamfile(chan, word2, chan->language);
+ d = ast_waitstream(chan, "");
+ } else {
+ ast_streamfile(chan, word5, chan->language);
+ d = ast_waitstream(chan, "");
+ }
+ }
+
+ return d;
+}
+
+
+static int sayword_exec(struct ast_channel *chan, const char *data)
+{
+ int res = 0;
+ char *s;
+ int inum;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(word1);
+ AST_APP_ARG(word2);
+ AST_APP_ARG(word5);
+ AST_APP_ARG(num);
+ );
+
+ if (!data) {
+ ast_log(LOG_WARNING, "SayCountPL requires 4 arguments: word-1,word-2,word-5,number\n");
+ return -1;
+ }
+
+ s = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, s);
+
+ /* Check to see if params passed */
+ if (!args.word1 || !args.word2 || !args.word5 || !args.num) {
+ ast_log(LOG_WARNING, "SayCountPL requires 4 arguments: word-1,word-2,word-3,number\n");
+ return -1;
+ }
+
+ if (sscanf(args.num, "%d", &inum) != 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num);
+ return -1;
+ }
+
+ /* do the saying part (after a bit of maths) */
+
+ res = saywords(chan, args.word1, args.word2, args.word5, inum);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_application_xml(app, sayword_exec);
+
+ return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say polish counting words");
diff --git a/addons/cdr_addon_mysql.c b/addons/cdr_addon_mysql.c
new file mode 100644
index 000000000..2a3ef4365
--- /dev/null
+++ b/addons/cdr_addon_mysql.c
@@ -0,0 +1,641 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * 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
+ *
+ * Modified Juli 2006
+ * Martin Portmann <map@infinitum.ch>
+ * Added mysql ssl support
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief MySQL CDR backend
+ * \ingroup cdr_drivers
+ */
+
+/*** MODULEINFO
+ <depend>mysqlclient</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <mysql/mysql.h>
+#include <mysql/errmsg.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"
+#include "asterisk/strings.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/threadstorage.h"
+
+#define DATE_FORMAT "%Y-%m-%d %T"
+
+AST_THREADSTORAGE(sql1_buf);
+AST_THREADSTORAGE(sql2_buf);
+AST_THREADSTORAGE(escape_buf);
+
+static const char desc[] = "MySQL CDR Backend";
+static const char name[] = "mysql";
+static const char config[] = "cdr_mysql.conf";
+
+static struct ast_str *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL, *dbcharset = NULL;
+
+static struct ast_str *ssl_ca = NULL, *ssl_cert = NULL, *ssl_key = NULL;
+
+static int dbport = 0;
+static int connected = 0;
+static time_t connect_time = 0;
+static int records = 0;
+static int totalrecords = 0;
+static int timeout = 0;
+static int calldate_compat = 0;
+
+AST_MUTEX_DEFINE_STATIC(mysql_lock);
+
+struct unload_string {
+ AST_LIST_ENTRY(unload_string) entry;
+ struct ast_str *str;
+};
+
+static AST_LIST_HEAD_STATIC(unload_strings, unload_string);
+
+struct column {
+ char *name;
+ char *cdrname;
+ char *staticvalue;
+ char *type;
+ AST_LIST_ENTRY(column) list;
+};
+
+/* Protected with mysql_lock */
+static AST_RWLIST_HEAD_STATIC(columns, column);
+
+static MYSQL mysql = { { NULL }, };
+
+static char *handle_cli_cdr_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cdr mysql status";
+ e->usage =
+ "Usage: cdr mysql status\n"
+ " Shows current connection status for cdr_mysql\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ if (connected) {
+ char status[256], status2[100] = "";
+ int ctime = time(NULL) - connect_time;
+ if (dbport)
+ snprintf(status, 255, "Connected to %s@%s, port %d", ast_str_buffer(dbname), ast_str_buffer(hostname), dbport);
+ else if (dbsock)
+ snprintf(status, 255, "Connected to %s on socket file %s", ast_str_buffer(dbname), S_OR(ast_str_buffer(dbsock), "default"));
+ else
+ snprintf(status, 255, "Connected to %s@%s", ast_str_buffer(dbname), ast_str_buffer(hostname));
+
+ if (!ast_strlen_zero(ast_str_buffer(dbuser)))
+ snprintf(status2, 99, " with username %s", ast_str_buffer(dbuser));
+ if (ast_str_strlen(dbtable))
+ snprintf(status2, 99, " using table %s", ast_str_buffer(dbtable));
+ if (ctime > 31536000) {
+ ast_cli(a->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(a->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(a->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(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
+ } else {
+ ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
+ }
+ if (records == totalrecords)
+ ast_cli(a->fd, " Wrote %d records since last restart.\n", totalrecords);
+ else
+ ast_cli(a->fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
+ } else {
+ ast_cli(a->fd, "Not currently connected to a MySQL server.\n");
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cdr_mysql_status_cli[] = {
+ AST_CLI_DEFINE(handle_cli_cdr_mysql_status, "Show connection status of cdr_mysql"),
+};
+
+static int mysql_log(struct ast_cdr *cdr)
+{
+ struct ast_str *sql1 = ast_str_thread_get(&sql1_buf, 1024), *sql2 = ast_str_thread_get(&sql2_buf, 1024);
+ int retries = 5;
+#if MYSQL_VERSION_ID >= 50013
+ my_bool my_bool_true = 1;
+#endif
+
+ if (!sql1 || !sql2) {
+ ast_log(LOG_ERROR, "Memory error\n");
+ return -1;
+ }
+
+ ast_mutex_lock(&mysql_lock);
+
+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, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
+ }
+#if MYSQL_VERSION_ID >= 50013
+ /* Add option for automatic reconnection */
+ if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
+ ast_log(LOG_ERROR, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
+ }
+#endif
+ if (ssl_ca || ssl_cert || ssl_key) {
+ mysql_ssl_set(&mysql, ssl_key ? ast_str_buffer(ssl_key) : NULL, ssl_cert ? ast_str_buffer(ssl_cert) : NULL, ssl_ca ? ast_str_buffer(ssl_ca) : NULL, NULL, NULL);
+ }
+ if (mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL, ssl_ca ? CLIENT_SSL : 0)) {
+ connected = 1;
+ connect_time = time(NULL);
+ records = 0;
+ if (dbcharset) {
+ ast_str_set(&sql1, 0, "SET NAMES '%s'", ast_str_buffer(dbcharset));
+ mysql_real_query(&mysql, ast_str_buffer(sql1), ast_str_strlen(sql1));
+ ast_debug(1, "SQL command as follows: %s\n", ast_str_buffer(sql1));
+ }
+ } else {
+ ast_log(LOG_ERROR, "Cannot connect to database server %s: (%d) %s\n", ast_str_buffer(hostname), mysql_errno(&mysql), mysql_error(&mysql));
+ connected = 0;
+ }
+ } else {
+ /* Long connection - ping the server */
+ int error;
+ if ((error = mysql_ping(&mysql))) {
+ connected = 0;
+ records = 0;
+ switch (mysql_errno(&mysql)) {
+ case CR_SERVER_GONE_ERROR:
+ case CR_SERVER_LOST:
+ ast_log(LOG_ERROR, "Server has gone away. Attempting to reconnect.\n");
+ break;
+ default:
+ ast_log(LOG_ERROR, "Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
+ }
+ retries--;
+ if (retries) {
+ goto db_reconnect;
+ } else {
+ ast_log(LOG_ERROR, "Retried to connect five times, giving up.\n");
+ }
+ }
+ }
+
+ if (connected) {
+ int column_count = 0;
+ char *cdrname;
+ char workspace[2048], *value = NULL;
+ struct column *entry;
+ struct ast_str *escape = ast_str_thread_get(&escape_buf, 16);
+
+ ast_str_set(&sql1, 0, "INSERT INTO %s (", AS_OR(dbtable, "cdr"));
+ ast_str_set(&sql2, 0, ") VALUES (");
+
+ AST_RWLIST_RDLOCK(&columns);
+ AST_RWLIST_TRAVERSE(&columns, entry, list) {
+ if (!strcmp(entry->name, "calldate")) {
+ /*!\note
+ * For some dumb reason, "calldate" used to be formulated using
+ * the datetime the record was posted, rather than the start
+ * time of the call. If someone really wants the old compatible
+ * behavior, it's provided here.
+ */
+ if (calldate_compat) {
+ struct timeval tv = ast_tvnow();
+ struct ast_tm tm;
+ char timestr[128];
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
+ ast_cdr_setvar(cdr, "calldate", timestr, 0);
+ cdrname = "calldate";
+ } else {
+ cdrname = "start";
+ }
+ } else {
+ cdrname = entry->cdrname;
+ }
+
+ /* Construct SQL */
+
+ /* Need the type and value to determine if we want the raw value or not */
+ if (entry->staticvalue) {
+ value = ast_strdupa(entry->staticvalue);
+ } else if ((!strcmp(cdrname, "start") ||
+ !strcmp(cdrname, "answer") ||
+ !strcmp(cdrname, "end") ||
+ !strcmp(cdrname, "disposition") ||
+ !strcmp(cdrname, "amaflags")) &&
+ (strstr(entry->type, "int") ||
+ strstr(entry->type, "dec") ||
+ strstr(entry->type, "float") ||
+ strstr(entry->type, "double") ||
+ strstr(entry->type, "real") ||
+ strstr(entry->type, "numeric") ||
+ strstr(entry->type, "fixed"))) {
+ ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 1);
+ } else {
+ ast_cdr_getvar(cdr, cdrname, &value, workspace, sizeof(workspace), 0, 0);
+ }
+
+ if (value) {
+ size_t valsz;
+
+ if (column_count++) {
+ ast_str_append(&sql1, 0, ",");
+ ast_str_append(&sql2, 0, ",");
+ }
+
+ ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1);
+ mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz);
+
+ ast_str_append(&sql1, 0, "%s", entry->name);
+ ast_str_append(&sql2, 0, "'%s'", ast_str_buffer(escape));
+ }
+ }
+ AST_RWLIST_UNLOCK(&columns);
+
+ ast_debug(1, "Inserting a CDR record.\n");
+ ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
+
+ ast_debug(1, "SQL command as follows: %s\n", ast_str_buffer(sql1));
+
+ if (mysql_real_query(&mysql, ast_str_buffer(sql1), ast_str_strlen(sql1))) {
+ ast_log(LOG_ERROR, "Failed to insert into database: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
+ mysql_close(&mysql);
+ connected = 0;
+ } else {
+ records++;
+ totalrecords++;
+ }
+ }
+ ast_mutex_unlock(&mysql_lock);
+ return 0;
+}
+
+static int my_unload_module(int reload)
+{
+ struct unload_string *us;
+ struct column *entry;
+
+ ast_cli_unregister_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
+
+ if (connected) {
+ mysql_close(&mysql);
+ connected = 0;
+ records = 0;
+ }
+
+ AST_LIST_LOCK(&unload_strings);
+ while ((us = AST_LIST_REMOVE_HEAD(&unload_strings, entry))) {
+ ast_free(us->str);
+ ast_free(us);
+ }
+ AST_LIST_UNLOCK(&unload_strings);
+
+ if (!reload) {
+ AST_RWLIST_WRLOCK(&columns);
+ }
+ while ((entry = AST_RWLIST_REMOVE_HEAD(&columns, list))) {
+ ast_free(entry);
+ }
+ if (!reload) {
+ AST_RWLIST_UNLOCK(&columns);
+ }
+
+ dbport = 0;
+ ast_cdr_unregister(name);
+
+ return 0;
+}
+
+static int my_load_config_string(struct ast_config *cfg, const char *category, const char *variable, struct ast_str **field, const char *def)
+{
+ struct unload_string *us;
+ const char *tmp;
+
+ if (!(us = ast_calloc(1, sizeof(*us))))
+ return -1;
+
+ if (!(*field = ast_str_create(16))) {
+ ast_free(us);
+ return -1;
+ }
+
+ us->str = *field;
+
+ AST_LIST_LOCK(&unload_strings);
+ AST_LIST_INSERT_HEAD(&unload_strings, us, entry);
+ AST_LIST_UNLOCK(&unload_strings);
+
+ tmp = ast_variable_retrieve(cfg, category, variable);
+
+ ast_str_set(field, 0, "%s", tmp ? tmp : def);
+
+ return 0;
+}
+
+static int my_load_config_number(struct ast_config *cfg, const char *category, const char *variable, int *field, int def)
+{
+ const char *tmp;
+
+ tmp = ast_variable_retrieve(cfg, category, variable);
+
+ if (!tmp || sscanf(tmp, "%d", field) < 1)
+ *field = def;
+
+ return 0;
+}
+
+static int my_load_module(int reload)
+{
+ int res;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct column *entry;
+ char *temp;
+ struct ast_str *compat;
+ MYSQL_ROW row;
+ MYSQL_RES *result;
+ char sqldesc[128];
+#if MYSQL_VERSION_ID >= 50013
+ my_bool my_bool_true = 1;
+#endif
+
+ cfg = ast_config_load(config, config_flags);
+ if (!cfg) {
+ ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
+ return AST_MODULE_LOAD_SUCCESS;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return AST_MODULE_LOAD_SUCCESS;
+
+ if (reload) {
+ AST_RWLIST_WRLOCK(&columns);
+ my_unload_module(1);
+ }
+
+ var = ast_variable_browse(cfg, "global");
+ if (!var) {
+ /* nothing configured */
+ if (reload) {
+ AST_RWLIST_UNLOCK(&columns);
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+ }
+
+ res = 0;
+
+ res |= my_load_config_string(cfg, "global", "hostname", &hostname, "localhost");
+ res |= my_load_config_string(cfg, "global", "dbname", &dbname, "astriskcdrdb");
+ res |= my_load_config_string(cfg, "global", "user", &dbuser, "root");
+ res |= my_load_config_string(cfg, "global", "sock", &dbsock, "");
+ res |= my_load_config_string(cfg, "global", "table", &dbtable, "cdr");
+ res |= my_load_config_string(cfg, "global", "password", &password, "");
+
+ res |= my_load_config_string(cfg, "global", "charset", &dbcharset, "");
+
+ res |= my_load_config_string(cfg, "global", "ssl_ca", &ssl_ca, "");
+ res |= my_load_config_string(cfg, "global", "ssl_cert", &ssl_cert, "");
+ res |= my_load_config_string(cfg, "global", "ssl_key", &ssl_key, "");
+
+ res |= my_load_config_number(cfg, "global", "port", &dbport, 0);
+ res |= my_load_config_number(cfg, "global", "timeout", &timeout, 0);
+ res |= my_load_config_string(cfg, "global", "compat", &compat, "no");
+ if (ast_true(ast_str_buffer(compat))) {
+ calldate_compat = 1;
+ } else {
+ calldate_compat = 0;
+ }
+
+ if (res < 0) {
+ if (reload) {
+ AST_RWLIST_UNLOCK(&columns);
+ }
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ /* Check for any aliases */
+ if (!reload) {
+ /* Lock, if not already */
+ AST_RWLIST_WRLOCK(&columns);
+ }
+ while ((entry = AST_LIST_REMOVE_HEAD(&columns, list))) {
+ ast_free(entry);
+ }
+
+ ast_debug(1, "Got hostname of %s\n", ast_str_buffer(hostname));
+ ast_debug(1, "Got port of %d\n", dbport);
+ ast_debug(1, "Got a timeout of %d\n", timeout);
+ if (dbsock)
+ ast_debug(1, "Got sock file of %s\n", ast_str_buffer(dbsock));
+ ast_debug(1, "Got user of %s\n", ast_str_buffer(dbuser));
+ ast_debug(1, "Got dbname of %s\n", ast_str_buffer(dbname));
+ ast_debug(1, "Got password of %s\n", ast_str_buffer(password));
+ ast_debug(1, "%sunning in calldate compatibility mode\n", calldate_compat ? "R" : "Not r");
+
+ if (dbcharset) {
+ ast_debug(1, "Got DB charset of %s\n", ast_str_buffer(dbcharset));
+ }
+
+ 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_VERSION_ID >= 50013
+ /* Add option for automatic reconnection */
+ if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
+ ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
+ }
+#endif
+
+ if ((ssl_ca && ast_str_strlen(ssl_ca)) || (ssl_cert && ast_str_strlen(ssl_cert)) || (ssl_key && ast_str_strlen(ssl_key))) {
+ mysql_ssl_set(&mysql,
+ ssl_key ? ast_str_buffer(ssl_key) : NULL,
+ ssl_cert ? ast_str_buffer(ssl_cert) : NULL,
+ ssl_ca ? ast_str_buffer(ssl_ca) : NULL,
+ NULL, NULL);
+ }
+ temp = dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL;
+ if (!mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, temp, ssl_ca && ast_str_strlen(ssl_ca) ? CLIENT_SSL : 0)) {
+ ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", ast_str_buffer(dbname), ast_str_buffer(hostname));
+ connected = 0;
+ records = 0;
+ } else {
+ ast_debug(1, "Successfully connected to MySQL database.\n");
+ connected = 1;
+ records = 0;
+ connect_time = time(NULL);
+ if (dbcharset) {
+ snprintf(sqldesc, sizeof(sqldesc), "SET NAMES '%s'", ast_str_buffer(dbcharset));
+ mysql_real_query(&mysql, sqldesc, strlen(sqldesc));
+ ast_debug(1, "SQL command as follows: %s\n", sqldesc);
+ }
+
+ /* Get table description */
+ snprintf(sqldesc, sizeof(sqldesc), "DESC %s", dbtable ? ast_str_buffer(dbtable) : "cdr");
+ if (mysql_query(&mysql, sqldesc)) {
+ ast_log(LOG_ERROR, "Unable to query table description!! Logging disabled.\n");
+ mysql_close(&mysql);
+ connected = 0;
+ AST_RWLIST_UNLOCK(&columns);
+ ast_config_destroy(cfg);
+ return AST_MODULE_LOAD_SUCCESS;
+ }
+
+ if (!(result = mysql_store_result(&mysql))) {
+ ast_log(LOG_ERROR, "Unable to query table description!! Logging disabled.\n");
+ mysql_close(&mysql);
+ connected = 0;
+ AST_RWLIST_UNLOCK(&columns);
+ ast_config_destroy(cfg);
+ return AST_MODULE_LOAD_SUCCESS;
+ }
+
+ while ((row = mysql_fetch_row(result))) {
+ struct column *entry;
+ char *cdrvar = "", *staticvalue = "";
+
+ ast_debug(1, "Got a field '%s' of type '%s'\n", row[0], row[1]);
+ /* Check for an alias or a static value */
+ for (var = ast_variable_browse(cfg, "columns"); var; var = var->next) {
+ if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, row[0]) == 0 ) {
+ char *alias = ast_strdupa(var->name + 5);
+ cdrvar = ast_strip(alias);
+ ast_verb(3, "Found alias %s for column %s\n", cdrvar, row[0]);
+ break;
+ } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, row[0]) == 0) {
+ char *item = ast_strdupa(var->name + 6);
+ item = ast_strip(item);
+ if (item[0] == '"' && item[strlen(item) - 1] == '"') {
+ /* Remove surrounding quotes */
+ item[strlen(item) - 1] = '\0';
+ item++;
+ }
+ staticvalue = item;
+ }
+ }
+
+ entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(row[0]) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1 + strlen(row[1]) + 1);
+ if (!entry) {
+ ast_log(LOG_ERROR, "Out of memory creating entry for column '%s'\n", row[0]);
+ res = -1;
+ break;
+ }
+
+ entry->name = (char *)entry + sizeof(*entry);
+ strcpy(entry->name, row[0]);
+
+ if (!ast_strlen_zero(cdrvar)) {
+ entry->cdrname = entry->name + strlen(row[0]) + 1;
+ strcpy(entry->cdrname, cdrvar);
+ } else { /* Point to same place as the column name */
+ entry->cdrname = (char *)entry + sizeof(*entry);
+ }
+
+ if (!ast_strlen_zero(staticvalue)) {
+ entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
+ strcpy(entry->staticvalue, staticvalue);
+ ast_debug(1, "staticvalue length: %d\n", (int) strlen(staticvalue) );
+ entry->type = entry->staticvalue + strlen(entry->staticvalue) + 1;
+ } else {
+ entry->type = entry->cdrname + strlen(entry->cdrname) + 1;
+ }
+ strcpy(entry->type, row[1]);
+
+ ast_debug(1, "Entry name '%s'\n", entry->name);
+ ast_debug(1, " cdrname '%s'\n", entry->cdrname);
+ ast_debug(1, " static '%s'\n", entry->staticvalue);
+ ast_debug(1, " type '%s'\n", entry->type);
+
+ AST_LIST_INSERT_TAIL(&columns, entry, list);
+ }
+ mysql_free_result(result);
+ }
+ AST_RWLIST_UNLOCK(&columns);
+ ast_config_destroy(cfg);
+ if (res < 0) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ 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_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
+ }
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return my_load_module(0);
+}
+
+static int unload_module(void)
+{
+ return my_unload_module(0);
+}
+
+static int reload(void)
+{
+ int ret;
+
+ ast_mutex_lock(&mysql_lock);
+ ret = my_load_module(1);
+ ast_mutex_unlock(&mysql_lock);
+
+ return ret;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MySQL CDR Backend",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+);
+
diff --git a/addons/chan_mobile.c b/addons/chan_mobile.c
new file mode 100644
index 000000000..415ca617e
--- /dev/null
+++ b/addons/chan_mobile.c
@@ -0,0 +1,4266 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Bluetooth Mobile Device channel driver
+ *
+ * \author Dave Bowerman <david.bowerman@gmail.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>bluetooth</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <pthread.h>
+#include <signal.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
+
+#include "asterisk/compat.h"
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/cli.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/causes.h"
+#include "asterisk/dsp.h"
+#include "asterisk/app.h"
+#include "asterisk/manager.h"
+#include "asterisk/io.h"
+
+#define MBL_CONFIG "mobile.conf"
+
+#define DEVICE_FRAME_SIZE 48
+#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
+#define CHANNEL_FRAME_SIZE 320
+
+static int prefformat = DEVICE_FRAME_FORMAT;
+
+static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
+static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */
+static sdp_session_t *sdp_session;
+
+AST_MUTEX_DEFINE_STATIC(unload_mutex);
+static int unloading_flag = 0;
+static inline int check_unloading(void);
+static inline void set_unloading(void);
+
+enum mbl_type {
+ MBL_TYPE_PHONE,
+ MBL_TYPE_HEADSET
+};
+
+struct adapter_pvt {
+ int dev_id; /* device id */
+ int hci_socket; /* device descriptor */
+ char id[31]; /* the 'name' from mobile.conf */
+ bdaddr_t addr; /* adddress of adapter */
+ unsigned int inuse:1; /* are we in use ? */
+ unsigned int alignment_detection:1; /* do alignment detection on this adpater? */
+ struct io_context *io; /*!< io context for audio connections */
+ struct io_context *accept_io; /*!< io context for sco listener */
+ int *sco_id; /*!< the io context id of the sco listener socket */
+ int sco_socket; /*!< sco listener socket */
+ pthread_t sco_listener_thread; /*!< sco listener thread */
+ AST_LIST_ENTRY(adapter_pvt) entry;
+};
+
+static AST_RWLIST_HEAD_STATIC(adapters, adapter_pvt);
+
+struct msg_queue_entry;
+struct hfp_pvt;
+struct mbl_pvt {
+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
+ struct ast_frame fr; /* "null" frame */
+ ast_mutex_t lock; /*!< pvt lock */
+ /*! queue for messages we are expecting */
+ AST_LIST_HEAD_NOLOCK(msg_queue, msg_queue_entry) msg_queue;
+ enum mbl_type type; /* Phone or Headset */
+ char id[31]; /* The id from mobile.conf */
+ int group; /* group number for group dialling */
+ bdaddr_t addr; /* address of device */
+ struct adapter_pvt *adapter; /* the adapter we use */
+ char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
+ struct hfp_pvt *hfp; /*!< hfp pvt */
+ int rfcomm_port; /* rfcomm port number */
+ int rfcomm_socket; /* rfcomm socket descriptor */
+ char rfcomm_buf[256];
+ char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET];
+ struct ast_smoother *smoother; /* our smoother, for making 48 byte frames */
+ int sco_socket; /* sco socket descriptor */
+ pthread_t monitor_thread; /* monitor thread handle */
+ int timeout; /*!< used to set the timeout for rfcomm data (may be used in the future) */
+ unsigned int no_callsetup:1;
+ unsigned int has_sms:1;
+ unsigned int do_alignment_detection:1;
+ unsigned int alignment_detection_triggered:1;
+ unsigned int blackberry:1;
+ short alignment_samples[4];
+ int alignment_count;
+ int ring_sched_id;
+ struct ast_dsp *dsp;
+ struct sched_context *sched;
+
+ /* flags */
+ unsigned int outgoing:1; /*!< outgoing call */
+ unsigned int incoming:1; /*!< incoming call */
+ unsigned int outgoing_sms:1; /*!< outgoing sms */
+ unsigned int incoming_sms:1; /*!< outgoing sms */
+ unsigned int needcallerid:1; /*!< we need callerid */
+ unsigned int needchup:1; /*!< we need to send a chup */
+ unsigned int needring:1; /*!< we need to send a RING */
+ unsigned int answered:1; /*!< we sent/recieved an answer */
+ unsigned int connected:1; /*!< do we have an rfcomm connection to a device */
+
+ AST_LIST_ENTRY(mbl_pvt) entry;
+};
+
+static AST_RWLIST_HEAD_STATIC(devices, mbl_pvt);
+
+static int handle_response_ok(struct mbl_pvt *pvt, char *buf);
+static int handle_response_error(struct mbl_pvt *pvt, char *buf);
+static int handle_response_ciev(struct mbl_pvt *pvt, char *buf);
+static int handle_response_clip(struct mbl_pvt *pvt, char *buf);
+static int handle_response_ring(struct mbl_pvt *pvt, char *buf);
+static int handle_response_cmti(struct mbl_pvt *pvt, char *buf);
+static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf);
+static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf);
+
+/* CLI stuff */
+static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry mbl_cli[] = {
+ AST_CLI_DEFINE(handle_cli_mobile_show_devices, "Show Bluetooth Cell / Mobile devices"),
+ AST_CLI_DEFINE(handle_cli_mobile_search, "Search for Bluetooth Cell / Mobile devices"),
+ AST_CLI_DEFINE(handle_cli_mobile_rfcomm, "Send commands to the rfcomm port for debugging"),
+};
+
+/* App stuff */
+static char *app_mblstatus = "MobileStatus";
+static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
+static char *mblstatus_desc =
+"MobileStatus(Device,Variable)\n"
+" Device - Id of mobile device from mobile.conf\n"
+" Variable - Variable to store status in will be 1-3.\n"
+" In order, Disconnected, Connected & Free, Connected & Busy.\n";
+
+static char *app_mblsendsms = "MobileSendSMS";
+static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
+static char *mblsendsms_desc =
+"MobileSendSms(Device,Dest,Message)\n"
+" Device - Id of device from mobile.conf\n"
+" Dest - destination\n"
+" Message - text of the message\n";
+
+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
+ const struct ast_channel *requestor);
+static struct ast_channel *mbl_request(const char *type, int format,
+ const struct ast_channel *requestor, void *data, int *cause);
+static int mbl_call(struct ast_channel *ast, char *dest, int timeout);
+static int mbl_hangup(struct ast_channel *ast);
+static int mbl_answer(struct ast_channel *ast);
+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static struct ast_frame *mbl_read(struct ast_channel *ast);
+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int mbl_devicestate(void *data);
+
+static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen);
+
+static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control);
+static int mbl_queue_hangup(struct mbl_pvt *pvt);
+static int mbl_ast_hangup(struct mbl_pvt *pvt);
+
+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel);
+static int rfcomm_write(int rsock, char *buf);
+static int rfcomm_write_full(int rsock, char *buf, size_t count);
+static int rfcomm_wait(int rsock, int *ms);
+static ssize_t rfcomm_read(int rsock, char *buf, size_t count);
+
+static int sco_connect(bdaddr_t src, bdaddr_t dst);
+static int sco_write(int s, char *buf, int len);
+static int sco_accept(int *id, int fd, short events, void *data);
+static int sco_bind(struct adapter_pvt *adapter);
+
+static void *do_sco_listen(void *data);
+static int sdp_search(char *addr, int profile);
+
+static int headset_send_ring(const void *data);
+
+/*
+ * bluetooth handsfree profile helpers
+ */
+
+#define HFP_HF_ECNR (1 << 0)
+#define HFP_HF_CW (1 << 1)
+#define HFP_HF_CID (1 << 2)
+#define HFP_HF_VOICE (1 << 3)
+#define HFP_HF_VOLUME (1 << 4)
+#define HFP_HF_STATUS (1 << 5)
+#define HFP_HF_CONTROL (1 << 6)
+
+#define HFP_AG_CW (1 << 0)
+#define HFP_AG_ECNR (1 << 1)
+#define HFP_AG_VOICE (1 << 2)
+#define HFP_AG_RING (1 << 3)
+#define HFP_AG_TAG (1 << 4)
+#define HFP_AG_REJECT (1 << 5)
+#define HFP_AG_STATUS (1 << 6)
+#define HFP_AG_CONTROL (1 << 7)
+#define HFP_AG_ERRORS (1 << 8)
+
+#define HFP_CIND_UNKNOWN -1
+#define HFP_CIND_NONE 0
+#define HFP_CIND_SERVICE 1
+#define HFP_CIND_CALL 2
+#define HFP_CIND_CALLSETUP 3
+#define HFP_CIND_CALLHELD 4
+#define HFP_CIND_SIGNAL 5
+#define HFP_CIND_ROAM 6
+#define HFP_CIND_BATTCHG 7
+
+/* call indicator values */
+#define HFP_CIND_CALL_NONE 0
+#define HFP_CIND_CALL_ACTIVE 1
+
+/* callsetup indicator values */
+#define HFP_CIND_CALLSETUP_NONE 0
+#define HFP_CIND_CALLSETUP_INCOMING 1
+#define HFP_CIND_CALLSETUP_OUTGOING 2
+#define HFP_CIND_CALLSETUP_ALERTING 3
+
+/*!
+ * \brief This struct holds HFP features that we support.
+ */
+struct hfp_hf {
+ int ecnr:1; /*!< echo-cancel/noise reduction */
+ int cw:1; /*!< call waiting and three way calling */
+ int cid:1; /*!< cli presentation (callier id) */
+ int voice:1; /*!< voice recognition activation */
+ int volume:1; /*!< remote volume control */
+ int status:1; /*!< enhanced call status */
+ int control:1; /*!< enhanced call control*/
+};
+
+/*!
+ * \brief This struct holds HFP features the AG supports.
+ */
+struct hfp_ag {
+ int cw:1; /*!< three way calling */
+ int ecnr:1; /*!< echo-cancel/noise reduction */
+ int voice:1; /*!< voice recognition */
+ int ring:1; /*!< in band ring tone capability */
+ int tag:1; /*!< attach a number to a voice tag */
+ int reject:1; /*!< ability to reject a call */
+ int status:1; /*!< enhanced call status */
+ int control:1; /*!< enhanced call control*/
+ int errors:1; /*!< extended error result codes*/
+};
+
+/*!
+ * \brief This struct holds mappings for indications.
+ */
+struct hfp_cind {
+ int service; /*!< whether we have service or not */
+ int call; /*!< call state */
+ int callsetup; /*!< bluetooth call setup indications */
+ int callheld; /*!< bluetooth call hold indications */
+ int signal; /*!< signal strength */
+ int roam; /*!< roaming indicator */
+ int battchg; /*!< battery charge indicator */
+};
+
+
+/*!
+ * \brief This struct holds state information about the current hfp connection.
+ */
+struct hfp_pvt {
+ struct mbl_pvt *owner; /*!< the mbl_pvt struct that owns this struct */
+ int initialized:1; /*!< whether a service level connection exists or not */
+ int nocallsetup:1; /*!< whether we detected a callsetup indicator */
+ struct hfp_ag brsf; /*!< the supported feature set of the AG */
+ int cind_index[16]; /*!< the cind/ciev index to name mapping for this AG */
+ int cind_state[16]; /*!< the cind/ciev state for this AG */
+ struct hfp_cind cind_map; /*!< the cind name to index mapping for this AG */
+ int rsock; /*!< our rfcomm socket */
+ int rport; /*!< our rfcomm port */
+};
+
+
+/* Our supported features.
+ * we only support caller id
+ */
+static struct hfp_hf hfp_our_brsf = {
+ .ecnr = 0,
+ .cw = 0,
+ .cid = 1,
+ .voice = 0,
+ .volume = 0,
+ .status = 0,
+ .control = 0,
+};
+
+
+static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value);
+static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, char **text);
+static int hfp_parse_brsf(struct hfp_pvt *hfp, const char *buf);
+static int hfp_parse_cind(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cind_test(struct hfp_pvt *hfp, char *buf);
+
+static int hfp_brsf2int(struct hfp_hf *hf);
+static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag);
+
+static int hfp_send_brsf(struct hfp_pvt *hfp, struct hfp_hf *brsf);
+static int hfp_send_cind(struct hfp_pvt *hfp);
+static int hfp_send_cind_test(struct hfp_pvt *hfp);
+static int hfp_send_cmer(struct hfp_pvt *hfp, int status);
+static int hfp_send_clip(struct hfp_pvt *hfp, int status);
+static int hfp_send_vgs(struct hfp_pvt *hfp, int value);
+
+#if 0
+static int hfp_send_vgm(struct hfp_pvt *hfp, int value);
+#endif
+static int hfp_send_dtmf(struct hfp_pvt *hfp, char digit);
+static int hfp_send_cmgf(struct hfp_pvt *hfp, int mode);
+static int hfp_send_cnmi(struct hfp_pvt *hfp);
+static int hfp_send_cmgr(struct hfp_pvt *hfp, int index);
+static int hfp_send_cmgs(struct hfp_pvt *hfp, const char *number);
+static int hfp_send_sms_text(struct hfp_pvt *hfp, const char *message);
+static int hfp_send_chup(struct hfp_pvt *hfp);
+static int hfp_send_atd(struct hfp_pvt *hfp, const char *number);
+static int hfp_send_ata(struct hfp_pvt *hfp);
+
+/*
+ * bluetooth headset profile helpers
+ */
+static int hsp_send_ok(int rsock);
+static int hsp_send_error(int rsock);
+static int hsp_send_vgs(int rsock, int gain);
+static int hsp_send_vgm(int rsock, int gain);
+static int hsp_send_ring(int rsock);
+
+
+/*
+ * Hayes AT command helpers
+ */
+typedef enum {
+ /* errors */
+ AT_PARSE_ERROR = -2,
+ AT_READ_ERROR = -1,
+ AT_UNKNOWN = 0,
+ /* at responses */
+ AT_OK,
+ AT_ERROR,
+ AT_RING,
+ AT_BRSF,
+ AT_CIND,
+ AT_CIEV,
+ AT_CLIP,
+ AT_CMTI,
+ AT_CMGR,
+ AT_SMS_PROMPT,
+ AT_CMS_ERROR,
+ /* at commands */
+ AT_A,
+ AT_D,
+ AT_CHUP,
+ AT_CKPD,
+ AT_CMGS,
+ AT_VGM,
+ AT_VGS,
+ AT_VTS,
+ AT_CMGF,
+ AT_CNMI,
+ AT_CMER,
+ AT_CIND_TEST,
+} at_message_t;
+
+static int at_match_prefix(char *buf, char *prefix);
+static at_message_t at_read_full(int rsock, char *buf, size_t count);
+static inline const char *at_msg2str(at_message_t msg);
+
+struct msg_queue_entry {
+ at_message_t expected;
+ at_message_t response_to;
+ void *data;
+
+ AST_LIST_ENTRY(msg_queue_entry) entry;
+};
+
+static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to);
+static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data);
+static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt);
+static void msg_queue_free_and_pop(struct mbl_pvt *pvt);
+static void msg_queue_flush(struct mbl_pvt *pvt);
+static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt);
+
+/*
+ * channel stuff
+ */
+
+static const struct ast_channel_tech mbl_tech = {
+ .type = "Mobile",
+ .description = "Bluetooth Mobile Device Channel Driver",
+ .capabilities = AST_FORMAT_SLINEAR,
+ .requester = mbl_request,
+ .call = mbl_call,
+ .hangup = mbl_hangup,
+ .answer = mbl_answer,
+ .send_digit_end = mbl_digit_end,
+ .read = mbl_read,
+ .write = mbl_write,
+ .fixup = mbl_fixup,
+ .devicestate = mbl_devicestate
+};
+
+/* CLI Commands implementation */
+
+static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mbl_pvt *pvt;
+ char bdaddr[18];
+ char group[6];
+
+#define FORMAT1 "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-5.5s %-3.3s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile show devices";
+ e->usage =
+ "Usage: mobile show devices\n"
+ " Shows the state of Bluetooth Cell / Mobile devices.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT1, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS");
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ ast_mutex_lock(&pvt->lock);
+ ba2str(&pvt->addr, bdaddr);
+ snprintf(group, sizeof(group), "%d", pvt->group);
+ ast_cli(a->fd, FORMAT1,
+ pvt->id,
+ bdaddr,
+ group,
+ pvt->adapter->id,
+ pvt->connected ? "Yes" : "No",
+ (!pvt->connected) ? "None" : (pvt->owner) ? "Busy" : (pvt->outgoing_sms || pvt->incoming_sms) ? "SMS" : "Free",
+ (pvt->has_sms) ? "Yes" : "No"
+ );
+ ast_mutex_unlock(&pvt->lock);
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+#undef FORMAT1
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct adapter_pvt *adapter;
+ inquiry_info *ii = NULL;
+ int max_rsp, num_rsp;
+ int len, flags;
+ int i, phport, hsport;
+ char addr[19] = {0};
+ char name[31] = {0};
+
+#define FORMAT1 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
+#define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile search";
+ e->usage =
+ "Usage: mobile search\n"
+ " Searches for Bluetooth Cell / Mobile devices in range.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ /* find a free adapter */
+ AST_RWLIST_RDLOCK(&adapters);
+ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
+ if (!adapter->inuse)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+
+ if (!adapter) {
+ ast_cli(a->fd, "All Bluetooth adapters are in use at this time.\n");
+ return CLI_SUCCESS;
+ }
+
+ len = 8;
+ max_rsp = 255;
+ flags = IREQ_CACHE_FLUSH;
+
+ ii = alloca(max_rsp * sizeof(inquiry_info));
+ num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags);
+ if (num_rsp > 0) {
+ ast_cli(a->fd, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
+ for (i = 0; i < num_rsp; i++) {
+ ba2str(&(ii + i)->bdaddr, addr);
+ name[0] = 0x00;
+ if (hci_read_remote_name(adapter->hci_socket, &(ii + i)->bdaddr, sizeof(name) - 1, name, 0) < 0)
+ strcpy(name, "[unknown]");
+ phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
+ if (!phport)
+ hsport = sdp_search(addr, HEADSET_PROFILE_ID);
+ else
+ hsport = 0;
+ ast_cli(a->fd, FORMAT2, addr, name, (phport > 0 || hsport > 0) ? "Yes" : "No",
+ (phport > 0) ? "Phone" : "Headset", (phport > 0) ? phport : hsport);
+ }
+ } else
+ ast_cli(a->fd, "No Bluetooth Cell / Mobile devices found.\n");
+
+#undef FORMAT1
+#undef FORMAT2
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buf[128];
+ struct mbl_pvt *pvt = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile rfcomm";
+ e->usage =
+ "Usage: mobile rfcomm <device ID> <command>\n"
+ " Send <command> to the rfcomm port on the device\n"
+ " with the specified <device ID>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, a->argv[2]))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt) {
+ ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
+ goto e_return;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ if (!pvt->connected) {
+ ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
+ goto e_unlock_pvt;
+ }
+
+ snprintf(buf, sizeof(buf), "%s\r", a->argv[3]);
+ rfcomm_write(pvt->rfcomm_socket, buf);
+ msg_queue_push(pvt, AT_OK, AT_UNKNOWN);
+
+e_unlock_pvt:
+ ast_mutex_unlock(&pvt->lock);
+e_return:
+ return CLI_SUCCESS;
+}
+
+/*
+
+ Dialplan applications implementation
+
+*/
+
+static int mbl_status_exec(struct ast_channel *ast, const char *data)
+{
+
+ struct mbl_pvt *pvt;
+ char *parse;
+ int stat;
+ char status[2];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(device);
+ AST_APP_ARG(variable);
+ );
+
+ if (ast_strlen_zero(data))
+ return -1;
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable))
+ return -1;
+
+ stat = 1;
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, args.device))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (pvt) {
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->connected)
+ stat = 2;
+ if (pvt->owner)
+ stat = 3;
+ ast_mutex_unlock(&pvt->lock);
+ }
+
+ snprintf(status, sizeof(status), "%d", stat);
+ pbx_builtin_setvar_helper(ast, args.variable, status);
+
+ return 0;
+
+}
+
+static int mbl_sendsms_exec(struct ast_channel *ast, const char *data)
+{
+
+ struct mbl_pvt *pvt;
+ char *parse, *message;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(device);
+ AST_APP_ARG(dest);
+ AST_APP_ARG(message);
+ );
+
+ if (ast_strlen_zero(data))
+ return -1;
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.device)) {
+ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.dest)) {
+ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.message)) {
+ ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, args.device))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt) {
+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device);
+ goto e_return;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ if (!pvt->connected) {
+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device);
+ goto e_unlock_pvt;
+ }
+
+ if (!pvt->has_sms) {
+ ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device);
+ goto e_unlock_pvt;
+ }
+
+ message = ast_strdup(args.message);
+
+ if (hfp_send_cmgs(pvt->hfp, args.dest)
+ || msg_queue_push_data(pvt, AT_SMS_PROMPT, AT_CMGS, message)) {
+
+ ast_log(LOG_ERROR, "[%s] problem sending SMS message\n", pvt->id);
+ goto e_free_message;
+ }
+
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+
+e_free_message:
+ ast_free(message);
+e_unlock_pvt:
+ ast_mutex_unlock(&pvt->lock);
+e_return:
+ return -1;
+}
+
+/*
+
+ Channel Driver callbacks
+
+*/
+
+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
+ const struct ast_channel *requestor)
+{
+
+ struct ast_channel *chn;
+
+ pvt->answered = 0;
+ pvt->alignment_count = 0;
+ pvt->alignment_detection_triggered = 0;
+ if (pvt->adapter->alignment_detection)
+ pvt->do_alignment_detection = 1;
+ else
+ pvt->do_alignment_detection = 0;
+
+ ast_smoother_reset(pvt->smoother, DEVICE_FRAME_SIZE);
+ ast_dsp_digitreset(pvt->dsp);
+
+ chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context,
+ requestor ? requestor->linkedid : "", 0,
+ "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
+ if (!chn) {
+ goto e_return;
+ }
+
+ chn->tech = &mbl_tech;
+ chn->nativeformats = prefformat;
+ chn->rawreadformat = prefformat;
+ chn->rawwriteformat = prefformat;
+ chn->writeformat = prefformat;
+ chn->readformat = prefformat;
+ chn->tech_pvt = pvt;
+
+ if (state == AST_STATE_RING)
+ chn->rings = 1;
+
+ ast_string_field_set(chn, language, "en");
+ pvt->owner = chn;
+
+ if (pvt->sco_socket != -1) {
+ ast_channel_set_fd(chn, 0, pvt->sco_socket);
+ }
+
+ return chn;
+
+e_return:
+ return NULL;
+}
+
+static struct ast_channel *mbl_request(const char *type, int format,
+ const struct ast_channel *requestor, void *data, int *cause)
+{
+
+ struct ast_channel *chn = NULL;
+ struct mbl_pvt *pvt;
+ char *dest_dev = NULL;
+ char *dest_num = NULL;
+ int oldformat, group = -1;
+
+ if (!data) {
+ ast_log(LOG_WARNING, "Channel requested with no data\n");
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+
+ oldformat = format;
+ format &= (AST_FORMAT_SLINEAR);
+ if (!format) {
+ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
+ return NULL;
+ }
+
+ dest_dev = ast_strdupa((char *)data);
+
+ dest_num = strchr(dest_dev, '/');
+ if (dest_num)
+ *dest_num++ = 0x00;
+
+ if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
+ group = atoi(&dest_dev[1]);
+ }
+
+ /* Find requested device and make sure it's connected. */
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (group > -1 && pvt->group == group && pvt->connected && !pvt->owner) {
+ break;
+ } else if (!strcmp(pvt->id, dest_dev)) {
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&devices);
+ if (!pvt || !pvt->connected || pvt->owner) {
+ ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ return NULL;
+ }
+
+ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
+ ast_log(LOG_WARNING, "Can't determine destination number.\n");
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL, requestor);
+ ast_mutex_unlock(&pvt->lock);
+ if (!chn) {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ return NULL;
+ }
+
+ return chn;
+
+}
+
+static int mbl_call(struct ast_channel *ast, char *dest, int timeout)
+{
+
+ struct mbl_pvt *pvt;
+ char *dest_dev = NULL;
+ char *dest_num = NULL;
+
+ dest_dev = ast_strdupa((char *)dest);
+
+ pvt = ast->tech_pvt;
+
+ if (pvt->type == MBL_TYPE_PHONE) {
+ dest_num = strchr(dest_dev, '/');
+ if (!dest_num) {
+ ast_log(LOG_WARNING, "Cant determine destination number.\n");
+ return -1;
+ }
+ *dest_num++ = 0x00;
+ }
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ ast_debug(1, "Calling %s on %s\n", dest, ast->name);
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->type == MBL_TYPE_PHONE) {
+ if (hfp_send_atd(pvt->hfp, dest_num)) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id);
+ return -1;
+ }
+ pvt->needchup = 1;
+ msg_queue_push(pvt, AT_OK, AT_D);
+ } else {
+ if (hsp_send_ring(pvt->rfcomm_socket)) {
+ ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
+ ast_mutex_unlock(&pvt->lock);
+ return -1;
+ }
+
+ if ((pvt->ring_sched_id = ast_sched_add(pvt->sched, 6000, headset_send_ring, pvt)) == -1) {
+ ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
+ ast_mutex_unlock(&pvt->lock);
+ return -1;
+ }
+
+ pvt->outgoing = 1;
+ pvt->needring = 1;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+
+}
+
+static int mbl_hangup(struct ast_channel *ast)
+{
+
+ struct mbl_pvt *pvt;
+
+ if (!ast->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ pvt = ast->tech_pvt;
+
+ ast_debug(1, "[%s] hanging up device\n", pvt->id);
+
+ ast_mutex_lock(&pvt->lock);
+ ast_channel_set_fd(ast, 0, -1);
+ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+
+ if (pvt->needchup) {
+ hfp_send_chup(pvt->hfp);
+ msg_queue_push(pvt, AT_OK, AT_CHUP);
+ pvt->needchup = 0;
+ }
+
+ pvt->outgoing = 0;
+ pvt->incoming = 0;
+ pvt->needring = 0;
+ pvt->owner = NULL;
+ ast->tech_pvt = NULL;
+
+ ast_mutex_unlock(&pvt->lock);
+
+ ast_setstate(ast, AST_STATE_DOWN);
+
+ return 0;
+
+}
+
+static int mbl_answer(struct ast_channel *ast)
+{
+
+ struct mbl_pvt *pvt;
+
+ pvt = ast->tech_pvt;
+
+ if (pvt->type == MBL_TYPE_HEADSET)
+ return 0;
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->incoming) {
+ hfp_send_ata(pvt->hfp);
+ msg_queue_push(pvt, AT_OK, AT_A);
+ pvt->answered = 1;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+
+}
+
+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ struct mbl_pvt *pvt = ast->tech_pvt;
+
+ if (pvt->type == MBL_TYPE_HEADSET)
+ return 0;
+
+ ast_mutex_lock(&pvt->lock);
+ if (hfp_send_dtmf(pvt->hfp, digit)) {
+ ast_mutex_unlock(&pvt->lock);
+ ast_debug(1, "[%s] error sending digit %c\n", pvt->id, digit);
+ return -1;
+ }
+ msg_queue_push(pvt, AT_OK, AT_VTS);
+ ast_mutex_unlock(&pvt->lock);
+
+ ast_debug(1, "[%s] dialed %c\n", pvt->id, digit);
+
+ return 0;
+}
+
+static struct ast_frame *mbl_read(struct ast_channel *ast)
+{
+
+ struct mbl_pvt *pvt = ast->tech_pvt;
+ struct ast_frame *fr = &ast_null_frame;
+ int r;
+
+ ast_debug(3, "*** mbl_read()\n");
+
+ while (ast_mutex_trylock(&pvt->lock)) {
+ CHANNEL_DEADLOCK_AVOIDANCE(ast);
+ }
+
+ if (!pvt->owner || pvt->sco_socket == -1) {
+ goto e_return;
+ }
+
+ memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
+ pvt->fr.frametype = AST_FRAME_VOICE;
+ pvt->fr.subclass = DEVICE_FRAME_FORMAT;
+ pvt->fr.src = "Mobile";
+ pvt->fr.offset = AST_FRIENDLY_OFFSET;
+ pvt->fr.mallocd = 0;
+ pvt->fr.delivery.tv_sec = 0;
+ pvt->fr.delivery.tv_usec = 0;
+ pvt->fr.data.ptr = pvt->io_buf + AST_FRIENDLY_OFFSET;
+
+ if ((r = read(pvt->sco_socket, pvt->fr.data.ptr, DEVICE_FRAME_SIZE)) == -1) {
+ if (errno != EAGAIN && errno != EINTR) {
+ ast_debug(1, "[%s] read error %d, going to wait for new connection\n", pvt->id, errno);
+ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+ ast_channel_set_fd(ast, 0, -1);
+ }
+ goto e_return;
+ }
+
+ pvt->fr.datalen = r;
+ pvt->fr.samples = r / 2;
+
+ if (pvt->do_alignment_detection)
+ do_alignment_detection(pvt, pvt->fr.data.ptr, r);
+
+ fr = ast_dsp_process(ast, pvt->dsp, &pvt->fr);
+
+ ast_mutex_unlock(&pvt->lock);
+
+ return fr;
+
+e_return:
+ ast_mutex_unlock(&pvt->lock);
+ return fr;
+}
+
+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+
+ struct mbl_pvt *pvt = ast->tech_pvt;
+ struct ast_frame *f;
+
+ ast_debug(3, "*** mbl_write\n");
+
+ if (frame->frametype != AST_FRAME_VOICE) {
+ return 0;
+ }
+
+ while (ast_mutex_trylock(&pvt->lock)) {
+ CHANNEL_DEADLOCK_AVOIDANCE(ast);
+ }
+
+ ast_smoother_feed(pvt->smoother, frame);
+
+ while ((f = ast_smoother_read(pvt->smoother))) {
+ sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
+ ast_frfree(f);
+ }
+
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+
+}
+
+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+
+ struct mbl_pvt *pvt = oldchan->tech_pvt;
+
+ if (!pvt) {
+ ast_debug(1, "fixup failed, no pvt on oldchan\n");
+ return -1;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->owner == oldchan)
+ pvt->owner = newchan;
+ ast_mutex_unlock(&pvt->lock);
+
+ return 0;
+
+}
+
+static int mbl_devicestate(void *data)
+{
+
+ char *device;
+ int res = AST_DEVICE_INVALID;
+ struct mbl_pvt *pvt;
+
+ device = ast_strdupa(S_OR(data, ""));
+
+ ast_debug(1, "Checking device state for device %s\n", device);
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, device))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt)
+ return res;
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->connected) {
+ if (pvt->owner)
+ res = AST_DEVICE_INUSE;
+ else
+ res = AST_DEVICE_NOT_INUSE;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ return res;
+
+}
+
+/*
+
+ Callback helpers
+
+*/
+
+/*
+
+ do_alignment_detection()
+
+ This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor.
+
+ Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf
+
+ Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
+ The result is static or white noise on the inbound (from the adapter) leg of the call.
+ This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
+
+ Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
+ then average the sum of the averages of frames 1, 2, and 3.
+ Frame zero is usually zero.
+ If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
+ for each subsequent frame during the call.
+
+ If the result is <= 100 then clear the flag so we dont come back in here...
+
+ This seems to work OK....
+
+*/
+
+static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen)
+{
+
+ int i;
+ short a, *s;
+ char *p;
+
+ if (pvt->alignment_detection_triggered) {
+ for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
+ *p = *(p-1);
+ *(p+1) = 0;
+ return;
+ }
+
+ if (pvt->alignment_count < 4) {
+ s = (short *)buf;
+ for (i=0, a=0; i<buflen/2; i++) {
+ a += *s++;
+ a /= i+1;
+ }
+ pvt->alignment_samples[pvt->alignment_count++] = a;
+ return;
+ }
+
+ ast_debug(1, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
+
+ a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
+ a /= 3;
+ if (a > 100) {
+ pvt->alignment_detection_triggered = 1;
+ ast_debug(1, "Alignment Detection Triggered.\n");
+ } else
+ pvt->do_alignment_detection = 0;
+
+}
+
+static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control)
+{
+ for (;;) {
+ if (pvt->owner) {
+ if (ast_channel_trylock(pvt->owner)) {
+ DEADLOCK_AVOIDANCE(&pvt->lock);
+ } else {
+ ast_queue_control(pvt->owner, control);
+ ast_channel_unlock(pvt->owner);
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+static int mbl_queue_hangup(struct mbl_pvt *pvt)
+{
+ for (;;) {
+ if (pvt->owner) {
+ if (ast_channel_trylock(pvt->owner)) {
+ DEADLOCK_AVOIDANCE(&pvt->lock);
+ } else {
+ ast_queue_hangup(pvt->owner);
+ ast_channel_unlock(pvt->owner);
+ break;
+ }
+ } else
+ break;
+ }
+ return 0;
+}
+
+static int mbl_ast_hangup(struct mbl_pvt *pvt)
+{
+ int res = 0;
+ for (;;) {
+ if (pvt->owner) {
+ if (ast_channel_trylock(pvt->owner)) {
+ DEADLOCK_AVOIDANCE(&pvt->lock);
+ } else {
+ res = ast_hangup(pvt->owner);
+ /* no need to unlock, ast_hangup() frees the
+ * channel */
+ break;
+ }
+ } else
+ break;
+ }
+ return res;
+}
+
+/*
+
+ rfcomm helpers
+
+*/
+
+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel)
+{
+
+ struct sockaddr_rc addr;
+ int s;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+ ast_debug(1, "socket() failed (%d).\n", errno);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &src);
+ addr.rc_channel = (uint8_t) 1;
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_debug(1, "bind() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &dst);
+ addr.rc_channel = remote_channel;
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_debug(1, "connect() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param rsock the socket to write to
+ * \param buf the null terminated buffer to write
+ *
+ * This function will write characters from buf. The buffer must be null
+ * terminated.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write(int rsock, char *buf)
+{
+ return rfcomm_write_full(rsock, buf, strlen(buf));
+}
+
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param rsock the socket to write to
+ * \param buf the buffer to write
+ * \param count the number of characters from the buffer to write
+ *
+ * This function will write count characters from buf. It will always write
+ * count chars unless it encounters an error.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write_full(int rsock, char *buf, size_t count)
+{
+ char *p = buf;
+ ssize_t out_count;
+
+ ast_debug(1, "rfcomm_write() (%d) [%.*s]\n", rsock, (int) count, buf);
+ while (count > 0) {
+ if ((out_count = write(rsock, p, count)) == -1) {
+ ast_debug(1, "rfcomm_write() error [%d]\n", errno);
+ return -1;
+ }
+ count -= out_count;
+ p += out_count;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Wait for activity on an rfcomm socket.
+ * \param rsock the socket to watch
+ * \param ms a pointer to an int containing a timeout in ms
+ * \return zero on timeout and the socket fd (non-zero) otherwise
+ * \retval 0 timeout
+ */
+static int rfcomm_wait(int rsock, int *ms)
+{
+ int exception, outfd;
+ outfd = ast_waitfor_n_fd(&rsock, 1, ms, &exception);
+ if (outfd < 0)
+ outfd = 0;
+
+ return outfd;
+}
+
+#ifdef RFCOMM_READ_DEBUG
+#define rfcomm_read_debug(c) __rfcomm_read_debug(c)
+static void __rfcomm_read_debug(char c)
+{
+ if (c == '\r')
+ ast_debug(2, "rfcomm_read: \\r\n");
+ else if (c == '\n')
+ ast_debug(2, "rfcomm_read: \\n\n");
+ else
+ ast_debug(2, "rfcomm_read: %c\n", c);
+}
+#else
+#define rfcomm_read_debug(c)
+#endif
+
+/*!
+ * \brief Append the given character to the given buffer and increase the
+ * in_count.
+ */
+static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c)
+{
+ if (*in_count < count) {
+ (*in_count)++;
+ *(*buf)++ = c;
+ }
+}
+
+/*!
+ * \brief Read a character from the given stream and check if it matches what
+ * we expected.
+ */
+static int rfcomm_read_and_expect_char(int rsock, char *result, char expected)
+{
+ int res;
+ char c;
+
+ if (!result)
+ result = &c;
+
+ if ((res = read(rsock, result, 1)) < 1) {
+ return res;
+ }
+ rfcomm_read_debug(*result);
+
+ if (*result != expected) {
+ return -2;
+ }
+
+ return 1;
+}
+
+/*!
+ * \brief Read a character from the given stream and append it to the given
+ * buffer if it matches the expected character.
+ */
+static int rfcomm_read_and_append_char(int rsock, char **buf, size_t count, size_t *in_count, char *result, char expected)
+{
+ int res;
+ char c;
+
+ if (!result)
+ result = &c;
+
+ if ((res = rfcomm_read_and_expect_char(rsock, result, expected)) < 1) {
+ return res;
+ }
+
+ rfcomm_append_buf(buf, count, in_count, *result);
+ return 1;
+}
+
+/*!
+ * \brief Read until '\r\n'.
+ * This function consumes the '\r\n' but does not add it to buf.
+ */
+static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
+{
+ int res;
+ char c;
+
+ while ((res = read(rsock, &c, 1)) == 1) {
+ rfcomm_read_debug(c);
+ if (c == '\r') {
+ if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) == 1) {
+ break;
+ } else if (res == -2) {
+ rfcomm_append_buf(buf, count, in_count, '\r');
+ } else {
+ rfcomm_append_buf(buf, count, in_count, '\r');
+ break;
+ }
+ }
+
+ rfcomm_append_buf(buf, count, in_count, c);
+ }
+ return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT SMS prompt.
+ * \note the entire parsed string is '\r\n> '
+ *
+ * By the time this function is executed, only a ' ' is left to read.
+ */
+static int rfcomm_read_sms_prompt(int rsock, char **buf, size_t count, size_t *in_count)
+{
+ int res;
+ if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, NULL, ' ')) < 1)
+ goto e_return;
+
+ return 1;
+
+e_return:
+ ast_log(LOG_ERROR, "error parsing SMS prompt on rfcomm socket\n");
+ return res;
+}
+
+/*!
+ * \brief Read and AT result code.
+ * \note the entire parsed string is '\r\n<result code>\r\n'
+ */
+static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
+{
+ int res;
+ char c;
+
+ if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) < 1) {
+ goto e_return;
+ }
+
+ if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, &c, '>')) == 1) {
+ return rfcomm_read_sms_prompt(rsock, buf, count, in_count);
+ } else if (res != -2) {
+ goto e_return;
+ }
+
+ rfcomm_append_buf(buf, count, in_count, c);
+ res = rfcomm_read_until_crlf(rsock, buf, count, in_count);
+
+ if (res != 1)
+ return res;
+
+ /* check for CMGR, which contains an embedded \r\n */
+ if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
+ rfcomm_append_buf(buf, count, in_count, '\r');
+ rfcomm_append_buf(buf, count, in_count, '\n');
+ return rfcomm_read_until_crlf(rsock, buf, count, in_count);
+ }
+
+ return 1;
+
+e_return:
+ ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket");
+ return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT command.
+ * \note the entire parsed string is '<at command>\r'
+ */
+static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
+{
+ int res;
+ char c;
+
+ while ((res = read(rsock, &c, 1)) == 1) {
+ rfcomm_read_debug(c);
+ /* stop when we get to '\r' */
+ if (c == '\r')
+ break;
+
+ rfcomm_append_buf(buf, count, in_count, c);
+ }
+ return res;
+}
+
+/*!
+ * \brief Read one Hayes AT message from an rfcomm socket.
+ * \param rsock the rfcomm socket to read from
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ *
+ * Here we need to read complete Hayes AT messages. The AT message formats we
+ * support are listed below.
+ *
+ * \verbatim
+ * \r\n<result code>\r\n
+ * <at command>\r
+ * \r\n>
+ * \endverbatim
+ *
+ * These formats correspond to AT result codes, AT commands, and the AT SMS
+ * prompt respectively. When messages are read the leading and trailing '\r'
+ * and '\n' characters are discarded. If the given buffer is not large enough
+ * to hold the response, what does not fit in the buffer will be dropped.
+ *
+ * \note The rfcomm connection to the device is asynchronous, so there is no
+ * guarantee that responses will be returned in a single read() call. We handle
+ * this by blocking until we can read an entire response.
+ *
+ * \retval 0 end of file
+ * \retval -1 read error
+ * \retval -2 parse error
+ * \retval other the number of characters added to buf
+ */
+static ssize_t rfcomm_read(int rsock, char *buf, size_t count)
+{
+ ssize_t res;
+ size_t in_count = 0;
+ char c;
+
+ if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) == 1) {
+ res = rfcomm_read_result(rsock, &buf, count, &in_count);
+ } else if (res == -2) {
+ rfcomm_append_buf(&buf, count, &in_count, c);
+ res = rfcomm_read_command(rsock, &buf, count, &in_count);
+ }
+
+ if (res < 1)
+ return res;
+ else
+ return in_count;
+}
+
+/*
+
+ sco helpers and callbacks
+
+*/
+
+static int sco_connect(bdaddr_t src, bdaddr_t dst)
+{
+
+ struct sockaddr_sco addr;
+ int s;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+ ast_debug(1, "socket() failed (%d).\n", errno);
+ return -1;
+ }
+
+/* XXX this does not work with the do_sco_listen() thread (which also bind()s
+ * to this address). Also I am not sure if it is necessary. */
+#if 0
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &src);
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_debug(1, "bind() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+#endif
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &dst);
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_debug(1, "sco connect() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
+static int sco_write(int s, char *buf, int len)
+{
+
+ int r;
+
+ if (s == -1) {
+ ast_debug(3, "sco_write() not ready\n");
+ return 0;
+ }
+
+ ast_debug(3, "sco_write()\n");
+
+ r = write(s, buf, len);
+ if (r == -1) {
+ ast_debug(3, "sco write error %d\n", errno);
+ return 0;
+ }
+
+ return 1;
+
+}
+
+/*!
+ * \brief Accept SCO connections.
+ * This function is an ast_io callback function used to accept incoming sco
+ * audio connections.
+ */
+static int sco_accept(int *id, int fd, short events, void *data)
+{
+ struct adapter_pvt *adapter = (struct adapter_pvt *) data;
+ struct sockaddr_sco addr;
+ socklen_t addrlen;
+ struct mbl_pvt *pvt;
+ socklen_t len;
+ char saddr[18];
+ struct sco_options so;
+ int sock;
+
+ addrlen = sizeof(struct sockaddr_sco);
+ if ((sock = accept(fd, (struct sockaddr *)&addr, &addrlen)) == -1) {
+ ast_log(LOG_ERROR, "error accepting audio connection on adapter %s\n", adapter->id);
+ return 0;
+ }
+
+ len = sizeof(so);
+ getsockopt(sock, SOL_SCO, SCO_OPTIONS, &so, &len);
+
+ ba2str(&addr.sco_bdaddr, saddr);
+ ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
+
+ /* figure out which device this sco connection belongs to */
+ pvt = NULL;
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+ if (!pvt) {
+ ast_log(LOG_WARNING, "could not find device for incoming audio connection\n");
+ close(sock);
+ return 1;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->sco_socket != -1) {
+ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+ }
+
+ pvt->sco_socket = sock;
+ if (pvt->owner) {
+ ast_channel_set_fd(pvt->owner, 0, sock);
+ } else {
+ ast_debug(1, "incoming audio connection for pvt without owner\n");
+ }
+
+ ast_mutex_unlock(&pvt->lock);
+
+ return 1;
+}
+
+/*!
+ * \brief Bind an SCO listener socket for the given adapter.
+ * \param adapter an adapter_pvt
+ * \return -1 on error, non zero on success
+ */
+static int sco_bind(struct adapter_pvt *adapter)
+{
+ struct sockaddr_sco addr;
+ int opt = 1;
+
+ if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+ ast_log(LOG_ERROR, "Unable to create sco listener socket for adapter %s.\n", adapter->id);
+ goto e_return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &adapter->addr);
+ if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
+ goto e_close_socket;
+ }
+ if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
+ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
+ goto e_close_socket;
+ }
+ if (listen(adapter->sco_socket, 5) < 0) {
+ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
+ goto e_close_socket;
+ }
+
+ return adapter->sco_socket;
+
+e_close_socket:
+ close(adapter->sco_socket);
+ adapter->sco_socket = -1;
+e_return:
+ return -1;
+}
+
+
+/*
+ * Hayes AT command helpers.
+ */
+
+/*!
+ * \brief Match the given buffer with the given prefix.
+ * \param buf the buffer to match
+ * \param prefix the prefix to match
+ */
+static int at_match_prefix(char *buf, char *prefix)
+{
+ return !strncmp(buf, prefix, strlen(prefix));
+}
+
+/*!
+ * \brief Read an AT message and clasify it.
+ * \param rsock an rfcomm socket
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ * \return the type of message received, in addition buf will contain the
+ * message received and will be null terminated
+ * \see at_read()
+ */
+static at_message_t at_read_full(int rsock, char *buf, size_t count)
+{
+ ssize_t s;
+ if ((s = rfcomm_read(rsock, buf, count - 1)) < 1)
+ return s;
+ buf[s] = '\0';
+
+ if (!strcmp("OK", buf)) {
+ return AT_OK;
+ } else if (!strcmp("ERROR", buf)) {
+ return AT_ERROR;
+ } else if (!strcmp("RING", buf)) {
+ return AT_RING;
+ } else if (!strcmp("AT+CKPD=200", buf)) {
+ return AT_CKPD;
+ } else if (!strcmp("> ", buf)) {
+ return AT_SMS_PROMPT;
+ } else if (at_match_prefix(buf, "+CMTI:")) {
+ return AT_CMTI;
+ } else if (at_match_prefix(buf, "+CIEV:")) {
+ return AT_CIEV;
+ } else if (at_match_prefix(buf, "+BRSF:")) {
+ return AT_BRSF;
+ } else if (at_match_prefix(buf, "+CIND:")) {
+ return AT_CIND;
+ } else if (at_match_prefix(buf, "+CLIP:")) {
+ return AT_CLIP;
+ } else if (at_match_prefix(buf, "+CMGR:")) {
+ return AT_CMGR;
+ } else if (at_match_prefix(buf, "+VGM:")) {
+ return AT_VGM;
+ } else if (at_match_prefix(buf, "+VGS:")) {
+ return AT_VGS;
+ } else if (at_match_prefix(buf, "+CMS ERROR:")) {
+ return AT_CMS_ERROR;
+ } else if (at_match_prefix(buf, "AT+VGM=")) {
+ return AT_VGM;
+ } else if (at_match_prefix(buf, "AT+VGS=")) {
+ return AT_VGS;
+ } else {
+ return AT_UNKNOWN;
+ }
+}
+
+/*!
+ * \brief Get the string representation of the given AT message.
+ * \param msg the message to process
+ * \return a string describing the given message
+ */
+static inline const char *at_msg2str(at_message_t msg)
+{
+ switch (msg) {
+ /* errors */
+ case AT_PARSE_ERROR:
+ return "PARSE ERROR";
+ case AT_READ_ERROR:
+ return "READ ERROR";
+ default:
+ case AT_UNKNOWN:
+ return "UNKNOWN";
+ /* at responses */
+ case AT_OK:
+ return "OK";
+ case AT_ERROR:
+ return "ERROR";
+ case AT_RING:
+ return "RING";
+ case AT_BRSF:
+ return "AT+BRSF";
+ case AT_CIND:
+ return "AT+CIND";
+ case AT_CIEV:
+ return "AT+CIEV";
+ case AT_CLIP:
+ return "AT+CLIP";
+ case AT_CMTI:
+ return "AT+CMTI";
+ case AT_CMGR:
+ return "AT+CMGR";
+ case AT_SMS_PROMPT:
+ return "SMS PROMPT";
+ case AT_CMS_ERROR:
+ return "+CMS ERROR";
+ /* at commands */
+ case AT_A:
+ return "ATA";
+ case AT_D:
+ return "ATD";
+ case AT_CHUP:
+ return "AT+CHUP";
+ case AT_CKPD:
+ return "AT+CKPD";
+ case AT_CMGS:
+ return "AT+CMGS";
+ case AT_VGM:
+ return "AT+VGM";
+ case AT_VGS:
+ return "AT+VGS";
+ case AT_VTS:
+ return "AT+VTS";
+ case AT_CMGF:
+ return "AT+CMGF";
+ case AT_CNMI:
+ return "AT+CNMI";
+ case AT_CMER:
+ return "AT+CMER";
+ case AT_CIND_TEST:
+ return "AT+CIND=?";
+ }
+}
+
+
+/*
+ * bluetooth handsfree profile helpers
+ */
+
+/*!
+ * \brief Parse a CIEV event.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \param value a pointer to an int to store the event value in (can be NULL)
+ * \return 0 on error (parse error, or unknown event) or a HFP_CIND_* value on
+ * success
+ */
+static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value)
+{
+ int i, v;
+ if (!value)
+ value = &v;
+
+ if (!sscanf(buf, "+CIEV: %d,%d", &i, value)) {
+ ast_debug(2, "[%s] error parsing CIEV event '%s'\n", hfp->owner->id, buf);
+ return HFP_CIND_NONE;
+ }
+
+ if (i >= sizeof(hfp->cind_state)) {
+ ast_debug(2, "[%s] CIEV event index too high (%s)\n", hfp->owner->id, buf);
+ return HFP_CIND_NONE;
+ }
+
+ hfp->cind_state[i] = *value;
+ return hfp->cind_index[i];
+}
+
+/*!
+ * \brief Parse a CLIP event.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the CID string is parsed
+ * \return NULL on error (parse error) or a pointer to the caller id
+ * inforamtion in buf
+ * success
+ */
+static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf)
+{
+ int i, state;
+ char *clip = NULL;
+ size_t s;
+
+ /* parse clip info in the following format:
+ * +CLIP: "123456789",128,...
+ */
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s && state != 3; i++) {
+ switch (state) {
+ case 0: /* search for start of the number (") */
+ if (buf[i] == '"') {
+ state++;
+ }
+ break;
+ case 1: /* mark the number */
+ clip = &buf[i];
+ state++;
+ /* fall through */
+ case 2: /* search for the end of the number (") */
+ if (buf[i] == '"') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ }
+ }
+
+ if (state != 3) {
+ return NULL;
+ }
+
+ return clip;
+}
+
+/*!
+ * \brief Parse a CMTI notification.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the CMTI message is parsed
+ * \return -1 on error (parse error) or the index of the new sms message
+ */
+static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf)
+{
+ int index = -1;
+
+ /* parse cmti info in the following format:
+ * +CMTI: <mem>,<index>
+ */
+ if (!sscanf(buf, "+CMTI: %*[^,],%d", &index)) {
+ ast_debug(2, "[%s] error parsing CMTI event '%s'\n", hfp->owner->id, buf);
+ return -1;
+ }
+
+ return index;
+}
+
+/*!
+ * \brief Parse a CMGR message.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \param from_number a pointer to a char pointer which will store the from
+ * number
+ * \param text a pointer to a char pointer which will store the message text
+ * @note buf will be modified when the CMGR message is parsed
+ * \retval -1 parse error
+ * \retval 0 success
+ */
+static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, char **text)
+{
+ int i, state;
+ size_t s;
+
+ /* parse cmgr info in the following format:
+ * +CMGR: <msg status>,"+123456789",...\r\n
+ * <message text>
+ */
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s && s != 6; i++) {
+ switch (state) {
+ case 0: /* search for start of the number section (,) */
+ if (buf[i] == ',') {
+ state++;
+ }
+ break;
+ case 1: /* find the opening quote (") */
+ if (buf[i] == '"') {
+ state++;
+ }
+ case 2: /* mark the start of the number */
+ if (from_number) {
+ *from_number = &buf[i];
+ state++;
+ }
+ /* fall through */
+ case 3: /* search for the end of the number (") */
+ if (buf[i] == '"') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ case 4: /* search for the start of the message text (\n) */
+ if (buf[i] == '\n') {
+ state++;
+ }
+ break;
+ case 5: /* mark the start of the message text */
+ if (text) {
+ *text = &buf[i];
+ state++;
+ }
+ break;
+ }
+ }
+
+ if (state != 6) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Convert a hfp_hf struct to a BRSF int.
+ * \param hf an hfp_hf brsf object
+ * \return an integer representing the given brsf struct
+ */
+static int hfp_brsf2int(struct hfp_hf *hf)
+{
+ int brsf = 0;
+
+ brsf |= hf->ecnr ? HFP_HF_ECNR : 0;
+ brsf |= hf->cw ? HFP_HF_CW : 0;
+ brsf |= hf->cid ? HFP_HF_CID : 0;
+ brsf |= hf->voice ? HFP_HF_VOICE : 0;
+ brsf |= hf->volume ? HFP_HF_VOLUME : 0;
+ brsf |= hf->status ? HFP_HF_STATUS : 0;
+ brsf |= hf->control ? HFP_HF_CONTROL : 0;
+
+ return brsf;
+}
+
+/*!
+ * \brief Convert a BRSF int to an hfp_ag struct.
+ * \param brsf a brsf integer
+ * \param ag a AG (hfp_ag) brsf object
+ * \return a pointer to the given hfp_ag object populated with the values from
+ * the given brsf integer
+ */
+static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag)
+{
+ ag->cw = brsf & HFP_AG_CW ? 1 : 0;
+ ag->ecnr = brsf & HFP_AG_ECNR ? 1 : 0;
+ ag->voice = brsf & HFP_AG_VOICE ? 1 : 0;
+ ag->ring = brsf & HFP_AG_RING ? 1 : 0;
+ ag->tag = brsf & HFP_AG_TAG ? 1 : 0;
+ ag->reject = brsf & HFP_AG_REJECT ? 1 : 0;
+ ag->status = brsf & HFP_AG_STATUS ? 1 : 0;
+ ag->control = brsf & HFP_AG_CONTROL ? 1 : 0;
+ ag->errors = brsf & HFP_AG_ERRORS ? 1 : 0;
+
+ return ag;
+}
+
+
+/*!
+ * \brief Send a BRSF request.
+ * \param hfp an hfp_pvt struct
+ * \param brsf an hfp_hf brsf struct
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+static int hfp_send_brsf(struct hfp_pvt *hfp, struct hfp_hf *brsf)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+BRSF=%d\r", hfp_brsf2int(brsf));
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send the CIND read command.
+ * \param hfp an hfp_pvt struct
+ */
+static int hfp_send_cind(struct hfp_pvt *hfp)
+{
+ return rfcomm_write(hfp->rsock, "AT+CIND?\r");
+}
+
+/*!
+ * \brief Send the CIND test command.
+ * \param hfp an hfp_pvt struct
+ */
+static int hfp_send_cind_test(struct hfp_pvt *hfp)
+{
+ return rfcomm_write(hfp->rsock, "AT+CIND=?\r");
+}
+
+/*!
+ * \brief Enable or disable indicator events reporting.
+ * \param hfp an hfp_pvt struct
+ * \param status enable or disable events reporting (should be 1 or 0)
+ */
+static int hfp_send_cmer(struct hfp_pvt *hfp, int status)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CMER=3,0,0,%d\r", status ? 1 : 0);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send the current speaker gain level.
+ * \param hfp an hfp_pvt struct
+ * \param value the value to send (must be between 0 and 15)
+ */
+static int hfp_send_vgs(struct hfp_pvt *hfp, int value)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+VGS=%d\r", value);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+#if 0
+/*!
+ * \brief Send the current microphone gain level.
+ * \param hfp an hfp_pvt struct
+ * \param value the value to send (must be between 0 and 15)
+ */
+static int hfp_send_vgm(struct hfp_pvt *hfp, int value)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+VGM=%d\r", value);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+#endif
+
+/*!
+ * \brief Enable or disable calling line identification.
+ * \param hfp an hfp_pvt struct
+ * \param status enable or disable calling line identification (should be 1 or
+ * 0)
+ */
+static int hfp_send_clip(struct hfp_pvt *hfp, int status)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CLIP=%d\r", status ? 1 : 0);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send a DTMF command.
+ * \param hfp an hfp_pvt struct
+ * \param digit the dtmf digit to send
+ * \return the result of rfcomm_write() or -1 on an invalid digit being sent
+ */
+static int hfp_send_dtmf(struct hfp_pvt *hfp, char digit)
+{
+ char cmd[10];
+
+ switch(digit) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ snprintf(cmd, sizeof(cmd), "AT+VTS=%c\r", digit);
+ return rfcomm_write(hfp->rsock, cmd);
+ default:
+ return -1;
+ }
+}
+
+/*!
+ * \brief Set the SMS mode.
+ * \param hfp an hfp_pvt struct
+ * \param mode the sms mode (0 = PDU, 1 = Text)
+ */
+static int hfp_send_cmgf(struct hfp_pvt *hfp, int mode)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CMGF=%d\r", mode);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Setup SMS new message indication.
+ * \param hfp an hfp_pvt struct
+ */
+static int hfp_send_cnmi(struct hfp_pvt *hfp)
+{
+ return rfcomm_write(hfp->rsock, "AT+CNMI=2,1,0,0,0\r");
+}
+
+/*!
+ * \brief Read an SMS message.
+ * \param hfp an hfp_pvt struct
+ * \param index the location of the requested message
+ */
+static int hfp_send_cmgr(struct hfp_pvt *hfp, int index)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CMGR=%d\r", index);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Start sending an SMS message.
+ * \param hfp an hfp_pvt struct
+ * \param number the destination of the message
+ */
+static int hfp_send_cmgs(struct hfp_pvt *hfp, const char *number)
+{
+ char cmd[64];
+ snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"\r", number);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send the text of an SMS message.
+ * \param hfp an hfp_pvt struct
+ * \param message the text of the message
+ */
+static int hfp_send_sms_text(struct hfp_pvt *hfp, const char *message)
+{
+ char cmd[162];
+ snprintf(cmd, sizeof(cmd), "%.160s\x1a", message);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send AT+CHUP.
+ * \param hfp an hfp_pvt struct
+ */
+static int hfp_send_chup(struct hfp_pvt *hfp)
+{
+ return rfcomm_write(hfp->rsock, "AT+CHUP\r");
+}
+
+/*!
+ * \brief Send ATD.
+ * \param hfp an hfp_pvt struct
+ * \param number the number to send
+ */
+static int hfp_send_atd(struct hfp_pvt *hfp, const char *number)
+{
+ char cmd[64];
+ snprintf(cmd, sizeof(cmd), "ATD%s;\r", number);
+ return rfcomm_write(hfp->rsock, cmd);
+}
+
+/*!
+ * \brief Send ATA.
+ * \param hfp an hfp_pvt struct
+ */
+static int hfp_send_ata(struct hfp_pvt *hfp)
+{
+ return rfcomm_write(hfp->rsock, "ATA\r");
+}
+
+/*!
+ * \brief Parse BRSF data.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ */
+static int hfp_parse_brsf(struct hfp_pvt *hfp, const char *buf)
+{
+ int brsf;
+
+ if (!sscanf(buf, "+BRSF:%d", &brsf))
+ return -1;
+
+ hfp_int2brsf(brsf, &hfp->brsf);
+
+ return 0;
+}
+
+/*!
+ * \brief Parse and store the given indicator.
+ * \param hfp an hfp_pvt struct
+ * \param group the indicator group
+ * \param indicator the indicator to parse
+ */
+static int hfp_parse_cind_indicator(struct hfp_pvt *hfp, int group, char *indicator)
+{
+ int value;
+
+ /* store the current indicator */
+ if (group >= sizeof(hfp->cind_state)) {
+ ast_debug(1, "ignoring CIND state '%s' for group %d, we only support up to %d indicators\n", indicator, group, (int) sizeof(hfp->cind_state));
+ return -1;
+ }
+
+ if (!sscanf(indicator, "%d", &value)) {
+ ast_debug(1, "error parsing CIND state '%s' for group %d\n", indicator, group);
+ return -1;
+ }
+
+ hfp->cind_state[group] = value;
+ return 0;
+}
+
+/*!
+ * \brief Read the result of the AT+CIND? command.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \note hfp_send_cind_test() and hfp_parse_cind_test() should be called at
+ * least once before this function is called.
+ */
+static int hfp_parse_cind(struct hfp_pvt *hfp, char *buf)
+{
+ int i, state, group;
+ size_t s;
+ char *indicator = NULL;
+
+ /* parse current state of all of our indicators. The list is in the
+ * following format:
+ * +CIND: 1,0,2,0,0,0,0
+ */
+ group = 0;
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s; i++) {
+ switch (state) {
+ case 0: /* search for start of the status indicators (a space) */
+ if (buf[i] == ' ') {
+ group++;
+ state++;
+ }
+ break;
+ case 1: /* mark this indicator */
+ indicator = &buf[i];
+ state++;
+ break;
+ case 2: /* search for the start of the next indicator (a comma) */
+ if (buf[i] == ',') {
+ buf[i] = '\0';
+
+ hfp_parse_cind_indicator(hfp, group, indicator);
+
+ group++;
+ state = 1;
+ }
+ break;
+ }
+ }
+
+ /* store the last indicator */
+ if (state == 2)
+ hfp_parse_cind_indicator(hfp, group, indicator);
+
+ return 0;
+}
+
+/*!
+ * \brief Parse the result of the AT+CIND=? command.
+ * \param hfp an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ */
+static int hfp_parse_cind_test(struct hfp_pvt *hfp, char *buf)
+{
+ int i, state, group;
+ size_t s;
+ char *indicator = NULL, *values;
+
+ hfp->nocallsetup = 1;
+
+ /* parse the indications list. It is in the follwing format:
+ * +CIND: ("ind1",(0-1)),("ind2",(0-5))
+ */
+ group = 0;
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s; i++) {
+ switch (state) {
+ case 0: /* search for start of indicator block */
+ if (buf[i] == '(') {
+ group++;
+ state++;
+ }
+ break;
+ case 1: /* search for '"' in indicator block */
+ if (buf[i] == '"') {
+ state++;
+ }
+ break;
+ case 2: /* mark the start of the indicator name */
+ indicator = &buf[i];
+ state++;
+ break;
+ case 3: /* look for the end of the indicator name */
+ if (buf[i] == '"') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ case 4: /* find the start of the value range */
+ if (buf[i] == '(') {
+ state++;
+ }
+ break;
+ case 5: /* mark the start of the value range */
+ values = &buf[i];
+ state++;
+ break;
+ case 6: /* find the end of the value range */
+ if (buf[i] == ')') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ case 7: /* process the values we found */
+ if (group < sizeof(hfp->cind_index)) {
+ if (!strcmp(indicator, "service")) {
+ hfp->cind_map.service = group;
+ hfp->cind_index[group] = HFP_CIND_SERVICE;
+ } else if (!strcmp(indicator, "call")) {
+ hfp->cind_map.call = group;
+ hfp->cind_index[group] = HFP_CIND_CALL;
+ } else if (!strcmp(indicator, "callsetup")) {
+ hfp->nocallsetup = 0;
+ hfp->cind_map.callsetup = group;
+ hfp->cind_index[group] = HFP_CIND_CALLSETUP;
+ } else if (!strcmp(indicator, "call_setup")) { /* non standard call setup identifier */
+ hfp->nocallsetup = 0;
+ hfp->cind_map.callsetup = group;
+ hfp->cind_index[group] = HFP_CIND_CALLSETUP;
+ } else if (!strcmp(indicator, "callheld")) {
+ hfp->cind_map.callheld = group;
+ hfp->cind_index[group] = HFP_CIND_CALLHELD;
+ } else if (!strcmp(indicator, "signal")) {
+ hfp->cind_map.signal = group;
+ hfp->cind_index[group] = HFP_CIND_SIGNAL;
+ } else if (!strcmp(indicator, "roam")) {
+ hfp->cind_map.roam = group;
+ hfp->cind_index[group] = HFP_CIND_ROAM;
+ } else if (!strcmp(indicator, "battchg")) {
+ hfp->cind_map.battchg = group;
+ hfp->cind_index[group] = HFP_CIND_BATTCHG;
+ } else {
+ hfp->cind_index[group] = HFP_CIND_UNKNOWN;
+ ast_debug(2, "ignoring unknown CIND indicator '%s'\n", indicator);
+ }
+ } else {
+ ast_debug(1, "can't store indicator %d (%s), we only support up to %d indicators", group, indicator, (int) sizeof(hfp->cind_index));
+ }
+
+ state = 0;
+ break;
+ }
+ }
+
+ hfp->owner->no_callsetup = hfp->nocallsetup;
+
+ return 0;
+}
+
+
+/*
+ * Bluetooth Headset Profile helpers
+ */
+
+/*!
+ * \brief Send an OK AT response.
+ * \param rsock the rfcomm socket to use
+ */
+static int hsp_send_ok(int rsock)
+{
+ return rfcomm_write(rsock, "\r\nOK\r\n");
+}
+
+/*!
+ * \brief Send an ERROR AT response.
+ * \param rsock the rfcomm socket to use
+ */
+static int hsp_send_error(int rsock)
+{
+ return rfcomm_write(rsock, "\r\nERROR\r\n");
+}
+
+/*!
+ * \brief Send a speaker gain unsolicited AT response
+ * \param rsock the rfcomm socket to use
+ * \param gain the speaker gain value
+ */
+static int hsp_send_vgs(int rsock, int gain)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "\r\n+VGS=%d\r\n", gain);
+ return rfcomm_write(rsock, cmd);
+}
+
+/*!
+ * \brief Send a microphone gain unsolicited AT response
+ * \param rsock the rfcomm socket to use
+ * \param gain the microphone gain value
+ */
+static int hsp_send_vgm(int rsock, int gain)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "\r\n+VGM=%d\r\n", gain);
+ return rfcomm_write(rsock, cmd);
+}
+
+/*!
+ * \brief Send a RING unsolicited AT response.
+ * \param rsock the rfcomm socket to use
+ */
+static int hsp_send_ring(int rsock)
+{
+ return rfcomm_write(rsock, "\r\nRING\r\n");
+}
+
+/*
+ * message queue functions
+ */
+
+/*!
+ * \brief Add an item to the back of the queue.
+ * \param pvt a mbl_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ */
+static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to)
+{
+ struct msg_queue_entry *msg;
+ if (!(msg = ast_calloc(1, sizeof(*msg)))) {
+ return -1;
+ }
+ msg->expected = expect;
+ msg->response_to = response_to;
+
+ AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+ return 0;
+}
+
+/*!
+ * \brief Add an item to the back of the queue with data.
+ * \param pvt a mbl_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ * \param data data associated with this message, it will be freed when the
+ * message is freed
+ */
+static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data)
+{
+ struct msg_queue_entry *msg;
+ if (!(msg = ast_calloc(1, sizeof(*msg)))) {
+ return -1;
+ }
+ msg->expected = expect;
+ msg->response_to = response_to;
+ msg->data = data;
+
+ AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+ return 0;
+}
+
+/*!
+ * \brief Remove an item from the front of the queue.
+ * \param pvt a mbl_pvt structure
+ * \return a pointer to the removed item
+ */
+static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt)
+{
+ return AST_LIST_REMOVE_HEAD(&pvt->msg_queue, entry);
+}
+
+/*!
+ * \brief Remove an item from the front of the queue, and free it.
+ * \param pvt a mbl_pvt structure
+ */
+static void msg_queue_free_and_pop(struct mbl_pvt *pvt)
+{
+ struct msg_queue_entry *msg;
+ if ((msg = msg_queue_pop(pvt))) {
+ if (msg->data)
+ ast_free(msg->data);
+ ast_free(msg);
+ }
+}
+
+/*!
+ * \brief Remove all itmes from the queue and free them.
+ * \param pvt a mbl_pvt structure
+ */
+static void msg_queue_flush(struct mbl_pvt *pvt)
+{
+ struct msg_queue_entry *msg;
+ while ((msg = msg_queue_head(pvt)))
+ msg_queue_free_and_pop(pvt);
+}
+
+/*!
+ * \brief Get the head of a queue.
+ * \param pvt a mbl_pvt structure
+ * \return a pointer to the head of the given msg queue
+ */
+static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt)
+{
+ return AST_LIST_FIRST(&pvt->msg_queue);
+}
+
+
+
+/*
+
+ sdp helpers
+
+*/
+
+static int sdp_search(char *addr, int profile)
+{
+
+ sdp_session_t *session = 0;
+ bdaddr_t bdaddr;
+ uuid_t svc_uuid;
+ uint32_t range = 0x0000ffff;
+ sdp_list_t *response_list, *search_list, *attrid_list;
+ int status, port;
+ sdp_list_t *proto_list;
+ sdp_record_t *sdprec;
+
+ str2ba(addr, &bdaddr);
+ port = 0;
+ session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
+ if (!session) {
+ ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
+ return 0;
+ }
+
+ sdp_uuid32_create(&svc_uuid, profile);
+ search_list = sdp_list_append(0, &svc_uuid);
+ attrid_list = sdp_list_append(0, &range);
+ response_list = 0x00;
+ status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
+ if (status == 0) {
+ if (response_list) {
+ sdprec = (sdp_record_t *) response_list->data;
+ proto_list = 0x00;
+ if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
+ port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
+ sdp_list_free(proto_list, 0);
+ }
+ sdp_record_free(sdprec);
+ sdp_list_free(response_list, 0);
+ } else
+ ast_debug(1, "No responses returned for device %s.\n", addr);
+ } else
+ ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
+
+ sdp_list_free(search_list, 0);
+ sdp_list_free(attrid_list, 0);
+ sdp_close(session);
+
+ return port;
+
+}
+
+static sdp_session_t *sdp_register(void)
+{
+
+ uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
+ uint8_t rfcomm_channel = 1;
+ const char *service_name = "Asterisk PABX";
+ const char *service_dsc = "Asterisk PABX";
+ const char *service_prov = "Asterisk";
+
+ uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
+ sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
+ sdp_data_t *channel = 0;
+
+ int err = 0;
+ sdp_session_t *session = 0;
+
+ sdp_record_t *record = sdp_record_alloc();
+
+ sdp_uuid128_create(&svc_uuid, &service_uuid_int);
+ sdp_set_service_id(record, svc_uuid);
+
+ sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
+
+ svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
+ svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
+ sdp_set_service_classes(record, svc_uuid_list);
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root_list = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups( record, root_list );
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ l2cap_list = sdp_list_append(0, &l2cap_uuid);
+ proto_list = sdp_list_append(0, l2cap_list);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
+ rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
+ sdp_list_append(rfcomm_list, channel);
+ sdp_list_append(proto_list, rfcomm_list);
+
+ access_proto_list = sdp_list_append(0, proto_list);
+ sdp_set_access_protos(record, access_proto_list);
+
+ sdp_set_info_attr(record, service_name, service_prov, service_dsc);
+
+ if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
+ ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
+ else
+ err = sdp_record_register(session, record, 0);
+
+ sdp_data_free(channel);
+ sdp_list_free(rfcomm_list, 0);
+ sdp_list_free(root_list, 0);
+ sdp_list_free(access_proto_list, 0);
+ sdp_list_free(svc_uuid_list, 0);
+
+ return session;
+
+}
+
+/*
+
+ Thread routines
+
+*/
+
+/*!
+ * \brief Handle the BRSF response.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_brsf(struct mbl_pvt *pvt, char *buf)
+{
+ struct msg_queue_entry *entry;
+ if ((entry = msg_queue_head(pvt)) && entry->expected == AT_BRSF) {
+ if (hfp_parse_brsf(pvt->hfp, buf)) {
+ ast_debug(1, "[%s] error parsing BRSF\n", pvt->id);
+ goto e_return;
+ }
+
+ if (msg_queue_push(pvt, AT_OK, AT_BRSF)) {
+ ast_debug(1, "[%s] error handling BRSF\n", pvt->id);
+ goto e_return;
+ }
+
+ msg_queue_free_and_pop(pvt);
+ } else if (entry) {
+ ast_debug(1, "[%s] recieved unexpected AT message 'BRSF' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+ } else {
+ ast_debug(1, "[%s] recieved unexpected AT message 'BRSF'\n", pvt->id);
+ }
+
+ return 0;
+
+e_return:
+ msg_queue_free_and_pop(pvt);
+ return -1;
+}
+
+/*!
+ * \brief Handle the CIND response.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cind(struct mbl_pvt *pvt, char *buf)
+{
+ struct msg_queue_entry *entry;
+ if ((entry = msg_queue_head(pvt)) && entry->expected == AT_CIND) {
+ switch (entry->response_to) {
+ case AT_CIND_TEST:
+ if (hfp_parse_cind_test(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND_TEST)) {
+ ast_debug(1, "[%s] error performing CIND test\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ case AT_CIND:
+ if (hfp_parse_cind(pvt->hfp, buf) || msg_queue_push(pvt, AT_OK, AT_CIND)) {
+ ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ default:
+ ast_debug(1, "[%s] error getting CIND state\n", pvt->id);
+ goto e_return;
+ }
+ msg_queue_free_and_pop(pvt);
+ } else if (entry) {
+ ast_debug(1, "[%s] recieved unexpected AT message 'CIND' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+ } else {
+ ast_debug(1, "[%s] recieved unexpected AT message 'CIND'\n", pvt->id);
+ }
+
+ return 0;
+
+e_return:
+ msg_queue_free_and_pop(pvt);
+ return -1;
+}
+
+/*!
+ * \brief Handle OK AT messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_ok(struct mbl_pvt *pvt, char *buf)
+{
+ struct msg_queue_entry *entry;
+ if ((entry = msg_queue_head(pvt)) && entry->expected == AT_OK) {
+ switch (entry->response_to) {
+
+ /* initilization stuff */
+ case AT_BRSF:
+ ast_debug(1, "[%s] BSRF sent successfully\n", pvt->id);
+
+ /* If this is a blackberry do CMER now, otherwise
+ * continue with CIND as normal. */
+ if (pvt->blackberry) {
+ if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
+ ast_debug(1, "[%s] error sending CMER\n", pvt->id);
+ goto e_return;
+ }
+ } else {
+ if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
+ ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
+ goto e_return;
+ }
+ }
+ break;
+ case AT_CIND_TEST:
+ ast_debug(1, "[%s] CIND test sent successfully\n", pvt->id);
+
+ ast_debug(2, "[%s] call: %d\n", pvt->id, pvt->hfp->cind_map.call);
+ ast_debug(2, "[%s] callsetup: %d\n", pvt->id, pvt->hfp->cind_map.callsetup);
+
+ if (hfp_send_cind(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND)) {
+ ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ case AT_CIND:
+ ast_debug(1, "[%s] CIND sent successfully\n", pvt->id);
+
+ /* check if a call is active */
+ if (pvt->hfp->cind_state[pvt->hfp->cind_map.call]) {
+ ast_verb(3, "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
+ goto e_return;
+ }
+
+ /* If this is NOT a blackberry proceed with CMER,
+ * otherwise send CLIP. */
+ if (!pvt->blackberry) {
+ if (hfp_send_cmer(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMER)) {
+ ast_debug(1, "[%s] error sending CMER\n", pvt->id);
+ goto e_return;
+ }
+ } else {
+ if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
+ ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
+ goto e_return;
+ }
+ }
+ break;
+ case AT_CMER:
+ ast_debug(1, "[%s] CMER sent successfully\n", pvt->id);
+
+ /* If this is a blackberry proceed with the CIND test,
+ * otherwise send CLIP. */
+ if (pvt->blackberry) {
+ if (hfp_send_cind_test(pvt->hfp) || msg_queue_push(pvt, AT_CIND, AT_CIND_TEST)) {
+ ast_debug(1, "[%s] error sending CIND test\n", pvt->id);
+ goto e_return;
+ }
+ } else {
+ if (hfp_send_clip(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
+ ast_debug(1, "[%s] error enabling calling line notification\n", pvt->id);
+ goto e_return;
+ }
+ }
+ break;
+ case AT_CLIP:
+ ast_debug(1, "[%s] caling line indication enabled\n", pvt->id);
+ if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) {
+ ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id);
+ goto e_return;
+ }
+
+ pvt->timeout = -1;
+ pvt->hfp->initialized = 1;
+ ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id);
+
+ break;
+ case AT_VGS:
+ ast_debug(1, "[%s] volume level synchronization successful\n", pvt->id);
+
+ /* set the SMS operating mode to text mode */
+ if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
+ ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ case AT_CMGF:
+ ast_debug(1, "[%s] sms text mode enabled\n", pvt->id);
+ /* turn on SMS new message indication */
+ if (hfp_send_cnmi(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_CNMI)) {
+ ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ case AT_CNMI:
+ ast_debug(1, "[%s] sms new message indication enabled\n", pvt->id);
+ pvt->has_sms = 1;
+ break;
+ /* end initilization stuff */
+
+ case AT_A:
+ ast_debug(1, "[%s] answer sent successfully\n", pvt->id);
+ pvt->needchup = 1;
+ break;
+ case AT_D:
+ ast_debug(1, "[%s] dial sent successfully\n", pvt->id);
+ pvt->needchup = 1;
+ pvt->outgoing = 1;
+ mbl_queue_control(pvt, AST_CONTROL_PROGRESS);
+ break;
+ case AT_CHUP:
+ ast_debug(1, "[%s] successful hangup\n", pvt->id);
+ break;
+ case AT_CMGR:
+ ast_debug(1, "[%s] successfully read sms message\n", pvt->id);
+ pvt->incoming_sms = 0;
+ break;
+ case AT_CMGS:
+ ast_debug(1, "[%s] successfully sent sms message\n", pvt->id);
+ pvt->outgoing_sms = 0;
+ break;
+ case AT_VTS:
+ ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
+ break;
+ case AT_UNKNOWN:
+ default:
+ ast_debug(1, "[%s] recieved OK for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+ break;
+ }
+ msg_queue_free_and_pop(pvt);
+ } else if (entry) {
+ ast_debug(1, "[%s] recieved AT message 'OK' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+ } else {
+ ast_debug(1, "[%s] recieved unexpected AT message 'OK'\n", pvt->id);
+ }
+ return 0;
+
+e_return:
+ msg_queue_free_and_pop(pvt);
+ return -1;
+}
+
+/*!
+ * \brief Handle ERROR AT messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_error(struct mbl_pvt *pvt, char *buf)
+{
+ struct msg_queue_entry *entry;
+ if ((entry = msg_queue_head(pvt))
+ && (entry->expected == AT_OK
+ || entry->expected == AT_ERROR
+ || entry->expected == AT_CMS_ERROR
+ || entry->expected == AT_CMGR
+ || entry->expected == AT_SMS_PROMPT)) {
+ switch (entry->response_to) {
+
+ /* initilization stuff */
+ case AT_BRSF:
+ ast_debug(1, "[%s] error reading BSRF\n", pvt->id);
+ goto e_return;
+ case AT_CIND_TEST:
+ ast_debug(1, "[%s] error during CIND test\n", pvt->id);
+ goto e_return;
+ case AT_CIND:
+ ast_debug(1, "[%s] error requesting CIND state\n", pvt->id);
+ goto e_return;
+ case AT_CMER:
+ ast_debug(1, "[%s] error during CMER request\n", pvt->id);
+ goto e_return;
+ case AT_CLIP:
+ ast_debug(1, "[%s] error enabling calling line indication\n", pvt->id);
+ goto e_return;
+ case AT_VGS:
+ ast_debug(1, "[%s] volume level synchronization failed\n", pvt->id);
+
+ /* this is not a fatal error, let's continue with initilization */
+
+ /* set the SMS operating mode to text mode */
+ if (hfp_send_cmgf(pvt->hfp, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
+ ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
+ goto e_return;
+ }
+ break;
+ case AT_CMGF:
+ ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
+ ast_debug(1, "[%s] no SMS support\n", pvt->id);
+ break;
+ case AT_CNMI:
+ ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
+ ast_debug(1, "[%s] no SMS support\n", pvt->id);
+ break;
+ /* end initilization stuff */
+
+ case AT_A:
+ ast_debug(1, "[%s] answer failed\n", pvt->id);
+ mbl_queue_hangup(pvt);
+ break;
+ case AT_D:
+ ast_debug(1, "[%s] dial failed\n", pvt->id);
+ pvt->needchup = 0;
+ mbl_queue_control(pvt, AST_CONTROL_CONGESTION);
+ break;
+ case AT_CHUP:
+ ast_debug(1, "[%s] error sending hangup, disconnecting\n", pvt->id);
+ goto e_return;
+ case AT_CMGR:
+ ast_debug(1, "[%s] error reading sms message\n", pvt->id);
+ pvt->incoming_sms = 0;
+ break;
+ case AT_CMGS:
+ ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+ pvt->outgoing_sms = 0;
+ break;
+ case AT_VTS:
+ ast_debug(1, "[%s] error sending digit\n", pvt->id);
+ break;
+ case AT_UNKNOWN:
+ default:
+ ast_debug(1, "[%s] recieved ERROR for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+ break;
+ }
+ msg_queue_free_and_pop(pvt);
+ } else if (entry) {
+ ast_debug(1, "[%s] recieved AT message 'ERROR' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+ } else {
+ ast_debug(1, "[%s] recieved unexpected AT message 'ERROR'\n", pvt->id);
+ }
+
+ return 0;
+
+e_return:
+ msg_queue_free_and_pop(pvt);
+ return -1;
+}
+
+/*!
+ * \brief Handle AT+CIEV messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_ciev(struct mbl_pvt *pvt, char *buf)
+{
+ int i;
+ switch (hfp_parse_ciev(pvt->hfp, buf, &i)) {
+ case HFP_CIND_CALL:
+ switch (i) {
+ case HFP_CIND_CALL_NONE:
+ ast_debug(1, "[%s] line disconnected\n", pvt->id);
+ if (pvt->owner) {
+ ast_debug(1, "[%s] hanging up owner\n", pvt->id);
+ if (mbl_queue_hangup(pvt)) {
+ ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
+ return -1;
+ }
+ }
+ pvt->needchup = 0;
+ pvt->needcallerid = 0;
+ pvt->incoming = 0;
+ pvt->outgoing = 0;
+ break;
+ case HFP_CIND_CALL_ACTIVE:
+ if (pvt->outgoing) {
+ ast_debug(1, "[%s] remote end answered\n", pvt->id);
+ mbl_queue_control(pvt, AST_CONTROL_ANSWER);
+ } else if (pvt->incoming && pvt->answered) {
+ ast_setstate(pvt->owner, AST_STATE_UP);
+ } else if (pvt->incoming) {
+ /* user answered from handset, disconnecting */
+ ast_verb(3, "[%s] user answered bluetooth device from handset, disconnecting\n", pvt->id);
+ mbl_queue_hangup(pvt);
+ return -1;
+ }
+ break;
+ }
+ break;
+
+ case HFP_CIND_CALLSETUP:
+ switch (i) {
+ case HFP_CIND_CALLSETUP_NONE:
+ if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) {
+ if (pvt->owner) {
+ if (mbl_queue_hangup(pvt)) {
+ ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
+ return -1;
+ }
+ }
+ pvt->needchup = 0;
+ pvt->needcallerid = 0;
+ pvt->incoming = 0;
+ pvt->outgoing = 0;
+ }
+ break;
+ case HFP_CIND_CALLSETUP_INCOMING:
+ ast_debug(1, "[%s] incoming call, waiting for caller id\n", pvt->id);
+ pvt->needcallerid = 1;
+ pvt->incoming = 1;
+ break;
+ case HFP_CIND_CALLSETUP_OUTGOING:
+ if (pvt->outgoing) {
+ ast_debug(1, "[%s] outgoing call\n", pvt->id);
+ } else {
+ ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id);
+ return -1;
+ }
+ break;
+ case HFP_CIND_CALLSETUP_ALERTING:
+ if (pvt->outgoing) {
+ ast_debug(1, "[%s] remote alerting\n", pvt->id);
+ mbl_queue_control(pvt, AST_CONTROL_RINGING);
+ }
+ break;
+ }
+ break;
+ case HFP_CIND_NONE:
+ ast_debug(1, "[%s] error parsing CIND: %s\n", pvt->id, buf);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * \brief Handle AT+CLIP messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_clip(struct mbl_pvt *pvt, char *buf)
+{
+ char *clip;
+ struct msg_queue_entry *msg;
+ struct ast_channel *chan;
+
+ if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CLIP) {
+ msg_queue_free_and_pop(pvt);
+
+ pvt->needcallerid = 0;
+ if (!(clip = hfp_parse_clip(pvt->hfp, buf))) {
+ ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
+ }
+
+ if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL))) {
+ ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
+ hfp_send_chup(pvt->hfp);
+ msg_queue_push(pvt, AT_OK, AT_CHUP);
+ return -1;
+ }
+
+ /* from this point on, we need to send a chup in the event of a
+ * hangup */
+ pvt->needchup = 1;
+
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
+ mbl_ast_hangup(pvt);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Handle RING messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_ring(struct mbl_pvt *pvt, char *buf)
+{
+ if (pvt->needcallerid) {
+ ast_debug(1, "[%s] got ring while waiting for caller id\n", pvt->id);
+ return msg_queue_push(pvt, AT_CLIP, AT_UNKNOWN);
+ } else {
+ return 0;
+ }
+}
+
+/*!
+ * \brief Handle AT+CMTI messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cmti(struct mbl_pvt *pvt, char *buf)
+{
+ int index = hfp_parse_cmti(pvt->hfp, buf);
+ if (index > 0) {
+ ast_debug(1, "[%s] incoming sms message\n", pvt->id);
+
+ if (hfp_send_cmgr(pvt->hfp, index)
+ || msg_queue_push(pvt, AT_CMGR, AT_CMGR)) {
+ ast_debug(1, "[%s] error sending CMGR to retrieve SMS message\n", pvt->id);
+ return -1;
+ }
+
+ pvt->incoming_sms = 1;
+ return 0;
+ } else {
+ ast_debug(1, "[%s] error parsing incoming sms message alert, disconnecting\n", pvt->id);
+ return -1;
+ }
+}
+
+/*!
+ * \brief Handle AT+CMGR messages.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf)
+{
+ char *from_number = NULL, *text = NULL;
+ struct ast_channel *chan;
+ struct msg_queue_entry *msg;
+
+ if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CMGR) {
+ msg_queue_free_and_pop(pvt);
+
+ if (hfp_parse_cmgr(pvt->hfp, buf, &from_number, &text)
+ || msg_queue_push(pvt, AT_OK, AT_CMGR)) {
+
+ ast_debug(1, "[%s] error parsing sms message, disconnecting\n", pvt->id);
+ return -1;
+ }
+
+ /* XXX this channel probably does not need to be associated with this pvt */
+ if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL))) {
+ ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
+ return -1;
+ }
+
+ strcpy(chan->exten, "sms");
+ pbx_builtin_setvar_helper(chan, "SMSSRC", from_number);
+ pbx_builtin_setvar_helper(chan, "SMSTXT", text);
+
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming sms\n", pvt->id);
+ mbl_ast_hangup(pvt);
+ }
+ } else {
+ ast_debug(1, "[%s] got unexpected +CMGR message, ignoring\n", pvt->id);
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Send an SMS message from the queue.
+ * \param pvt a mbl_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf)
+{
+ struct msg_queue_entry *msg;
+ if (!(msg = msg_queue_head(pvt))) {
+ ast_debug(1, "[%s] error, got sms prompt with no pending sms messages\n", pvt->id);
+ return 0;
+ }
+
+ if (msg->expected != AT_SMS_PROMPT) {
+ ast_debug(1, "[%s] error, got sms prompt but no pending sms messages\n", pvt->id);
+ return 0;
+ }
+
+ if (hfp_send_sms_text(pvt->hfp, msg->data)
+ || msg_queue_push(pvt, AT_OK, AT_CMGS)) {
+ msg_queue_free_and_pop(pvt);
+ ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+ return 0;
+ }
+
+ msg_queue_free_and_pop(pvt);
+ return 0;
+}
+
+
+static void *do_monitor_phone(void *data)
+{
+ struct mbl_pvt *pvt = (struct mbl_pvt *)data;
+ struct hfp_pvt *hfp = pvt->hfp;
+ char buf[256];
+ int t;
+ at_message_t at_msg;
+ struct msg_queue_entry *entry;
+
+ /* Note: At one point the initilization procedure was neatly contained
+ * in the hfp_init() function, but that initilization method did not
+ * work with non standard devices. As a result, the initilization
+ * procedure is not spread throughout the event handling loop.
+ */
+
+ /* start initilization with the BRSF request */
+ ast_mutex_lock(&pvt->lock);
+ pvt->timeout = 10000;
+ if (hfp_send_brsf(hfp, &hfp_our_brsf) || msg_queue_push(pvt, AT_BRSF, AT_BRSF)) {
+ ast_debug(1, "[%s] error sending BRSF\n", hfp->owner->id);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ while (!check_unloading()) {
+ ast_mutex_lock(&pvt->lock);
+ t = pvt->timeout;
+ ast_mutex_unlock(&pvt->lock);
+
+ if (!rfcomm_wait(pvt->rfcomm_socket, &t)) {
+ ast_debug(1, "[%s] timeout waiting for rfcomm data, disconnecting\n", pvt->id);
+ ast_mutex_lock(&pvt->lock);
+ if (!hfp->initialized) {
+ if ((entry = msg_queue_head(pvt))) {
+ switch (entry->response_to) {
+ case AT_CIND_TEST:
+ if (pvt->blackberry)
+ ast_debug(1, "[%s] timeout during CIND test\n", hfp->owner->id);
+ else
+ ast_debug(1, "[%s] timeout during CIND test, try setting 'blackberry=yes'\n", hfp->owner->id);
+ break;
+ case AT_CMER:
+ if (pvt->blackberry)
+ ast_debug(1, "[%s] timeout after sending CMER, try setting 'blackberry=no'\n", hfp->owner->id);
+ else
+ ast_debug(1, "[%s] timeout after sending CMER\n", hfp->owner->id);
+ break;
+ default:
+ ast_debug(1, "[%s] timeout while waiting for %s in response to %s\n", pvt->id, at_msg2str(entry->expected), at_msg2str(entry->response_to));
+ break;
+ }
+ }
+ }
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+
+ if ((at_msg = at_read_full(hfp->rsock, buf, sizeof(buf))) < 0) {
+ /* XXX gnu specific strerror_r is assummed here, this
+ * is not really safe. See the strerror(3) man page
+ * for more info. */
+ ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+ break;
+ }
+
+ ast_debug(1, "[%s] %s\n", pvt->id, buf);
+
+ switch (at_msg) {
+ case AT_BRSF:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_brsf(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CIND:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_cind(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_OK:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_ok(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CMS_ERROR:
+ case AT_ERROR:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_error(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_RING:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_ring(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CIEV:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_ciev(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CLIP:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_clip(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CMTI:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_cmti(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_CMGR:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_response_cmgr(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_SMS_PROMPT:
+ ast_mutex_lock(&pvt->lock);
+ if (handle_sms_prompt(pvt, buf)) {
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ case AT_UNKNOWN:
+ ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf);
+ break;
+ case AT_PARSE_ERROR:
+ ast_debug(1, "[%s] error parsing message\n", pvt->id);
+ goto e_cleanup;
+ case AT_READ_ERROR:
+ ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+ goto e_cleanup;
+ default:
+ break;
+ }
+ }
+
+e_cleanup:
+
+ if (!hfp->initialized)
+ ast_verb(3, "Error initializing Bluetooth device %s.\n", pvt->id);
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->owner) {
+ ast_debug(1, "[%s] device disconnected, hanging up owner\n", pvt->id);
+ pvt->needchup = 0;
+ mbl_queue_hangup(pvt);
+ }
+
+ close(pvt->rfcomm_socket);
+ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+
+ msg_queue_flush(pvt);
+
+ pvt->connected = 0;
+ hfp->initialized = 0;
+
+ pvt->adapter->inuse = 0;
+ ast_mutex_unlock(&pvt->lock);
+
+ ast_verb(3, "Bluetooth Device %s has disconnected.\n", pvt->id);
+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
+
+ return NULL;
+}
+
+static int headset_send_ring(const void *data)
+{
+ struct mbl_pvt *pvt = (struct mbl_pvt *) data;
+ ast_mutex_lock(&pvt->lock);
+ if (!pvt->needring) {
+ ast_mutex_unlock(&pvt->lock);
+ return 0;
+ }
+ ast_mutex_unlock(&pvt->lock);
+
+ if (hsp_send_ring(pvt->rfcomm_socket)) {
+ ast_debug(1, "[%s] error sending RING\n", pvt->id);
+ return 0;
+ }
+ return 1;
+}
+
+static void *do_monitor_headset(void *data)
+{
+
+ struct mbl_pvt *pvt = (struct mbl_pvt *)data;
+ char buf[256];
+ int t;
+ at_message_t at_msg;
+ struct ast_channel *chan = NULL;
+
+ ast_verb(3, "Bluetooth Device %s initialised and ready.\n", pvt->id);
+
+ while (!check_unloading()) {
+
+ t = ast_sched_wait(pvt->sched);
+ if (t == -1) {
+ t = 6000;
+ }
+
+ ast_sched_runq(pvt->sched);
+
+ if (rfcomm_wait(pvt->rfcomm_socket, &t) == 0)
+ continue;
+
+ if ((at_msg = at_read_full(pvt->rfcomm_socket, buf, sizeof(buf))) < 0) {
+ if (strerror_r(errno, buf, sizeof(buf)))
+ ast_debug(1, "[%s] error reading from device\n", pvt->id);
+ else
+ ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, buf, errno);
+
+ goto e_cleanup;
+ }
+ ast_debug(1, "[%s] %s\n", pvt->id, buf);
+
+ switch (at_msg) {
+ case AT_VGS:
+ case AT_VGM:
+ /* XXX volume change requested, we will just
+ * pretend to do something with it */
+ if (hsp_send_ok(pvt->rfcomm_socket)) {
+ ast_debug(1, "[%s] error sending AT message 'OK'\n", pvt->id);
+ goto e_cleanup;
+ }
+ break;
+ case AT_CKPD:
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->outgoing) {
+ pvt->needring = 0;
+ hsp_send_ok(pvt->rfcomm_socket);
+ if (pvt->answered) {
+ /* we have an answered call up to the
+ * HS, he wants to hangup */
+ mbl_queue_hangup(pvt);
+ } else {
+ /* we have an outgoing call to the HS,
+ * he wants to answer */
+ if ((pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr)) == -1) {
+ ast_log(LOG_ERROR, "[%s] unable to create audio connection\n", pvt->id);
+ mbl_queue_hangup(pvt);
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+
+ ast_channel_set_fd(pvt->owner, 0, pvt->sco_socket);
+
+ mbl_queue_control(pvt, AST_CONTROL_ANSWER);
+ pvt->answered = 1;
+
+ if (hsp_send_vgs(pvt->rfcomm_socket, 13) || hsp_send_vgm(pvt->rfcomm_socket, 13)) {
+ ast_debug(1, "[%s] error sending VGS/VGM\n", pvt->id);
+ mbl_queue_hangup(pvt);
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ }
+ } else if (pvt->incoming) {
+ /* we have an incoming call from the
+ * HS, he wants to hang up */
+ mbl_queue_hangup(pvt);
+ } else {
+ /* no call is up, HS wants to dial */
+ hsp_send_ok(pvt->rfcomm_socket);
+
+ if ((pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr)) == -1) {
+ ast_log(LOG_ERROR, "[%s] unable to create audio connection\n", pvt->id);
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+
+ pvt->incoming = 1;
+
+ if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL))) {
+ ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+
+ ast_channel_set_fd(chan, 0, pvt->sco_socket);
+
+ ast_copy_string(chan->exten, "s", AST_MAX_EXTENSION);
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
+ ast_hangup(chan);
+ ast_mutex_unlock(&pvt->lock);
+ goto e_cleanup;
+ }
+ }
+ ast_mutex_unlock(&pvt->lock);
+ break;
+ default:
+ ast_debug(1, "[%s] received unknown AT command: %s (%s)\n", pvt->id, buf, at_msg2str(at_msg));
+ if (hsp_send_error(pvt->rfcomm_socket)) {
+ ast_debug(1, "[%s] error sending AT message 'ERROR'\n", pvt->id);
+ goto e_cleanup;
+ }
+ break;
+ }
+ }
+
+e_cleanup:
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->owner) {
+ ast_debug(1, "[%s] device disconnected, hanging up owner\n", pvt->id);
+ mbl_queue_hangup(pvt);
+ }
+
+
+ close(pvt->rfcomm_socket);
+ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+
+ pvt->connected = 0;
+
+ pvt->needring = 0;
+ pvt->outgoing = 0;
+ pvt->incoming = 0;
+
+ pvt->adapter->inuse = 0;
+ ast_mutex_unlock(&pvt->lock);
+
+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
+ ast_verb(3, "Bluetooth Device %s has disconnected\n", pvt->id);
+
+ return NULL;
+
+}
+
+static int start_monitor(struct mbl_pvt *pvt)
+{
+
+ if (pvt->type == MBL_TYPE_PHONE) {
+ pvt->hfp->rsock = pvt->rfcomm_socket;
+
+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+ return 0;
+ }
+ } else {
+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) {
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+ return 0;
+ }
+ }
+
+ return 1;
+
+}
+
+static void *do_discovery(void *data)
+{
+
+ struct adapter_pvt *adapter;
+ struct mbl_pvt *pvt;
+
+ while (!check_unloading()) {
+ AST_RWLIST_RDLOCK(&adapters);
+ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
+ if (!adapter->inuse) {
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ ast_mutex_lock(&pvt->lock);
+ if (!adapter->inuse && !pvt->connected && !strcmp(adapter->id, pvt->adapter->id)) {
+ if ((pvt->rfcomm_socket = rfcomm_connect(adapter->addr, pvt->addr, pvt->rfcomm_port)) > -1) {
+ if (start_monitor(pvt)) {
+ pvt->connected = 1;
+ adapter->inuse = 1;
+ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id);
+ ast_verb(3, "Bluetooth Device %s has connected, initilizing...\n", pvt->id);
+ }
+ }
+ }
+ ast_mutex_unlock(&pvt->lock);
+ }
+ AST_RWLIST_UNLOCK(&devices);
+ }
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+
+
+ /* Go to sleep (only if we are not unloading) */
+ if (!check_unloading())
+ sleep(discovery_interval);
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Service new and existing SCO connections.
+ * This thread accepts new sco connections and handles audio data. There is
+ * one do_sco_listen thread for each adapter.
+ */
+static void *do_sco_listen(void *data)
+{
+ struct adapter_pvt *adapter = (struct adapter_pvt *) data;
+ int res;
+
+ while (!check_unloading()) {
+ /* check for new sco connections */
+ if ((res = ast_io_wait(adapter->accept_io, 0)) == -1) {
+ /* handle errors */
+ ast_log(LOG_ERROR, "ast_io_wait() failed for adapter %s\n", adapter->id);
+ break;
+ }
+
+ /* handle audio data */
+ if ((res = ast_io_wait(adapter->io, 1)) == -1) {
+ ast_log(LOG_ERROR, "ast_io_wait() failed for audio on adapter %s\n", adapter->id);
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+
+ Module
+
+*/
+
+/*!
+ * \brief Load an adapter from the configuration file.
+ * \param cfg the config to load the adapter from
+ * \param cat the adapter to load
+ *
+ * This function loads the given adapter and starts the sco listener thread for
+ * that adapter.
+ *
+ * \return NULL on error, a pointer to the adapter that was loaded on success
+ */
+static struct adapter_pvt *mbl_load_adapter(struct ast_config *cfg, const char *cat)
+{
+ const char *id, *address;
+ struct adapter_pvt *adapter;
+ struct ast_variable *v;
+ struct hci_dev_req dr;
+ uint16_t vs;
+
+ id = ast_variable_retrieve(cfg, cat, "id");
+ address = ast_variable_retrieve(cfg, cat, "address");
+
+ if (ast_strlen_zero(id) || ast_strlen_zero(address)) {
+ ast_log(LOG_ERROR, "Skipping adapter. Missing id or address settings.\n");
+ goto e_return;
+ }
+
+ ast_debug(1, "Reading configuration for adapter %s %s.\n", id, address);
+
+ if (!(adapter = ast_calloc(1, sizeof(*adapter)))) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Error allocating memory.\n", id);
+ goto e_return;
+ }
+
+ ast_copy_string(adapter->id, id, sizeof(adapter->id));
+ str2ba(address, &adapter->addr);
+
+ /* attempt to connect to the adapter */
+ adapter->dev_id = hci_devid(address);
+ adapter->hci_socket = hci_open_dev(adapter->dev_id);
+ if (adapter->dev_id < 0 || adapter->hci_socket < 0) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Unable to communicate with adapter.\n", adapter->id);
+ goto e_free_adapter;
+ }
+
+ /* check voice setting */
+ hci_read_voice_setting(adapter->hci_socket, &vs, 1000);
+ vs = htobs(vs);
+ if (vs != 0x0060) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Voice setting must be 0x0060 - see 'man hciconfig' for details.\n", adapter->id);
+ goto e_hci_close_dev;
+ }
+
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "forcemaster")) {
+ if (ast_true(v->value)) {
+ dr.dev_id = adapter->dev_id;
+ if (hci_strtolm("master", &dr.dev_opt)) {
+ if (ioctl(adapter->hci_socket, HCISETLINKMODE, (unsigned long) &dr) < 0) {
+ ast_log(LOG_WARNING, "Unable to set adapter %s link mode to MASTER. Ignoring 'forcemaster' option.\n", adapter->id);
+ }
+ }
+ }
+ } else if (!strcasecmp(v->name, "alignmentdetection")) {
+ adapter->alignment_detection = ast_true(v->value);
+ }
+ }
+
+ /* create io contexts */
+ if (!(adapter->accept_io = io_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create I/O context for audio connection listener\n");
+ goto e_hci_close_dev;
+ }
+
+ if (!(adapter->io = io_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create I/O context for audio connections\n");
+ goto e_destroy_accept_io;
+ }
+
+ /* bind the sco listener socket */
+ if (sco_bind(adapter) < 0) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Error binding audio connection listerner socket.\n", adapter->id);
+ goto e_destroy_io;
+ }
+
+ /* add the socket to the io context */
+ if (!(adapter->sco_id = ast_io_add(adapter->accept_io, adapter->sco_socket, sco_accept, AST_IO_IN, adapter))) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Error adding listener socket to I/O context.\n", adapter->id);
+ goto e_close_sco;
+ }
+
+ /* start the sco listener for this adapter */
+ if (ast_pthread_create_background(&adapter->sco_listener_thread, NULL, do_sco_listen, adapter)) {
+ ast_log(LOG_ERROR, "Skipping adapter %s. Error creating audio connection listerner thread.\n", adapter->id);
+ goto e_remove_sco;
+ }
+
+ /* add the adapter to our global list */
+ AST_RWLIST_WRLOCK(&adapters);
+ AST_RWLIST_INSERT_HEAD(&adapters, adapter, entry);
+ AST_RWLIST_UNLOCK(&adapters);
+ ast_debug(1, "Loaded adapter %s %s.\n", adapter->id, address);
+
+ return adapter;
+
+e_remove_sco:
+ ast_io_remove(adapter->accept_io, adapter->sco_id);
+e_close_sco:
+ close(adapter->sco_socket);
+e_destroy_io:
+ io_context_destroy(adapter->io);
+e_destroy_accept_io:
+ io_context_destroy(adapter->accept_io);
+e_hci_close_dev:
+ hci_close_dev(adapter->hci_socket);
+e_free_adapter:
+ ast_free(adapter);
+e_return:
+ return NULL;
+}
+
+/*!
+ * \brief Load a device from the configuration file.
+ * \param cfg the config to load the device from
+ * \param cat the device to load
+ * \return NULL on error, a pointer to the device that was loaded on success
+ */
+static struct mbl_pvt *mbl_load_device(struct ast_config *cfg, const char *cat)
+{
+ struct mbl_pvt *pvt;
+ struct adapter_pvt *adapter;
+ struct ast_variable *v;
+ const char *address, *adapter_str, *port;
+ ast_debug(1, "Reading configuration for device %s.\n", cat);
+
+ adapter_str = ast_variable_retrieve(cfg, cat, "adapter");
+ if(ast_strlen_zero(adapter_str)) {
+ ast_log(LOG_ERROR, "Skipping device %s. No adapter specified.\n", cat);
+ goto e_return;
+ }
+
+ /* find the adapter */
+ AST_RWLIST_RDLOCK(&adapters);
+ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
+ if (!strcmp(adapter->id, adapter_str))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+ if (!adapter) {
+ ast_log(LOG_ERROR, "Skiping device %s. Unknown adapter '%s' specified.\n", cat, adapter_str);
+ goto e_return;
+ }
+
+ address = ast_variable_retrieve(cfg, cat, "address");
+ port = ast_variable_retrieve(cfg, cat, "port");
+ if (ast_strlen_zero(port) || ast_strlen_zero(address)) {
+ ast_log(LOG_ERROR, "Skipping device %s. Missing required port or address setting.\n", cat);
+ goto e_return;
+ }
+
+ /* create and initialize our pvt structure */
+ if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
+ ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", cat);
+ goto e_return;
+ }
+
+ ast_mutex_init(&pvt->lock);
+ AST_LIST_HEAD_INIT_NOLOCK(&pvt->msg_queue);
+
+ /* set some defaults */
+
+ pvt->type = MBL_TYPE_PHONE;
+ ast_copy_string(pvt->context, "default", sizeof(pvt->context));
+
+ /* populate the pvt structure */
+ pvt->adapter = adapter;
+ ast_copy_string(pvt->id, cat, sizeof(pvt->id));
+ str2ba(address, &pvt->addr);
+ pvt->timeout = -1;
+ pvt->rfcomm_socket = -1;
+ pvt->rfcomm_port = atoi(port);
+ pvt->sco_socket = -1;
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+ pvt->ring_sched_id = -1;
+
+ /* setup the smoother */
+ if (!(pvt->smoother = ast_smoother_new(DEVICE_FRAME_SIZE))) {
+ ast_log(LOG_ERROR, "Skipping device %s. Error setting up frame smoother.\n", cat);
+ goto e_free_pvt;
+ }
+
+ /* setup the dsp */
+ if (!(pvt->dsp = ast_dsp_new())) {
+ ast_log(LOG_ERROR, "Skipping device %s. Error setting up dsp for dtmf detection.\n", cat);
+ goto e_free_smoother;
+ }
+
+ /* setup the scheduler */
+ if (!(pvt->sched = sched_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create scheduler context for headset device\n");
+ goto e_free_dsp;
+ }
+
+ ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DIGIT_DETECT);
+ ast_dsp_set_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+
+ for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+ if (!strcasecmp(v->name, "type")) {
+ if (!strcasecmp(v->value, "headset"))
+ pvt->type = MBL_TYPE_HEADSET;
+ else
+ pvt->type = MBL_TYPE_PHONE;
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(pvt->context, v->value, sizeof(pvt->context));
+ } else if (!strcasecmp(v->name, "group")) {
+ /* group is set to 0 if invalid */
+ pvt->group = atoi(v->value);
+ } else if (!strcasecmp(v->name, "nocallsetup")) {
+ pvt->no_callsetup = ast_true(v->value);
+
+ if (pvt->no_callsetup)
+ ast_debug(1, "Setting nocallsetup mode for device %s.\n", pvt->id);
+ } else if (!strcasecmp(v->name, "blackberry")) {
+ pvt->blackberry = ast_true(v->value);
+ }
+ }
+
+ if (pvt->type == MBL_TYPE_PHONE) {
+ if (!(pvt->hfp = ast_calloc(1, sizeof(*pvt->hfp)))) {
+ ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", pvt->id);
+ goto e_free_sched;
+ }
+
+ pvt->hfp->owner = pvt;
+ pvt->hfp->rport = pvt->rfcomm_port;
+ pvt->hfp->nocallsetup = pvt->no_callsetup;
+ }
+
+ AST_RWLIST_WRLOCK(&devices);
+ AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
+ AST_RWLIST_UNLOCK(&devices);
+ ast_debug(1, "Loaded device %s.\n", pvt->id);
+
+ return pvt;
+
+e_free_sched:
+ sched_context_destroy(pvt->sched);
+e_free_dsp:
+ ast_dsp_free(pvt->dsp);
+e_free_smoother:
+ ast_smoother_free(pvt->smoother);
+e_free_pvt:
+ ast_free(pvt);
+e_return:
+ return NULL;
+}
+
+static int mbl_load_config(void)
+{
+ struct ast_config *cfg;
+ const char *cat;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(MBL_CONFIG, config_flags);
+ if (!cfg)
+ return -1;
+
+ /* parse [general] section */
+ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
+ if (!strcasecmp(v->name, "interval")) {
+ if (!sscanf(v->value, "%d", &discovery_interval)) {
+ ast_log(LOG_NOTICE, "error parsing 'interval' in general section, using default value\n");
+ }
+ }
+ }
+
+ /* load adapters */
+ for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
+ if (!strcasecmp(cat, "adapter")) {
+ mbl_load_adapter(cfg, cat);
+ }
+ }
+
+ if (AST_RWLIST_EMPTY(&adapters)) {
+ ast_log(LOG_ERROR,
+ "***********************************************************************\n"
+ "No adapters could be loaded from the configuration file.\n"
+ "Please review mobile.conf. See sample for details.\n"
+ "***********************************************************************\n"
+ );
+ ast_config_destroy(cfg);
+ return -1;
+ }
+
+ /* now load devices */
+ for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
+ if (strcasecmp(cat, "general") && strcasecmp(cat, "adapter")) {
+ mbl_load_device(cfg, cat);
+ }
+ }
+
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+/*!
+ * \brief Check if the module is unloading.
+ * \retval 0 not unloading
+ * \retval 1 unloading
+ */
+static inline int check_unloading()
+{
+ int res;
+ ast_mutex_lock(&unload_mutex);
+ res = unloading_flag;
+ ast_mutex_unlock(&unload_mutex);
+
+ return res;
+}
+
+/*!
+ * \brief Set the unloading flag.
+ */
+static inline void set_unloading()
+{
+ ast_mutex_lock(&unload_mutex);
+ unloading_flag = 1;
+ ast_mutex_unlock(&unload_mutex);
+}
+
+static int unload_module(void)
+{
+ struct mbl_pvt *pvt;
+ struct adapter_pvt *adapter;
+
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&mbl_tech);
+
+ /* Unregister the CLI & APP */
+ ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
+ ast_unregister_application(app_mblstatus);
+ ast_unregister_application(app_mblsendsms);
+
+ /* signal everyone we are unloading */
+ set_unloading();
+
+ /* Kill the discovery thread */
+ if (discovery_thread != AST_PTHREADT_NULL) {
+ pthread_kill(discovery_thread, SIGURG);
+ pthread_join(discovery_thread, NULL);
+ }
+
+ /* stop the sco listener threads */
+ AST_RWLIST_WRLOCK(&adapters);
+ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
+ pthread_kill(adapter->sco_listener_thread, SIGURG);
+ pthread_join(adapter->sco_listener_thread, NULL);
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+
+ /* Destroy the device list */
+ AST_RWLIST_WRLOCK(&devices);
+ while ((pvt = AST_RWLIST_REMOVE_HEAD(&devices, entry))) {
+ if (pvt->monitor_thread != AST_PTHREADT_NULL) {
+ pthread_kill(pvt->monitor_thread, SIGURG);
+ pthread_join(pvt->monitor_thread, NULL);
+ }
+
+ close(pvt->sco_socket);
+ close(pvt->rfcomm_socket);
+
+ msg_queue_flush(pvt);
+
+ if (pvt->hfp) {
+ ast_free(pvt->hfp);
+ }
+
+ ast_smoother_free(pvt->smoother);
+ ast_dsp_free(pvt->dsp);
+ sched_context_destroy(pvt->sched);
+ ast_free(pvt);
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ /* Destroy the adapter list */
+ AST_RWLIST_WRLOCK(&adapters);
+ while ((adapter = AST_RWLIST_REMOVE_HEAD(&adapters, entry))) {
+ close(adapter->sco_socket);
+ io_context_destroy(adapter->io);
+ io_context_destroy(adapter->accept_io);
+ hci_close_dev(adapter->hci_socket);
+ ast_free(adapter);
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+
+ if (sdp_session)
+ sdp_close(sdp_session);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+
+ int dev_id, s;
+
+ /* Check if we have Bluetooth, no point loading otherwise... */
+ dev_id = hci_get_route(NULL);
+ s = hci_open_dev(dev_id);
+ if (dev_id < 0 || s < 0) {
+ ast_log(LOG_ERROR, "No Bluetooth devices found. Not loading module.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ hci_close_dev(s);
+
+ if (mbl_load_config()) {
+ ast_log(LOG_ERROR, "Errors reading config file %s. Not loading module.\n", MBL_CONFIG);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ sdp_session = sdp_register();
+
+ /* Spin the discovery thread */
+ if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
+ goto e_cleanup;
+ }
+
+ /* register our channel type */
+ if (ast_channel_register(&mbl_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile");
+ goto e_cleanup;
+ }
+
+ ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
+ ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc);
+ ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc);
+
+ return AST_MODULE_LOAD_SUCCESS;
+
+e_cleanup:
+ if (sdp_session)
+ sdp_close(sdp_session);
+
+ return AST_MODULE_LOAD_FAILURE;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/addons/chan_ooh323.c b/addons/chan_ooh323.c
new file mode 100644
index 000000000..70c0f0209
--- /dev/null
+++ b/addons/chan_ooh323.c
@@ -0,0 +1,3162 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+
+#include "chan_ooh323.h"
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+/* Defaults */
+#define DEFAULT_CONTEXT "default"
+#define DEFAULT_H323ID "Asterisk PBX"
+#define DEFAULT_LOGFILE "/var/log/asterisk/h323_log"
+#define DEFAULT_H323ACCNT "ast_h323"
+
+/* Flags */
+#define H323_SILENCESUPPRESSION (1<<0)
+#define H323_GKROUTED (1<<1)
+#define H323_TUNNELING (1<<2)
+#define H323_FASTSTART (1<<3)
+#define H323_OUTGOING (1<<4)
+#define H323_ALREADYGONE (1<<5)
+#define H323_NEEDDESTROY (1<<6)
+#define H323_DISABLEGK (1<<7)
+
+/* Channel description */
+static const char type[] = "OOH323";
+static const char tdesc[] = "Objective Systems H323 Channel Driver";
+static const char config[] = "ooh323.conf";
+
+
+/* Channel Definition */
+static struct ast_channel *ooh323_request(const char *type, int format,
+ void *data, int *cause);
+static int ooh323_digit_begin(struct ast_channel *ast, char digit);
+static int ooh323_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static int ooh323_call(struct ast_channel *ast, char *dest, int timeout);
+static int ooh323_hangup(struct ast_channel *ast);
+static int ooh323_answer(struct ast_channel *ast);
+static struct ast_frame *ooh323_read(struct ast_channel *ast);
+static int ooh323_write(struct ast_channel *ast, struct ast_frame *f);
+static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+static enum ast_rtp_get_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static enum ast_rtp_get_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp,
+ struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active);
+
+static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
+
+#if 0
+static void ast_ooh323c_exit();
+#endif
+
+static const struct ast_channel_tech ooh323_tech = {
+ .type = type,
+ .description = tdesc,
+ .capabilities = -1,
+ .requester = ooh323_request,
+ .send_digit_begin = ooh323_digit_begin,
+ .send_digit_end = ooh323_digit_end,
+ .call = ooh323_call,
+ .hangup = ooh323_hangup,
+ .answer = ooh323_answer,
+ .read = ooh323_read,
+ .write = ooh323_write,
+ .exception = ooh323_read,
+ .indicate = ooh323_indicate,
+ .fixup = ooh323_fixup,
+ .send_html = 0,
+ .bridge = ast_rtp_bridge,
+};
+
+static struct ast_rtp_protocol ooh323_rtp = {
+ .type = type,
+ .get_rtp_info = ooh323_get_rtp_peer,
+ .get_vrtp_info = ooh323_get_vrtp_peer,
+ .set_rtp_peer = ooh323_set_rtp_peer
+};
+
+/* H.323 channel private structure */
+static struct ooh323_pvt {
+ ast_mutex_t lock; /* Channel private lock */
+ struct ast_rtp *rtp;
+ struct ast_rtp *vrtp; /* Placeholder for now */
+ struct ast_channel *owner; /* Master Channel */
+ time_t lastrtptx;
+ time_t lastrtprx;
+ unsigned int flags;
+ unsigned int call_reference;
+ char *callToken;
+ char *username;
+ char *host;
+ char *callerid_name;
+ char *callerid_num;
+ char caller_h323id[AST_MAX_EXTENSION];
+ char caller_dialedDigits[AST_MAX_EXTENSION];
+ char caller_email[AST_MAX_EXTENSION];
+ char caller_url[256];
+ char callee_h323id[AST_MAX_EXTENSION];
+ char callee_dialedDigits[AST_MAX_EXTENSION];
+ char callee_email[AST_MAX_EXTENSION];
+ char callee_url[AST_MAX_EXTENSION];
+
+ int port;
+ int readformat; /* negotiated read format */
+ int writeformat; /* negotiated write format */
+ int capability;
+ struct ast_codec_pref prefs;
+ int dtmfmode;
+ char exten[AST_MAX_EXTENSION]; /* Requested extension */
+ char context[AST_MAX_EXTENSION]; /* Context where to start */
+ char accountcode[256]; /* Account code */
+ int nat;
+ int amaflags;
+ struct ast_dsp *vad;
+ struct ooh323_pvt *next; /* Next entity */
+} *iflist = NULL;
+
+/* Protect the channel/interface list (ooh323_pvt) */
+AST_MUTEX_DEFINE_STATIC(iflock);
+
+/* Profile of H.323 user registered with PBX*/
+struct ooh323_user{
+ ast_mutex_t lock;
+ char name[256];
+ char context[AST_MAX_EXTENSION];
+ int incominglimit;
+ unsigned inUse;
+ char accountcode[20];
+ int amaflags;
+ int capability;
+ struct ast_codec_pref prefs;
+ int dtmfmode;
+ int rtptimeout;
+ int mUseIP; /* Use IP address or H323-ID to search user */
+ char mIP[20];
+ struct ooh323_user *next;
+};
+
+/* Profile of valid asterisk peers */
+struct ooh323_peer{
+ ast_mutex_t lock;
+ char name[256];
+ unsigned outgoinglimit;
+ unsigned outUse;
+ int capability;
+ struct ast_codec_pref prefs;
+ char accountcode[20];
+ int amaflags;
+ int dtmfmode;
+ int mFriend; /* indicates defined as friend */
+ char ip[20];
+ int port;
+ char *h323id; /* H323-ID alias, which asterisk will register with gk to reach this peer*/
+ char *email; /* Email alias, which asterisk will register with gk to reach this peer*/
+ char *url; /* url alias, which asterisk will register with gk to reach this peer*/
+ char *e164; /* e164 alias, which asterisk will register with gk to reach this peer*/
+ int rtptimeout;
+ struct ooh323_peer *next;
+};
+
+
+/* List of H.323 users known to PBX */
+static struct ast_user_list {
+ struct ooh323_user *users;
+ ast_mutex_t lock;
+} userl;
+
+static struct ast_peer_list {
+ struct ooh323_peer *peers;
+ ast_mutex_t lock;
+} peerl;
+
+/* Mutex to protect H.323 reload process */
+static int h323_reloading = 0;
+AST_MUTEX_DEFINE_STATIC(h323_reload_lock);
+
+/* Mutex to protect usage counter */
+static int usecnt = 0;
+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
+
+AST_MUTEX_DEFINE_STATIC(ooh323c_cmd_lock);
+
+/* stack callbacks */
+int onAlerting(ooCallData *call);
+int onNewCallCreated(ooCallData *call);
+int onCallEstablished(ooCallData *call);
+int onCallCleared(ooCallData *call);
+
+static char gLogFile[256] = DEFAULT_LOGFILE;
+static int gPort = 1720;
+static char gIP[20];
+static char gCallerID[AST_MAX_EXTENSION] = DEFAULT_H323ID;
+static struct ooAliases *gAliasList;
+static int gCapability = AST_FORMAT_ULAW;
+static struct ast_codec_pref gPrefs;
+static int gDTMFMode = H323_DTMF_RFC2833;
+static char gGatekeeper[100];
+static enum RasGatekeeperMode gRasGkMode = RasNoGatekeeper;
+
+static int gIsGateway = 0;
+static int gFastStart = 1;
+static int gTunneling = 1;
+static int gMediaWaitForConnect = 0;
+static int gTOS = 0;
+static int gRTPTimeout = 60;
+static char gAccountcode[80] = DEFAULT_H323ACCNT;
+static int gAMAFLAGS;
+static char gContext[AST_MAX_EXTENSION] = DEFAULT_CONTEXT;
+static int gIncomingLimit = 4;
+static int gOutgoingLimit = 4;
+OOBOOL gH323Debug = FALSE;
+
+static struct ooh323_config
+{
+ int mTCPPortStart;
+ int mTCPPortEnd;
+} ooconfig;
+
+/** Asterisk RTP stuff*/
+static struct sched_context *sched;
+static struct io_context *io;
+
+/* Protect the monitoring thread, so only one process can kill or start it,
+ and not when it's doing something critical. */
+AST_MUTEX_DEFINE_STATIC(monlock);
+
+
+/* This is the thread for the monitor which checks for input on the channels
+ which are not currently in use. */
+static pthread_t monitor_thread = AST_PTHREADT_NULL;
+
+
+static struct ast_channel *ooh323_new(struct ooh323_pvt *i, int state,
+ const char *host)
+{
+ struct ast_channel *ch = NULL;
+ int fmt;
+ if (gH323Debug)
+ ast_verbose("--- ooh323_new - %s\n", host);
+
+
+ /* Don't hold a h323 pvt lock while we allocate a channel */
+ ast_mutex_unlock(&i->lock);
+ ch = ast_channel_alloc(1, state, i->callerid_num, i->callerid_name, i->accountcode, i->exten, i->context, i->amaflags, "OOH323/%s-%08x", host, (unsigned int)(unsigned long) i);
+ ast_mutex_lock(&i->lock);
+
+ if (ch) {
+ ast_channel_lock(ch);
+ ch->tech = &ooh323_tech;
+
+ ch->nativeformats = i->capability;
+
+ fmt = ast_best_codec(ch->nativeformats);
+
+ ch->fds[0] = ast_rtp_fd(i->rtp);
+ ch->fds[1] = ast_rtcp_fd(i->rtp);
+
+ if (state == AST_STATE_RING)
+ ch->rings = 1;
+
+ ch->adsicpe = AST_ADSI_UNAVAILABLE;
+ ch->writeformat = fmt;
+ ch->rawwriteformat = fmt;
+ ch->readformat = fmt;
+ ch->rawreadformat = fmt;
+ ch->tech_pvt = i;
+ i->owner = ch;
+
+ /* Allocate dsp for in-band DTMF support */
+ if (i->dtmfmode & H323_DTMF_INBAND) {
+ i->vad = ast_dsp_new();
+ ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT);
+ }
+
+ ast_mutex_lock(&usecnt_lock);
+ usecnt++;
+ ast_mutex_unlock(&usecnt_lock);
+
+ /* Notify the module monitors that use count for resource has changed*/
+ ast_update_use_count();
+
+ ast_copy_string(ch->context, i->context, sizeof(ch->context));
+ ast_copy_string(ch->exten, i->exten, sizeof(ch->exten));
+
+ ch->priority = 1;
+ if (i->callerid_name) {
+ ch->cid.cid_name = strdup(i->callerid_name);
+ }
+ if (i->callerid_num) {
+
+ ch->cid.cid_num = strdup(i->callerid_num);
+ }
+
+ if (!ast_test_flag(i, H323_OUTGOING)) {
+
+ if (!ast_strlen_zero(i->caller_h323id)) {
+ pbx_builtin_setvar_helper(ch, "_CALLER_H323ID", i->caller_h323id);
+
+ }
+ if (!ast_strlen_zero(i->caller_dialedDigits)) {
+ pbx_builtin_setvar_helper(ch, "_CALLER_H323DIALEDDIGITS",
+ i->caller_dialedDigits);
+ }
+ if (!ast_strlen_zero(i->caller_email)) {
+ pbx_builtin_setvar_helper(ch, "_CALLER_H323EMAIL",
+ i->caller_email);
+ }
+ if (!ast_strlen_zero(i->caller_url)) {
+ pbx_builtin_setvar_helper(ch, "_CALLER_H323URL", i->caller_url);
+ }
+ }
+
+ if (!ast_strlen_zero(i->accountcode))
+ ast_string_field_set(ch, accountcode, i->accountcode);
+
+ if (i->amaflags)
+ ch->amaflags = i->amaflags;
+
+ ast_setstate(ch, state);
+ if (state != AST_STATE_DOWN) {
+ if (ast_pbx_start(ch)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name);
+ ast_channel_unlock(ch);
+ ast_hangup(ch);
+ ch = NULL;
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+
+
+ if (ch)
+ ast_channel_unlock(ch);
+
+ if (gH323Debug)
+ ast_verbose("+++ h323_new\n");
+
+ return ch;
+}
+
+
+
+static struct ooh323_pvt *ooh323_alloc(int callref, char *callToken)
+{
+ struct ooh323_pvt *pvt = NULL;
+ struct in_addr ipAddr;
+ if (gH323Debug)
+ ast_verbose("--- ooh323_alloc\n");
+
+ if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
+ ast_log(LOG_ERROR, "Couldn't allocate private ooh323 structure\n");
+ return NULL;
+ }
+
+ ast_mutex_init(&pvt->lock);
+ ast_mutex_lock(&pvt->lock);
+
+ if (!inet_aton(gIP, &ipAddr)) {
+ ast_log(LOG_ERROR, "Invalid OOH323 driver ip address\n");
+ ast_mutex_unlock(&pvt->lock);
+ ast_mutex_destroy(&pvt->lock);
+ free(pvt);
+ return NULL;
+ }
+
+ if (!(pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, ipAddr))) {
+ ast_log(LOG_WARNING, "Unable to create RTP session: %s\n",
+ strerror(errno));
+ ast_mutex_unlock(&pvt->lock);
+ ast_mutex_destroy(&pvt->lock);
+ free(pvt);
+ return NULL;
+ }
+
+ ast_rtp_setqos(pvt->rtp, gTOS, 0, "ooh323");
+
+ pvt->call_reference = callref;
+ if (callToken)
+ pvt->callToken = strdup(callToken);
+
+ /* whether to use gk for this call */
+ if (gRasGkMode == RasNoGatekeeper)
+ OO_SETFLAG(pvt->flags, H323_DISABLEGK);
+
+ pvt->dtmfmode = gDTMFMode;
+ ast_copy_string(pvt->context, gContext, sizeof(pvt->context));
+ ast_copy_string(pvt->accountcode, gAccountcode, sizeof(pvt->accountcode));
+ pvt->amaflags = gAMAFLAGS;
+ pvt->capability = gCapability;
+ memcpy(&pvt->prefs, &gPrefs, sizeof(pvt->prefs));
+
+ ast_mutex_unlock(&pvt->lock);
+ /* Add to interface list */
+ ast_mutex_lock(&iflock);
+ pvt->next = iflist;
+ iflist = pvt;
+ ast_mutex_unlock(&iflock);
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_alloc\n");
+
+ return pvt;
+}
+
+
+/*
+ Possible data values - peername, exten/peername, exten@ip
+ */
+static struct ast_channel *ooh323_request(const char *type, int format,
+ void *data, int *cause)
+{
+ struct ast_channel *chan = NULL;
+ struct ooh323_pvt *p = NULL;
+ struct ooh323_peer *peer = NULL;
+ char *dest = NULL;
+ char *ext = NULL;
+ char tmp[256];
+ char formats[512];
+ int oldformat;
+ int port = 0;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_request - data %s format %s\n", (char*)data,
+ ast_getformatname_multiple(formats,512,format));
+
+ oldformat = format;
+ format &= AST_FORMAT_AUDIO_MASK;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format "
+ "'%d'\n", format);
+ return NULL;
+ }
+
+ p = ooh323_alloc(0,0); /* Initial callRef is zero */
+
+ if (!p) {
+ ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char*)data);
+ return NULL;
+ }
+ ast_mutex_lock(&p->lock);
+
+ /* This is an outgoing call, since ooh323_request is called */
+ ast_set_flag(p, H323_OUTGOING);
+
+ ast_copy_string(tmp, data, sizeof(data));
+
+ dest = strchr(tmp, '/');
+
+ if (dest) {
+ *dest = '\0';
+ dest++;
+ ext = tmp;
+ } else if ((dest = strchr(tmp, '@'))) {
+ *dest = '\0';
+ dest++;
+ ext = tmp;
+ } else {
+ dest = tmp;
+ ext = NULL;
+ }
+
+#if 0
+ if ((sport = strchr(dest, ':'))) {
+ *sport = '\0';
+ sport++;
+ port = atoi(sport);
+ }
+#endif
+
+ if (dest) {
+ peer = find_peer(dest, port);
+ } else{
+ ast_log(LOG_ERROR, "Destination format is not supported\n");
+ return NULL;
+ }
+
+ if (peer) {
+ p->username = strdup(peer->name);
+ p->host = strdup(peer->ip);
+ p->port = peer->port;
+ /* Disable gk as we are going to call a known peer*/
+ OO_SETFLAG(p->flags, H323_DISABLEGK);
+
+ if (ext)
+ ast_copy_string(p->exten, ext, sizeof(p->exten));
+
+ if (peer->capability & format) {
+ p->capability = peer->capability & format;
+ } else {
+ p->capability = peer->capability;
+ }
+ memcpy(&p->prefs, &peer->prefs, sizeof(struct ast_codec_pref));
+ p->dtmfmode = peer->dtmfmode;
+ ast_copy_string(p->accountcode, peer->accountcode, sizeof(p->accountcode));
+ p->amaflags = peer->amaflags;
+ } else {
+ p->dtmfmode = gDTMFMode;
+ p->capability = gCapability;
+
+ memcpy(&p->prefs, &gPrefs, sizeof(struct ast_codec_pref));
+ p->username = strdup(dest);
+
+ p->host = strdup(dest);
+ if (port > 0) {
+ p->port = port;
+ }
+ if (ext) {
+ ast_copy_string(p->exten, ext, sizeof(p->exten));
+ }
+ }
+
+
+ chan = ooh323_new(p, AST_STATE_DOWN, p->username);
+
+ ast_mutex_unlock(&p->lock);
+
+ if (!chan) {
+ ast_mutex_lock(&iflock);
+ ooh323_destroy(p);
+ ast_mutex_unlock(&iflock);
+ }
+
+ restart_monitor();
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_request\n");
+
+ return chan;
+
+}
+
+
+static struct ooh323_pvt* find_call(ooCallData *call)
+{
+ struct ooh323_pvt *p;
+
+ if (gH323Debug)
+ ast_verbose("--- find_call\n");
+
+ ast_mutex_lock(&iflock);
+
+ for (p = iflist; p; p = p->next) {
+ if (p->callToken && !strcmp(p->callToken, call->callToken)) {
+ break;
+ }
+ }
+ ast_mutex_unlock(&iflock);
+
+ if (gH323Debug)
+ ast_verbose("+++ find_call\n");
+
+ return p;
+}
+
+struct ooh323_user *find_user(const char * name, const char* ip)
+{
+ struct ooh323_user *user;
+
+ if (gH323Debug)
+ ast_verbose("--- find_user\n");
+
+ ast_mutex_lock(&userl.lock);
+ for (user = userl.users; user; user = user->next) {
+ if (ip && user->mUseIP && !strcmp(user->mIP, ip)) {
+ break;
+ }
+ if (name && !strcmp(user->name, name)) {
+ break;
+ }
+ }
+ ast_mutex_unlock(&userl.lock);
+
+ if (gH323Debug)
+ ast_verbose("+++ find_user\n");
+
+ return user;
+}
+
+struct ooh323_peer *find_friend(const char *name, int port)
+{
+ struct ooh323_peer *peer;
+
+ if (gH323Debug)
+ ast_verbose("--- find_friend \"%s\"\n", name);
+
+
+ ast_mutex_lock(&peerl.lock);
+ for (peer = peerl.peers; peer; peer = peer->next) {
+ if (gH323Debug) {
+ ast_verbose(" comparing with \"%s\"\n", peer->ip);
+ }
+ if (!strcmp(peer->ip, name)) {
+ if (port <= 0 || (port > 0 && peer->port == port)) {
+ break;
+ }
+ }
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+ if (gH323Debug) {
+ if (peer) {
+ ast_verbose(" found matching friend\n");
+ }
+ ast_verbose("+++ find_friend \"%s\"\n", name);
+ }
+
+ return peer;
+}
+
+
+struct ooh323_peer *find_peer(const char * name, int port)
+{
+ struct ooh323_peer *peer;
+
+ if (gH323Debug)
+ ast_verbose("--- find_peer \"%s\"\n", name);
+
+ ast_mutex_lock(&peerl.lock);
+ for (peer = peerl.peers; peer; peer = peer->next) {
+ if (gH323Debug) {
+ ast_verbose(" comparing with \"%s\"\n", peer->ip);
+ }
+ if (!strcasecmp(peer->name, name))
+ break;
+ if (peer->h323id && !strcasecmp(peer->h323id, name))
+ break;
+ if (peer->e164 && !strcasecmp(peer->e164, name))
+ break;
+ /*
+ if (!strcmp(peer->ip, name)) {
+ if (port > 0 && peer->port == port) { break; }
+ else if (port <= 0) { break; }
+ }
+ */
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+ if (gH323Debug) {
+ if (peer) {
+ ast_verbose(" found matching peer\n");
+ }
+ ast_verbose("+++ find_peer \"%s\"\n", name);
+ }
+
+ return peer;
+}
+
+static int ooh323_digit_begin(struct ast_channel *chan, char digit)
+{
+ char dtmf[2];
+ struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_digit_begin\n");
+
+ if (!p) {
+ ast_log(LOG_ERROR, "No private structure for call\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ if (p->rtp && (p->dtmfmode & H323_DTMF_RFC2833)) {
+ ast_rtp_senddigit_begin(p->rtp, digit);
+ } else if (((p->dtmfmode & H323_DTMF_Q931) ||
+ (p->dtmfmode & H323_DTMF_H245ALPHANUMERIC) ||
+ (p->dtmfmode & H323_DTMF_H245SIGNAL))) {
+ dtmf[0] = digit;
+ dtmf[1] = '\0';
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ ooSendDTMFDigit(p->callToken, dtmf);
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+ }
+ ast_mutex_unlock(&p->lock);
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_digit_begin\n");
+
+ return 0;
+}
+
+static int ooh323_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ struct ooh323_pvt *p = (struct ooh323_pvt *) chan->tech_pvt;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_digit_end\n");
+
+ if (!p) {
+ ast_log(LOG_ERROR, "No private structure for call\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ if (p->rtp && (p->dtmfmode & H323_DTMF_RFC2833))
+ ast_rtp_senddigit_end(p->rtp, digit);
+
+ ast_mutex_unlock(&p->lock);
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_digit_end\n");
+
+ return 0;
+}
+
+
+static int ooh323_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct ooh323_pvt *p = ast->tech_pvt;
+ char destination[256];
+ int res = 0;
+ const char *val = NULL;
+ ooCallOptions opts = {
+ .fastStart = TRUE,
+ .tunneling = TRUE,
+ .disableGk = TRUE,
+ .callMode = OO_CALLMODE_AUDIOCALL
+ };
+ if (gH323Debug)
+ ast_verbose("--- ooh323_call- %s\n", dest);
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "ooh323_call called on %s, neither down nor "
+ "reserved\n", ast->name);
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ ast_set_flag(p, H323_OUTGOING);
+ if (ast->cid.cid_num) {
+ if (p->callerid_num) {
+ free(p->callerid_num);
+ }
+ p->callerid_num = strdup(ast->cid.cid_num);
+ }
+
+ if (ast->cid.cid_name) {
+ if (p->callerid_name) {
+ free(p->callerid_name);
+ }
+ p->callerid_name = strdup(ast->cid.cid_name);
+ }
+ else{
+ ast->cid.cid_name = strdup(gCallerID);
+ if (p->callerid_name) {
+ free(p->callerid_name);
+ }
+ p->callerid_name = strdup(ast->cid.cid_name);
+ }
+
+ /* Retrieve vars */
+
+
+ if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323ID"))) {
+ ast_copy_string(p->caller_h323id, val, sizeof(p->caller_h323id));
+ }
+
+ if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323DIALEDDIGITS"))) {
+ ast_copy_string(p->caller_dialedDigits, val, sizeof(p->caller_dialedDigits));
+ if (!p->callerid_num) {
+ p->callerid_num = strdup(val);
+ }
+ }
+
+ if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323EMAIL"))) {
+ ast_copy_string(p->caller_email, val, sizeof(p->caller_email));
+ }
+
+ if ((val = pbx_builtin_getvar_helper(ast, "CALLER_H323URL"))) {
+ ast_copy_string(p->caller_url, val, sizeof(p->caller_url));
+ }
+
+
+ if (!(p->callToken = (char*)malloc(AST_MAX_EXTENSION))) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "Failed to allocate memory for callToken\n");
+ return -1; /* TODO: need to clean/hangup?? */
+ }
+
+ if (p->host && p->port != 0)
+ snprintf(destination, sizeof(destination), "%s:%d", p->host, p->port);
+ else if (p->host)
+ snprintf(destination, sizeof(destination), "%s", p->host);
+ else
+ ast_copy_string(destination, dest, sizeof(destination));
+
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ if (OO_TESTFLAG(p->flags, H323_DISABLEGK))
+ res = ooMakeCall(destination, p->callToken, AST_MAX_EXTENSION, &opts);
+ else
+ res = ooMakeCall(destination, p->callToken, AST_MAX_EXTENSION, NULL);
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+
+ ast_mutex_unlock(&p->lock);
+ if (res != OO_OK) {
+ ast_log(LOG_ERROR, "Failed to make call\n");
+ return -1; /* TODO: cleanup */
+ }
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_call\n");
+
+ return 0;
+}
+
+static int ooh323_hangup(struct ast_channel *ast)
+{
+ struct ooh323_pvt *p = ast->tech_pvt;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_hangup\n");
+
+ if (p) {
+ ast_mutex_lock(&p->lock);
+
+ if (gH323Debug)
+ ast_verbose(" hanging %s\n", p->username);
+ ast->tech_pvt = NULL;
+ if (!ast_test_flag(p, H323_ALREADYGONE)) {
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ ooHangCall(p->callToken,
+ ooh323_convert_hangupcause_asteriskToH323(p->owner->hangupcause));
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+ ast_set_flag(p, H323_ALREADYGONE);
+ /* ast_mutex_unlock(&p->lock); */
+ } else {
+ ast_set_flag(p, H323_NEEDDESTROY);
+ }
+ /* detach channel here */
+ if (p->owner) {
+ p->owner->tech_pvt = NULL;
+ p->owner = NULL;
+ }
+
+ ast_mutex_unlock(&p->lock);
+ ast_mutex_lock(&usecnt_lock);
+ usecnt--;
+ ast_mutex_unlock(&usecnt_lock);
+
+ /* Notify the module monitors that use count for resource has changed */
+ ast_update_use_count();
+
+ } else {
+ ast_log(LOG_ERROR, "No call to hangup\n" );
+ return -1;
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_hangup\n");
+
+ return 0;
+}
+
+static int ooh323_answer(struct ast_channel *ast)
+{
+ struct ooh323_pvt *p = ast->tech_pvt;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_answer\n");
+
+ ast_mutex_lock(&p->lock);
+ if (ast->_state != AST_STATE_UP) {
+ ast_channel_lock(ast);
+ ast_setstate(ast, AST_STATE_UP);
+ ast_debug(1, "ooh323_answer(%s)\n", ast->name);
+ ast_channel_unlock(ast);
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ ooAnswerCall(p->callToken);
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+ }
+ ast_mutex_unlock(&p->lock);
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_answer\n");
+
+ return 0;
+}
+
+static struct ast_frame *ooh323_read(struct ast_channel *ast)
+{
+ struct ast_frame *fr;
+ static struct ast_frame null_frame = { AST_FRAME_NULL, };
+ struct ooh323_pvt *p = ast->tech_pvt;
+
+ ast_mutex_lock(&p->lock);
+ if (p->rtp)
+ fr = ooh323_rtp_read(ast, p);
+ else
+ fr = &null_frame;
+ /* time(&p->lastrtprx); */
+ ast_mutex_unlock(&p->lock);
+ return fr;
+}
+
+static int ooh323_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct ooh323_pvt *p = ast->tech_pvt;
+ int res = 0;
+
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (!(f->subclass & ast->nativeformats)) {
+ ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native "
+ "formats is %d (read/write = %d/%d)\n",
+ f->subclass, ast->nativeformats, ast->readformat,
+ ast->writeformat);
+ return 0;
+ }
+ if (p) {
+ ast_mutex_lock(&p->lock);
+ if (p->rtp)
+ res = ast_rtp_write(p->rtp, f);
+ ast_mutex_unlock(&p->lock);
+ }
+ } else if (f->frametype == AST_FRAME_IMAGE) {
+ return 0;
+ } else {
+ ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n",
+ f->frametype);
+ return 0;
+ }
+
+ return res;
+}
+
+static int ooh323_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
+{
+
+ struct ooh323_pvt *p = (struct ooh323_pvt *) ast->tech_pvt;
+ char *callToken = (char *)NULL;
+
+ ast_mutex_lock(&p->lock);
+ callToken = (p->callToken ? strdup(p->callToken) : NULL);
+ ast_mutex_unlock(&p->lock);
+
+ if (!callToken) {
+ if (gH323Debug)
+ ast_verbose(" ooh323_indicate - No callToken\n");
+ return -1;
+ }
+
+ if (gH323Debug)
+ ast_verbose("----- ooh323_indicate %d on call %s\n", condition, callToken);
+
+
+ switch (condition) {
+ case AST_CONTROL_CONGESTION:
+ if (!ast_test_flag(p, H323_ALREADYGONE)) {
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ ooHangCall(callToken, OO_REASON_LOCAL_CONGESTED);
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+ ast_set_flag(p, H323_ALREADYGONE);
+ }
+ break;
+ case AST_CONTROL_BUSY:
+ if (!ast_test_flag(p, H323_ALREADYGONE)) {
+ ast_mutex_lock(&ooh323c_cmd_lock);
+ ooHangCall(callToken, OO_REASON_LOCAL_BUSY);
+ ast_mutex_unlock(&ooh323c_cmd_lock);
+ ast_set_flag(p, H323_ALREADYGONE);
+ }
+ break;
+ case AST_CONTROL_HOLD:
+ ast_moh_start(ast, data, NULL);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_moh_stop(ast);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_PROGRESS:
+ case -1:
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to indicate condition %d on %s\n",
+ condition, callToken);
+ }
+
+ if (gH323Debug)
+ ast_verbose("++++ ooh323_indicate %d on %s\n", condition, callToken);
+
+
+ return -1;
+}
+
+static int ooh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct ooh323_pvt *p = newchan->tech_pvt;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323c ooh323_fixup\n");
+
+ ast_mutex_lock(&p->lock);
+ if (p->owner != oldchan) {
+ ast_log(LOG_WARNING, "Old channel wasn't %p but was %p\n", oldchan, p->owner);
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
+
+ if (p->owner == oldchan) {
+ p->owner = newchan;
+ } else {
+ p->owner = oldchan;
+ }
+
+ ast_mutex_unlock(&p->lock);
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323c ooh323_fixup \n");
+
+ return 0;
+}
+
+
+void ooh323_set_write_format(ooCallData *call, int fmt)
+{
+#if 0
+ struct ooh323_pvt *p = NULL;
+ char formats[512];
+#ifdef print_debug
+ printf("--- ooh323_update_writeformat %s\n",
+ ast_getformatname_multiple(formats,512, fmt));
+#endif
+
+ p = find_call(call);
+ if (!p) {
+ ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken);
+ return;
+ }
+
+ ast_mutex_lock(&p->lock);
+
+ p->writeformat = fmt;
+ ast_mutex_unlock(&p->lock);
+
+ if (p->owner) {
+ printf("Writeformat before update %s\n",
+ ast_getformatname_multiple(formats,512, p->owner->writeformat));
+ ast_set_write_format(p->owner, fmt);
+ }
+ else
+ ast_log(LOG_ERROR, "No owner found\n");
+
+
+#ifdef print_debug
+ printf("+++ ooh323_update_writeformat\n");
+#endif
+#endif
+}
+
+
+void ooh323_set_read_format(ooCallData *call, int fmt)
+{
+#if 0
+ struct ooh323_pvt *p = NULL;
+ char formats[512];
+#ifdef print_debug
+ printf("--- ooh323_update_readformat %s\n",
+ ast_getformatname_multiple(formats,512, fmt));
+#endif
+
+ p = find_call(call);
+ if (!p) {
+ ast_log(LOG_ERROR, "No matching call found for %s\n", call->callToken);
+ return;
+ }
+
+ ast_mutex_lock(&p->lock);
+ p->readformat = fmt;
+ ast_mutex_unlock(&p->lock);
+ ast_set_read_format(p->owner, fmt);
+
+#ifdef print_debug
+ printf("+++ ooh323_update_readformat\n");
+#endif
+#endif
+}
+
+int onAlerting(ooCallData *call)
+{
+ struct ooh323_pvt *p = NULL;
+ struct ast_channel *c = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- onAlerting %s\n", call->callToken);
+
+ if (!(p = find_call(call))) {
+ ast_log(LOG_ERROR, "No matching call found\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ if (!ast_test_flag(p, H323_OUTGOING)) {
+ if (!(c = ooh323_new(p, AST_STATE_RING, p->username))) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "Could not create ast_channel\n");
+ return -1;
+ }
+ ast_mutex_unlock(&p->lock);
+ } else {
+ if (!p->owner) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "Channel has no owner\n");
+ return 0;
+ }
+ c = p->owner;
+ ast_mutex_unlock(&p->lock);
+ ast_channel_lock(c);
+ ast_setstate(c, AST_STATE_RINGING);
+ ast_channel_unlock(c);
+ ast_queue_control(c, AST_CONTROL_RINGING);
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ onAlerting %s\n", call->callToken);
+
+ return OO_OK;
+}
+
+/**
+ * Callback for sending digits from H.323 up to asterisk
+ *
+ */
+int ooh323_onReceivedDigit(OOH323CallData *call, const char *digit)
+{
+ struct ooh323_pvt *p = NULL;
+ struct ast_frame f;
+ int res;
+
+ ast_debug(1, "Received Digit: %c\n", digit[0]);
+ p = find_call(call);
+ if (!p) {
+ ast_log(LOG_ERROR, "Failed to find a matching call.\n");
+ return -1;
+ }
+ if (!p->owner) {
+ ast_log(LOG_ERROR, "Channel has no owner\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ memset(&f, 0, sizeof(f));
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = digit[0];
+ f.datalen = 0;
+ f.samples = 800;
+ f.offset = 0;
+ f.data.ptr = NULL;
+ f.mallocd = 0;
+ f.src = "SEND_DIGIT";
+ ast_mutex_unlock(&p->lock);
+ res = ast_queue_frame(p->owner, &f);
+ return res;
+}
+
+int ooh323_onReceivedSetup(ooCallData *call, Q931Message *pmsg)
+{
+ struct ooh323_pvt *p = NULL;
+ struct ooh323_user *user = NULL;
+ ooAliases *alias = NULL;
+ char *at = NULL;
+ char number [OO_MAX_NUMBER_LENGTH];
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_onReceivedSetup %s\n", call->callToken);
+
+
+ if (!(p = ooh323_alloc(call->callReference, call->callToken))) {
+ ast_log(LOG_ERROR, "Failed to create a new call.\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ ast_clear_flag(p, H323_OUTGOING);
+
+
+ if (call->remoteDisplayName) {
+ p->callerid_name = strdup(call->remoteDisplayName);
+ }
+
+ if (ooCallGetCallingPartyNumber(call, number, OO_MAX_NUMBER_LENGTH) == OO_OK) {
+ p->callerid_num = strdup(number);
+ }
+
+ if (call->remoteAliases) {
+ for (alias = call->remoteAliases; alias; alias = alias->next) {
+ if (alias->type == T_H225AliasAddress_h323_ID) {
+ if (!p->callerid_name) {
+ p->callerid_name = strdup(alias->value);
+ }
+ ast_copy_string(p->caller_h323id, alias->value, sizeof(p->caller_h323id));
+ } else if (alias->type == T_H225AliasAddress_dialedDigits) {
+ if (!p->callerid_num) {
+ p->callerid_num = strdup(alias->value);
+ }
+ ast_copy_string(p->caller_dialedDigits, alias->value,
+ sizeof(p->caller_dialedDigits));
+ } else if (alias->type == T_H225AliasAddress_email_ID) {
+ ast_copy_string(p->caller_email, alias->value, sizeof(p->caller_email));
+ } else if (alias->type == T_H225AliasAddress_url_ID) {
+ ast_copy_string(p->caller_url, alias->value, sizeof(p->caller_url));
+ }
+ }
+ }
+
+ number[0] = '\0';
+ if (ooCallGetCalledPartyNumber(call, number, OO_MAX_NUMBER_LENGTH) == OO_OK) {
+ ast_copy_string(p->exten, number, sizeof(p->exten));
+ } else {
+ update_our_aliases(call, p);
+ if (!ast_strlen_zero(p->callee_dialedDigits)) {
+ ast_copy_string(p->exten, p->callee_dialedDigits, sizeof(p->exten));
+ } else if (!ast_strlen_zero(p->callee_h323id)) {
+ ast_copy_string(p->exten, p->callee_h323id, sizeof(p->exten));
+ } else if (!ast_strlen_zero(p->callee_email)) {
+ ast_copy_string(p->exten, p->callee_email, sizeof(p->exten));
+ if ((at = strchr(p->exten, '@'))) {
+ *at = '\0';
+ }
+ }
+ }
+
+ /* if no extension found, set to default 's' */
+ if (ast_strlen_zero(p->exten)) {
+ ast_copy_string(p->exten, "s", sizeof(p->exten));
+ }
+
+ if (!p->callerid_name) {
+ p->callerid_name = strdup(call->remoteIP);
+ }
+
+ if (p->callerid_name) {
+ if ((user = find_user(p->callerid_name, call->remoteIP))) {
+ ast_mutex_lock(&user->lock);
+ p->username = strdup(user->name);
+ ast_copy_string(p->context, user->context, sizeof(p->context));
+ ast_copy_string(p->accountcode, user->accountcode, sizeof(p->accountcode));
+ p->amaflags = user->amaflags;
+ p->capability = user->capability;
+ memcpy(&p->prefs, &user->prefs, sizeof(struct ast_codec_pref));
+ p->dtmfmode = user->dtmfmode;
+ /* Since, call is coming from a pbx user, no need to use gk */
+ OO_SETFLAG(p->flags, H323_DISABLEGK);
+ OO_SETFLAG(call->flags, OO_M_DISABLEGK);
+ ast_mutex_unlock(&user->lock);
+ }
+ }
+
+
+ ooh323c_set_capability_for_call(call, &p->prefs, p->capability, p->dtmfmode);
+ configure_local_rtp(p, call);
+
+ ast_mutex_unlock(&p->lock);
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_onReceivedSetup - Determined context %s, "
+ "extension %s\n", p->context, p->exten);
+
+ return OO_OK;
+}
+
+
+
+int onNewCallCreated(ooCallData *call)
+{
+ struct ooh323_pvt *p = NULL;
+ int i = 0;
+
+ if (gH323Debug)
+ ast_verbose("--- onNewCallCreated %s\n", call->callToken);
+
+ if (!strcmp(call->callType, "outgoing")) {
+ p = find_call(call);
+ if (!p) {
+ ast_log(LOG_ERROR, "No matching call found for outgoing call\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ if (p->callerid_name) {
+ ooCallSetCallerId(call, p->callerid_name);
+ }
+ if (p->callerid_num) {
+ i = 0;
+ while (*(p->callerid_num + i) != '\0') {
+ if (!isdigit(*(p->callerid_num + i))) {
+ break;
+ }
+ i++;
+ }
+ if (*(p->callerid_num + i) == '\0') {
+ ooCallSetCallingPartyNumber(call, p->callerid_num);
+ } else {
+ if (!p->callerid_name) {
+ ooCallSetCallerId(call, p->callerid_num);
+ }
+ }
+ }
+
+ if (!ast_strlen_zero(p->caller_h323id))
+ ooCallAddAliasH323ID(call, p->caller_h323id);
+
+ if (!ast_strlen_zero(p->caller_dialedDigits)) {
+ if (gH323Debug) {
+ ast_verbose("Setting dialed digits %s\n", p->caller_dialedDigits);
+ }
+ ooCallAddAliasDialedDigits(call, p->caller_dialedDigits);
+ } else if (p->callerid_num) {
+ if (ooIsDailedDigit(p->callerid_num)) {
+ if (gH323Debug) {
+ ast_verbose("setting callid number %s\n", p->callerid_num);
+ }
+ ooCallAddAliasDialedDigits(call, p->callerid_num);
+ } else if (ast_strlen_zero(p->caller_h323id)) {
+ ooCallAddAliasH323ID(call, p->callerid_num);
+ }
+ }
+
+
+ if (!ast_strlen_zero(p->exten)) {
+ if (ooIsDailedDigit(p->exten)) {
+ ooCallSetCalledPartyNumber(call, p->exten);
+ ooCallAddRemoteAliasDialedDigits(call, p->exten);
+ } else {
+ ooCallAddRemoteAliasH323ID(call, p->exten);
+ }
+ }
+
+ if (gH323Debug) {
+ char prefsBuf[256];
+ ast_codec_pref_string(&p->prefs, prefsBuf, sizeof(prefsBuf));
+ ast_verbose(" Outgoing call %s(%s) - Codec prefs - %s\n",
+ p->username?p->username:"NULL", call->callToken, prefsBuf);
+ }
+
+ ooh323c_set_capability_for_call(call, &p->prefs, p->capability, p->dtmfmode);
+
+ configure_local_rtp(p, call);
+ ast_mutex_unlock(&p->lock);
+ }
+ if (gH323Debug)
+ ast_verbose("+++ onNewCallCreated %s\n", call->callToken);
+
+ return OO_OK;
+}
+
+int onCallEstablished(ooCallData *call)
+{
+ struct ooh323_pvt *p = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- onCallEstablished %s\n", call->callToken);
+
+ if (!(p = find_call(call))) {
+ ast_log(LOG_ERROR, "Failed to find a matching call.\n");
+ return -1;
+ }
+ ast_mutex_lock(&p->lock);
+ if (!p->owner) {
+ ast_mutex_unlock(&p->lock);
+ ast_log(LOG_ERROR, "Channel has no owner\n");
+ return -1;
+ }
+
+ while (ast_channel_trylock(p->owner)) {
+ ast_debug(1,"Failed to grab lock, trying again\n");
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ }
+ if (p->owner->_state != AST_STATE_UP) {
+ ast_setstate(p->owner, AST_STATE_UP);
+ }
+ ast_channel_unlock(p->owner);
+ if (ast_test_flag(p, H323_OUTGOING)) {
+ struct ast_channel* c = p->owner;
+ ast_mutex_unlock(&p->lock);
+ ast_queue_control(c, AST_CONTROL_ANSWER);
+ } else {
+ ast_mutex_unlock(&p->lock);
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ onCallEstablished %s\n", call->callToken);
+
+ return OO_OK;
+}
+
+int onCallCleared(ooCallData *call)
+{
+ struct ooh323_pvt *p = NULL;
+ int ownerLock = 0;
+
+ if (gH323Debug)
+ ast_verbose("--- onCallCleared %s \n", call->callToken);
+
+ p = find_call(call);
+ if (!p) {
+ return 0;
+ }
+ ast_mutex_lock(&p->lock);
+
+ while (p->owner) {
+ if (ast_channel_trylock(p->owner)) {
+ ooTrace(OOTRCLVLINFO, "Failed to grab lock, trying again\n");
+ ast_debug(1,"Failed to grab lock, trying again\n");
+ ast_mutex_unlock(&p->lock);
+ usleep(1);
+ ast_mutex_lock(&p->lock);
+ } else {
+ ownerLock = 1;
+ break;
+ }
+ }
+
+ if (ownerLock) {
+ if (!ast_test_flag(p, H323_ALREADYGONE)) {
+
+ /* NOTE: Channel is not detached yet */
+ ast_set_flag(p, H323_ALREADYGONE);
+ p->owner->hangupcause =
+ ooh323_convert_hangupcause_h323ToAsterisk(call->callEndReason);
+ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_channel_unlock(p->owner);
+ ast_queue_hangup(p->owner);
+ ast_mutex_unlock(&p->lock);
+ return OO_OK;
+ }
+ ast_channel_unlock(p->owner);
+ }
+ ast_set_flag(p, H323_NEEDDESTROY);
+ ast_mutex_unlock(&p->lock);
+
+ if (gH323Debug)
+ ast_verbose("+++ onCallCleared\n");
+
+ return OO_OK;
+}
+
+#if 0
+static void ooh323_delete_user(struct ooh323_user *user)
+{
+ struct ooh323_user *prev = NULL, *cur = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_delete_user\n");
+
+ if (user) {
+ cur = userl.users;
+ ast_mutex_lock(&userl.lock);
+ while (cur) {
+ if (cur == user) break;
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ userl.users = cur->next;
+ }
+ ast_mutex_unlock(&userl.lock);
+
+ free(user);
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_delete_user\n");
+
+}
+#endif
+
+void ooh323_delete_peer(struct ooh323_peer *peer)
+{
+ struct ooh323_peer *prev = NULL, *cur = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_delete_peer\n");
+
+ if (peer) {
+ ast_mutex_lock(&peerl.lock);
+ for (cur = peerl.peers; cur; prev = cur, cur = cur->next) {
+ if (cur == peer) {
+ break;
+ }
+ }
+
+ if (cur) {
+ if (prev) {
+ prev->next = cur->next;
+ } else {
+ peerl.peers = cur->next;
+ }
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+ if (peer->h323id)
+ free(peer->h323id);
+ if (peer->email)
+ free(peer->email);
+ if (peer->url)
+ free(peer->url);
+ if (peer->e164)
+ free(peer->e164);
+
+ free(peer);
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_delete_peer\n");
+}
+
+
+
+static struct ooh323_user *build_user(const char *name, struct ast_variable *v)
+{
+ struct ooh323_user *user = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- build_user\n");
+
+ user = ast_calloc(1, sizeof(*user));
+ if (user) {
+ ast_mutex_init(&user->lock);
+ ast_copy_string(user->name, name, sizeof(user->name));
+ user->capability = gCapability;
+ memcpy(&user->prefs, &gPrefs, sizeof(user->prefs));
+ user->rtptimeout = gRTPTimeout;
+ user->dtmfmode = gDTMFMode;
+ /* set default context */
+ ast_copy_string(user->context, gContext, sizeof(user->context));
+ ast_copy_string(user->accountcode, gAccountcode, sizeof(user->accountcode));
+ user->amaflags = gAMAFLAGS;
+
+ while (v) {
+ if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(user->context, v->value, sizeof(user->context));
+ } else if (!strcasecmp(v->name, "incominglimit")) {
+ user->incominglimit = atoi(v->value);
+ if (user->incominglimit < 0)
+ user->incominglimit = 0;
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode));
+ } else if (!strcasecmp(v->name, "rtptimeout")) {
+ user->rtptimeout = atoi(v->value);
+ if (user->rtptimeout < 0)
+ user->rtptimeout = gRTPTimeout;
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&user->prefs, &user->capability,
+ v->value, 0);
+ } else if (!strcasecmp(v->name, "allow")) {
+ const char* tcodecs = v->value;
+ if (!strcasecmp(v->value, "all")) {
+ tcodecs = "ulaw,alaw,g729,g723,gsm";
+ }
+ ast_parse_allow_disallow(&user->prefs, &user->capability,
+ tcodecs, 1);
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ user->amaflags = ast_cdr_amaflags2int(v->value);
+ } else if (!strcasecmp(v->name, "ip")) {
+ ast_copy_string(user->mIP, v->value, sizeof(user->mIP));
+ user->mUseIP = 1;
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ if (!strcasecmp(v->value, "rfc2833"))
+ user->dtmfmode = H323_DTMF_RFC2833;
+ else if (!strcasecmp(v->value, "q931keypad"))
+ user->dtmfmode = H323_DTMF_Q931;
+ else if (!strcasecmp(v->value, "h245alphanumeric"))
+ user->dtmfmode = H323_DTMF_H245ALPHANUMERIC;
+ else if (!strcasecmp(v->value, "h245signal"))
+ user->dtmfmode = H323_DTMF_H245SIGNAL;
+ }
+ v = v->next;
+ }
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ build_user\n");
+
+ return user;
+}
+
+static struct ooh323_peer *build_peer(const char *name, struct ast_variable *v, int friend_type)
+{
+ struct ooh323_peer *peer = NULL;
+
+ if (gH323Debug)
+ ast_verbose("--- build_peer\n");
+
+ peer = ast_calloc(1, sizeof(*peer));
+ if (peer) {
+ memset(peer, 0, sizeof(struct ooh323_peer));
+ ast_mutex_init(&peer->lock);
+ ast_copy_string(peer->name, name, sizeof(peer->name));
+ peer->capability = gCapability;
+ memcpy(&peer->prefs, &gPrefs, sizeof(struct ast_codec_pref));
+ peer->rtptimeout = gRTPTimeout;
+ ast_copy_string(peer->accountcode, gAccountcode, sizeof(peer->accountcode));
+ peer->amaflags = gAMAFLAGS;
+ peer->dtmfmode = gDTMFMode;
+ if (0 == friend_type) {
+ peer->mFriend = 1;
+ }
+
+ while (v) {
+ if (!strcasecmp(v->name, "h323id")) {
+ if (!(peer->h323id = ast_strdup(v->value))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for h323id of "
+ "peer %s\n", name);
+ ooh323_delete_peer(peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "e164")) {
+ if (!(peer->e164 = ast_strdup(v->value))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for e164 of "
+ "peer %s\n", name);
+ ooh323_delete_peer(peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "email")) {
+ if (!(peer->email = ast_strdup(v->value))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for email of "
+ "peer %s\n", name);
+ ooh323_delete_peer(peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "url")) {
+ if (!(peer->url = ast_strdup(v->value))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for h323id of "
+ "peer %s\n", name);
+ ooh323_delete_peer(peer);
+ return NULL;
+ }
+ } else if (!strcasecmp(v->name, "port")) {
+ peer->port = atoi(v->value);
+ } else if (!strcasecmp(v->name, "ip")) {
+ ast_copy_string(peer->ip, v->value, sizeof(peer->ip));
+ } else if (!strcasecmp(v->name, "outgoinglimit")) {
+ if ((peer->outgoinglimit = atoi(v->value)) < 0) {
+ peer->outgoinglimit = 0;
+ }
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode));
+ } else if (!strcasecmp(v->name, "rtptimeout")) {
+ if ((peer->rtptimeout = atoi(v->value)) < 0) {
+ peer->rtptimeout = gRTPTimeout;
+ }
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&peer->prefs, &peer->capability,
+ v->value, 0);
+ } else if (!strcasecmp(v->name, "allow")) {
+ const char* tcodecs = v->value;
+ if (!strcasecmp(v->value, "all")) {
+ tcodecs = "ulaw,alaw,g729,g723,gsm";
+ }
+ ast_parse_allow_disallow(&peer->prefs, &peer->capability,
+ tcodecs, 1);
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ peer->amaflags = ast_cdr_amaflags2int(v->value);
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ if (!strcasecmp(v->value, "rfc2833"))
+ peer->dtmfmode = H323_DTMF_RFC2833;
+ else if (!strcasecmp(v->value, "q931keypad"))
+ peer->dtmfmode = H323_DTMF_Q931;
+ else if (!strcasecmp(v->value, "h245alphanumeric"))
+ peer->dtmfmode = H323_DTMF_H245ALPHANUMERIC;
+ else if (!strcasecmp(v->value, "h245signal"))
+ peer->dtmfmode = H323_DTMF_H245SIGNAL;
+ }
+ v = v->next;
+ }
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ build_peer\n");
+
+ return peer;
+}
+
+static int ooh323_do_reload(void)
+{
+ if (gH323Debug) {
+ ast_verbose("--- ooh323_do_reload\n");
+ }
+
+ reload_config(1);
+
+ if (gH323Debug) {
+ ast_verbose("+++ ooh323_do_reload\n");
+ }
+
+ return 0;
+}
+
+#if 0
+/*--- h323_reload: Force reload of module from cli ---*/
+static int ooh323_reload(int fd, int argc, char *argv[])
+{
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_reload\n");
+
+ ast_mutex_lock(&h323_reload_lock);
+ if (h323_reloading) {
+ ast_verbose("Previous OOH323 reload not yet done\n");
+ }
+ else {
+ h323_reloading = 1;
+ }
+ ast_mutex_unlock(&h323_reload_lock);
+ restart_monitor();
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_reload\n");
+
+ return 0;
+}
+#endif
+
+#if 0
+static int reload(void *mod)
+{
+ return ooh323_reload(0, 0, NULL);
+}
+#endif
+
+int reload_config(int reload)
+{
+ int format;
+ struct ooAliases *pNewAlias = NULL;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+ struct ooh323_user *user = NULL;
+ struct ooh323_peer *peer = NULL;
+ char *cat;
+ const char *utype;
+
+ if (gH323Debug)
+ ast_verbose("--- reload_config\n");
+
+ cfg = ast_config_load((char*)config, config_flags);
+
+ /* We *must* have a config file otherwise stop immediately */
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to load config %s, OOH323 disabled\n", config);
+ return 1;
+ } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
+ return RESULT_SUCCESS;
+
+ if (reload) {
+ delete_users();
+ delete_peers();
+ }
+
+ /* Inintialize everything to default */
+ strcpy(gLogFile, DEFAULT_LOGFILE);
+ gPort = 1720;
+ gIP[0] = '\0';
+ strcpy(gCallerID, DEFAULT_H323ID);
+ gCapability = AST_FORMAT_ULAW;
+ memset(&gPrefs, 0, sizeof(struct ast_codec_pref));
+ gDTMFMode = H323_DTMF_RFC2833;
+ gRasGkMode = RasNoGatekeeper;
+ gGatekeeper[0] = '\0';
+ gRTPTimeout = 60;
+ strcpy(gAccountcode, DEFAULT_H323ACCNT);
+ gFastStart = 1;
+ gTunneling = 1;
+ gTOS = 0;
+ strcpy(gContext, DEFAULT_CONTEXT);
+ gAliasList = NULL;
+ gMediaWaitForConnect = 0;
+ ooconfig.mTCPPortStart = 12030;
+ ooconfig.mTCPPortEnd = 12230;
+
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+
+ if (!strcasecmp(v->name, "port")) {
+ gPort = (int)strtol(v->value, NULL, 10);
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ ast_copy_string(gIP, v->value, sizeof(gIP));
+ } else if (!strcasecmp(v->name, "h225portrange")) {
+ char* endlimit = 0;
+ char temp[256];
+ ast_copy_string(temp, v->value, sizeof(temp));
+ endlimit = strchr(temp, ',');
+ if (endlimit) {
+ *endlimit = '\0';
+ endlimit++;
+ ooconfig.mTCPPortStart = atoi(temp);
+ ooconfig.mTCPPortEnd = atoi(endlimit);
+
+ if (ooH323EpSetTCPPortRange(ooconfig.mTCPPortStart,
+ ooconfig.mTCPPortEnd) == OO_FAILED) {
+ ast_log(LOG_ERROR, "h225portrange: Failed to set range\n");
+ }
+ } else {
+ ast_log(LOG_ERROR, "h225portrange: Invalid format, separate port range with \",\"\n");
+ }
+ } else if (!strcasecmp(v->name, "gateway")) {
+ gIsGateway = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "faststart")) {
+ gFastStart = ast_true(v->value);
+ if (gFastStart)
+ ooH323EpEnableFastStart();
+ else
+ ooH323EpDisableFastStart();
+ } else if (!strcasecmp(v->name, "mediawaitforconnect")) {
+ gMediaWaitForConnect = ast_true(v->value);
+ if (gMediaWaitForConnect)
+ ooH323EpEnableMediaWaitForConnect();
+ else
+ ooH323EpDisableMediaWaitForConnect();
+ } else if (!strcasecmp(v->name, "h245tunneling")) {
+ gTunneling = ast_true(v->value);
+ if (gTunneling)
+ ooH323EpEnableH245Tunneling();
+ else
+ ooH323EpDisableH245Tunneling();
+ } else if (!strcasecmp(v->name, "h323id")) {
+ pNewAlias = malloc(sizeof(*pNewAlias));
+ if (!pNewAlias) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for h323id alias\n");
+ return 1;
+ }
+ pNewAlias->type = T_H225AliasAddress_h323_ID;
+ pNewAlias->value = strdup(v->value);
+ pNewAlias->next = gAliasList;
+ gAliasList = pNewAlias;
+ pNewAlias = NULL;
+ } else if (!strcasecmp(v->name, "e164")) {
+ pNewAlias = malloc(sizeof(*pNewAlias));
+ if (!pNewAlias) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for e164 alias\n");
+ return 1;
+ }
+ pNewAlias->type = T_H225AliasAddress_dialedDigits;
+ pNewAlias->value = strdup(v->value);
+ pNewAlias->next = gAliasList;
+ gAliasList = pNewAlias;
+ pNewAlias = NULL;
+ } else if (!strcasecmp(v->name, "email")) {
+ pNewAlias = malloc(sizeof(*pNewAlias));
+ if (!pNewAlias) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for email alias\n");
+ return 1;
+ }
+ pNewAlias->type = T_H225AliasAddress_email_ID;
+ pNewAlias->value = strdup(v->value);
+ pNewAlias->next = gAliasList;
+ gAliasList = pNewAlias;
+ pNewAlias = NULL;
+ } else if (!strcasecmp(v->name, "callerid")) {
+ ast_copy_string(gCallerID, v->value, sizeof(gCallerID));
+ } else if (!strcasecmp(v->name, "incominglimit")) {
+ gIncomingLimit = atoi(v->value);
+ } else if (!strcasecmp(v->name, "outgoinglimit")) {
+ gOutgoingLimit = atoi(v->value);
+ } else if (!strcasecmp(v->name, "gatekeeper")) {
+ if (!strcasecmp(v->value, "DISABLE")) {
+ gRasGkMode = RasNoGatekeeper;
+ } else if (!strcasecmp(v->value, "DISCOVER")) {
+ gRasGkMode = RasDiscoverGatekeeper;
+ } else {
+ gRasGkMode = RasUseSpecificGatekeeper;
+ ast_copy_string(gGatekeeper, v->value, sizeof(gGatekeeper));
+ }
+ } else if (!strcasecmp(v->name, "logfile")) {
+ ast_copy_string(gLogFile, v->value, sizeof(gLogFile));
+ } else if (!strcasecmp(v->name, "context")) {
+ ast_copy_string(gContext, v->value, sizeof(gContext));
+ ast_verb(3, " == Setting default context to %s\n", gContext);
+ } else if (!strcasecmp(v->name, "rtptimeout")) {
+ gRTPTimeout = atoi(v->value);
+ if (gRTPTimeout <= 0)
+ gRTPTimeout = 60;
+ } else if (!strcasecmp(v->name, "tos")) {
+ if (sscanf(v->value, "%i", &format) == 1)
+ gTOS = format & 0xff;
+ else if (!strcasecmp(v->value, "lowdelay"))
+ gTOS = IPTOS_LOWDELAY;
+ else if (!strcasecmp(v->value, "throughput"))
+ gTOS = IPTOS_THROUGHPUT;
+ else if (!strcasecmp(v->value, "reliability"))
+ gTOS = IPTOS_RELIABILITY;
+ else if (!strcasecmp(v->value, "mincost"))
+ gTOS = IPTOS_MINCOST;
+ else if (!strcasecmp(v->value, "none"))
+ gTOS = 0;
+ else
+ ast_log(LOG_WARNING, "Invalid tos value at line %d, should be "
+ "'lowdelay', 'throughput', 'reliability', "
+ "'mincost', or 'none'\n", v->lineno);
+ } else if (!strcasecmp(v->name, "amaflags")) {
+ gAMAFLAGS = ast_cdr_amaflags2int(v->value);
+ } else if (!strcasecmp(v->name, "accountcode")) {
+ ast_copy_string(gAccountcode, v->value, sizeof(gAccountcode)-1);
+ } else if (!strcasecmp(v->name, "disallow")) {
+ ast_parse_allow_disallow(&gPrefs, &gCapability, v->value, 0);
+ } else if (!strcasecmp(v->name, "allow")) {
+ const char* tcodecs = v->value;
+ if (!strcasecmp(v->value, "all")) {
+ tcodecs = "ulaw,alaw,g729,g723,gsm";
+ }
+ ast_parse_allow_disallow(&gPrefs, &gCapability, tcodecs, 1);
+ } else if (!strcasecmp(v->name, "dtmfmode")) {
+ if (!strcasecmp(v->value, "inband"))
+ gDTMFMode = H323_DTMF_INBAND;
+ else if (!strcasecmp(v->value, "rfc2833"))
+ gDTMFMode = H323_DTMF_RFC2833;
+ else if (!strcasecmp(v->value, "q931keypad"))
+ gDTMFMode = H323_DTMF_Q931;
+ else if (!strcasecmp(v->value, "h245alphanumeric"))
+ gDTMFMode = H323_DTMF_H245ALPHANUMERIC;
+ else if (!strcasecmp(v->value, "h245signal"))
+ gDTMFMode = H323_DTMF_H245SIGNAL;
+ else {
+ ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value);
+ gDTMFMode = H323_DTMF_RFC2833;
+ }
+ }
+ v = v->next;
+ }
+
+ for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
+ if (strcasecmp(cat, "general")) {
+ int friend_type = 0;
+ utype = ast_variable_retrieve(cfg, cat, "type");
+ if (utype) {
+ friend_type = strcasecmp(utype, "friend");
+ if (!strcmp(utype, "user") || 0 == friend_type) {
+ user = build_user(cat, ast_variable_browse(cfg, cat));
+ if (user) {
+ ast_mutex_lock(&userl.lock);
+ user->next = userl.users;
+ userl.users = user;
+ ast_mutex_unlock(&userl.lock);
+ } else {
+ ast_log(LOG_WARNING, "Failed to build user %s\n", cat);
+ }
+ }
+ if (!strcasecmp(utype, "peer") || 0 == friend_type) {
+ peer = build_peer(cat, ast_variable_browse(cfg, cat), friend_type);
+ if (peer) {
+ ast_mutex_lock(&peerl.lock);
+ peer->next = peerl.peers;
+ peerl.peers = peer;
+ ast_mutex_unlock(&peerl.lock);
+ } else {
+ ast_log(LOG_WARNING, "Failed to build peer %s\n", cat);
+ }
+ }
+ }
+ }
+ }
+ ast_config_destroy(cfg);
+
+
+ /* Determine ip address if neccessary */
+ if (ast_strlen_zero(gIP)) {
+ ooGetLocalIPAddress(gIP);
+ if (!strcmp(gIP, "127.0.0.1")) {
+ ast_log(LOG_NOTICE, "Failed to determine local ip address. Please "
+ "specify it in ooh323.conf. OOH323 Disabled\n");
+ return 1;
+ }
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ reload_config\n");
+
+ return 0;
+}
+
+static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char ip_port[30];
+ struct ooh323_peer *prev = NULL, *peer = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 show peer";
+ e->usage =
+ "Usage: ooh323 show peer <name>\n"
+ " List details of specific OOH323 peer.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&peerl.lock);
+ peer = peerl.peers;
+ while (peer) {
+ ast_mutex_lock(&peer->lock);
+ if (!strcmp(peer->name, a->argv[3]))
+ break;
+ else {
+ prev = peer;
+ peer = peer->next;
+ ast_mutex_unlock(&prev->lock);
+ }
+ }
+
+ if (peer) {
+ snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port);
+ ast_cli(a->fd, "%-15.15s%s\n", "Name: ", peer->name);
+ ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "(");
+ print_codec_to_cli(a->fd, &peer->prefs);
+ ast_cli(a->fd, ")\n");
+ ast_cli(a->fd, "%-15.15s", "DTMF Mode: ");
+ if (peer->dtmfmode & H323_DTMF_RFC2833)
+ ast_cli(a->fd, "%s\n", "rfc2833");
+ else if (peer->dtmfmode & H323_DTMF_Q931)
+ ast_cli(a->fd, "%s\n", "q931keypad");
+ else if (peer->dtmfmode & H323_DTMF_H245ALPHANUMERIC)
+ ast_cli(a->fd, "%s\n", "h245alphanumeric");
+ else if (peer->dtmfmode & H323_DTMF_H245SIGNAL)
+ ast_cli(a->fd, "%s\n", "h245signal");
+ else
+ ast_cli(a->fd, "%s\n", "unknown");
+ ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", peer->accountcode);
+ ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ",
+ ast_cdr_flags2str(peer->amaflags));
+ ast_cli(a->fd, "%-15.15s%s\n", "Ip:Port: ", ip_port);
+ ast_cli(a->fd, "%-15.15s%d\n", "OutgoingLimit: ", peer->outgoinglimit);
+ ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", peer->rtptimeout);
+ ast_mutex_unlock(&peer->lock);
+ } else {
+ ast_cli(a->fd, "Peer %s not found\n", a->argv[3]);
+ ast_cli(a->fd, "\n");
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_ooh323_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char ip_port[30];
+ char formats[512];
+ struct ooh323_peer *prev = NULL, *peer = NULL;
+
+#define FORMAT "%-15.15s %-15.15s %-23.23s %-s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 show peers";
+ e->usage =
+ "Usage: ooh323 show peers\n"
+ " Lists all known OOH323 peers.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT, "Name", "Accountcode", "ip:port", "Formats");
+
+ ast_mutex_lock(&peerl.lock);
+ peer = peerl.peers;
+ while (peer) {
+ ast_mutex_lock(&peer->lock);
+ snprintf(ip_port, sizeof(ip_port), "%s:%d", peer->ip, peer->port);
+ ast_cli(a->fd, FORMAT, peer->name,
+ peer->accountcode,
+ ip_port,
+ ast_getformatname_multiple(formats, sizeof(formats), peer->capability));
+ prev = peer;
+ peer = peer->next;
+ ast_mutex_unlock(&prev->lock);
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+#undef FORMAT
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief Print codec list from preference to CLI/manager */
+static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
+{
+ int x, codec;
+
+ for (x = 0; x < 32; x++) {
+ codec = ast_codec_pref_index(pref, x);
+ if (!codec)
+ break;
+ ast_cli(fd, "%s", ast_getformatname(codec));
+ ast_cli(fd, ":%d", pref->framing[x]);
+ if (x < 31 && ast_codec_pref_index(pref, x + 1))
+ ast_cli(fd, ",");
+ }
+ if (!x)
+ ast_cli(fd, "none");
+}
+
+static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ooh323_user *prev = NULL, *user = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 show user";
+ e->usage =
+ "Usage: ooh323 show user <name>\n"
+ " List details of specific OOH323 user.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&userl.lock);
+ user = userl.users;
+ while (user) {
+ ast_mutex_lock(&user->lock);
+ if (!strcmp(user->name, a->argv[3]))
+ break;
+ else {
+ prev = user;
+ user = user->next;
+ ast_mutex_unlock(&prev->lock);
+ }
+ }
+
+ if (user) {
+ ast_cli(a->fd, "%-15.15s%s\n", "Name: ", user->name);
+ ast_cli(a->fd, "%-15.15s%s", "Format Prefs: ", "(");
+ print_codec_to_cli(a->fd, &user->prefs);
+ ast_cli(a->fd, ")\n");
+ ast_cli(a->fd, "%-15.15s", "DTMF Mode: ");
+ if (user->dtmfmode & H323_DTMF_RFC2833)
+ ast_cli(a->fd, "%s\n", "rfc2833");
+ else if (user->dtmfmode & H323_DTMF_Q931)
+ ast_cli(a->fd, "%s\n", "q931keypad");
+ else if (user->dtmfmode & H323_DTMF_H245ALPHANUMERIC)
+ ast_cli(a->fd, "%s\n", "h245alphanumeric");
+ else if (user->dtmfmode & H323_DTMF_H245SIGNAL)
+ ast_cli(a->fd, "%s\n", "h245signal");
+ else
+ ast_cli(a->fd, "%s\n", "unknown");
+ ast_cli(a->fd, "%-15.15s%s\n", "AccountCode: ", user->accountcode);
+ ast_cli(a->fd, "%-15.15s%s\n", "AMA flags: ", ast_cdr_flags2str(user->amaflags));
+ ast_cli(a->fd, "%-15.15s%s\n", "Context: ", user->context);
+ ast_cli(a->fd, "%-15.15s%d\n", "IncomingLimit: ", user->incominglimit);
+ ast_cli(a->fd, "%-15.15s%d\n", "rtptimeout: ", user->rtptimeout);
+ ast_mutex_unlock(&user->lock);
+ } else {
+ ast_cli(a->fd, "User %s not found\n", a->argv[3]);
+ ast_cli(a->fd, "\n");
+ }
+ ast_mutex_unlock(&userl.lock);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_ooh323_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char formats[512];
+ struct ooh323_user *prev = NULL, *user = NULL;
+
+#define FORMAT1 "%-15.15s %-15.15s %-15.15s %-s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 show users";
+ e->usage =
+ "Usage: ooh323 show users \n"
+ " Lists all known OOH323 users.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT1, "Username", "Accountcode", "Context", "Formats");
+
+ ast_mutex_lock(&userl.lock);
+ user = userl.users;
+ while (user) {
+ ast_mutex_lock(&user->lock);
+ ast_cli(a->fd, FORMAT1, user->name,
+ user->accountcode, user->context,
+ ast_getformatname_multiple(formats, 512, user->capability));
+ prev = user;
+ user = user->next;
+ ast_mutex_unlock(&prev->lock);
+ }
+ ast_mutex_unlock(&userl.lock);
+
+#undef FORMAT1
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_ooh323_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 set debug [off]";
+ e->usage =
+ "Usage: ooh323 set debug [off]\n"
+ " Enables/Disables debugging of OOH323 channel driver\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4 && strcasecmp(a->argv[3], "off"))
+ return CLI_SHOWUSAGE;
+
+ gH323Debug = (a->argc == 4) ? FALSE : TRUE;
+ ast_cli(a->fd, "OOH323 Debugging %s\n", gH323Debug ? "Enabled" : "Disabled");
+
+ return CLI_SUCCESS;
+}
+
+#if 0
+static int ooh323_show_channels(int fd, int argc, char *argv[])
+{
+ return RESULT_SUCCESS;
+}
+#endif
+
+static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char value[512];
+ ooAliases *pAlias = NULL, *pAliasNext = NULL;;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "ooh323 show config";
+ e->usage =
+ "Usage: ooh323 show config\n"
+ " Shows global configuration of H.323 channel driver\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ snprintf(value, sizeof(value), "%s:%d", gIP, gPort);
+ ast_cli(a->fd, "\nObjective Open H.323 Channel Driver's Config:\n");
+ ast_cli(a->fd, "%-20s%s\n", "IP:Port: ", value);
+ ast_cli(a->fd, "%-20s%s\n", "FastStart", gFastStart?"yes":"no");
+ ast_cli(a->fd, "%-20s%s\n", "Tunneling", gTunneling?"yes":"no");
+ ast_cli(a->fd, "%-20s%s\n", "CallerId", gCallerID);
+ ast_cli(a->fd, "%-20s%s\n", "MediaWaitForConnect", gMediaWaitForConnect ? "yes" : "no");
+
+#if 0
+ {
+ extern OOH323EndPoint gH323ep;
+
+ ast_cli(a->fd, "%-20s%s\n", "FASTSTART",
+ (OO_TESTFLAG(gH323ep.flags, OO_M_FASTSTART) != 0) ? "yes" : "no");
+ ast_cli(a->fd, "%-20s%s\n", "TUNNELING",
+ (OO_TESTFLAG(gH323ep.flags, OO_M_TUNNELING) != 0) ? "yes" : "no");
+ ast_cli(a->fd, "%-20s%s\n", "MEDIAWAITFORCONN",
+ (OO_TESTFLAG(gH323ep.flags, OO_M_MEDIAWAITFORCONN) != 0) ? "yes" : "no");
+ }
+#endif
+
+ if (gRasGkMode == RasNoGatekeeper)
+ snprintf(value, sizeof(value), "%s", "No Gatekeeper");
+ else if (gRasGkMode == RasDiscoverGatekeeper)
+ snprintf(value, sizeof(value), "%s", "Discover");
+ else
+ snprintf(value, sizeof(value), "%s", gGatekeeper);
+
+ ast_cli(a->fd, "%-20s%s\n", "Gatekeeper:", value);
+ ast_cli(a->fd, "%-20s%s\n", "H.323 LogFile:", gLogFile);
+ ast_cli(a->fd, "%-20s%s\n", "Context:", gContext);
+ ast_cli(a->fd, "%-20s%s\n", "Capability:", ast_getformatname_multiple(value, sizeof(value), gCapability));
+ ast_cli(a->fd, "%-20s", "DTMF Mode: ");
+ if (gDTMFMode & H323_DTMF_RFC2833)
+ ast_cli(a->fd, "%s\n", "rfc2833");
+ else if (gDTMFMode & H323_DTMF_Q931)
+ ast_cli(a->fd, "%s\n", "q931keypad");
+ else if (gDTMFMode & H323_DTMF_H245ALPHANUMERIC)
+ ast_cli(a->fd, "%s\n", "h245alphanumeric");
+ else if (gDTMFMode & H323_DTMF_H245SIGNAL)
+ ast_cli(a->fd, "%s\n", "h245signal");
+ else
+ ast_cli(a->fd, "%s\n", "unknown");
+ ast_cli(a->fd, "%-20s%s\n", "AccountCode: ", gAccountcode);
+ ast_cli(a->fd, "%-20s%s\n", "AMA flags: ", ast_cdr_flags2str(gAMAFLAGS));
+
+ pAlias = gAliasList;
+ if (pAlias)
+ ast_cli(a->fd, "%-20s\n", "Aliases: ");
+ while (pAlias) {
+ pAliasNext = pAlias->next;
+ if (pAliasNext) {
+ ast_cli(a->fd, "\t%-30s\t%-30s\n", pAlias->value, pAliasNext->value);
+ pAlias = pAliasNext->next;
+ } else {
+ ast_cli(a->fd, "\t%-30s\n", pAlias->value);
+ pAlias = pAlias->next;
+ }
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_ooh323[] = {
+ AST_CLI_DEFINE(handle_cli_ooh323_set_debug, "Enable/Disable OOH323 debugging"),
+ AST_CLI_DEFINE(handle_cli_ooh323_show_config, "Show details on global configuration of H.323 channel driver"),
+ AST_CLI_DEFINE(handle_cli_ooh323_show_peer, "Show details on specific OOH323 peer"),
+ AST_CLI_DEFINE(handle_cli_ooh323_show_peers, "Show defined OOH323 peers"),
+ AST_CLI_DEFINE(handle_cli_ooh323_show_user, "Show details on specific OOH323 user"),
+ AST_CLI_DEFINE(handle_cli_ooh323_show_users, "Show defined OOH323 users"),
+};
+
+static int load_module(void)
+{
+ int res;
+ struct ooAliases * pNewAlias = NULL;
+ struct ooh323_peer *peer = NULL;
+ OOH225MsgCallbacks h225Callbacks = {0, 0, 0, 0};
+
+ OOH323CALLBACKS h323Callbacks = {
+ .onNewCallCreated = onNewCallCreated,
+ .onAlerting = onAlerting,
+ .onIncomingCall = NULL,
+ .onOutgoingCall = NULL,
+ .onCallEstablished = onCallEstablished,
+ .onCallCleared = onCallCleared,
+ .openLogicalChannels = NULL,
+ .onReceivedDTMF = &ooh323_onReceivedDigit
+ };
+
+ ast_log(LOG_NOTICE,
+ "---------------------------------------------------------------------------------\n"
+ "--- ******* IMPORTANT NOTE ***********\n"
+ "---\n"
+ "--- This module is currently unsupported. Use it at your own risk.\n"
+ "---\n"
+ "---------------------------------------------------------------------------------\n");
+
+ h225Callbacks.onReceivedSetup = &ooh323_onReceivedSetup;
+
+ userl.users = NULL;
+ ast_mutex_init(&userl.lock);
+ peerl.peers = NULL;
+ ast_mutex_init(&peerl.lock);
+
+#if 0
+ ast_register_atexit(&ast_ooh323c_exit);
+#endif
+
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+ }
+ if (!(io = io_context_create())) {
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+ }
+
+
+ if (!(res = reload_config(0))) {
+ /* Make sure we can register our OOH323 channel type */
+ if (ast_channel_register(&ooh323_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ return 0;
+ }
+ ast_rtp_proto_register(&ooh323_rtp);
+ ast_cli_register_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry));
+
+ /* fire up the H.323 Endpoint */
+ if (OO_OK != ooH323EpInitialize(OO_CALLMODE_AUDIOCALL, gLogFile)) {
+ ast_log(LOG_ERROR, "Failed to initialize OOH323 endpoint-OOH323 Disabled\n");
+ return 1;
+ }
+
+ if (gIsGateway)
+ ooH323EpSetAsGateway();
+
+ ooH323EpDisableAutoAnswer();
+ ooH323EpSetH225MsgCallbacks(h225Callbacks);
+ ooH323EpSetTraceLevel(OOTRCLVLDBGC);
+ ooH323EpSetLocalAddress(gIP, gPort);
+ ooH323EpSetCallerID(gCallerID);
+
+ /* Set aliases if any */
+ for (pNewAlias = gAliasList; pNewAlias; pNewAlias = pNewAlias->next) {
+ switch (pNewAlias->type) {
+ case T_H225AliasAddress_h323_ID:
+ ooH323EpAddAliasH323ID(pNewAlias->value);
+ break;
+ case T_H225AliasAddress_dialedDigits:
+ ooH323EpAddAliasDialedDigits(pNewAlias->value);
+ break;
+ case T_H225AliasAddress_email_ID:
+ ooH323EpAddAliasEmailID(pNewAlias->value);
+ break;
+ }
+ }
+
+ ast_mutex_lock(&peerl.lock);
+ peer = peerl.peers;
+ while (peer) {
+ if (peer->h323id)
+ ooH323EpAddAliasH323ID(peer->h323id);
+ if (peer->email)
+ ooH323EpAddAliasEmailID(peer->email);
+ if (peer->e164)
+ ooH323EpAddAliasDialedDigits(peer->e164);
+ if (peer->url)
+ ooH323EpAddAliasURLID(peer->url);
+ peer = peer->next;
+ }
+ ast_mutex_unlock(&peerl.lock);
+
+
+ if (gMediaWaitForConnect)
+ ooH323EpEnableMediaWaitForConnect();
+ else
+ ooH323EpDisableMediaWaitForConnect();
+
+ /* Fast start and tunneling options */
+ if (gFastStart)
+ ooH323EpEnableFastStart();
+ else
+ ooH323EpDisableFastStart();
+
+ if (!gTunneling)
+ ooH323EpDisableH245Tunneling();
+
+ /* Gatekeeper */
+ if (gRasGkMode == RasUseSpecificGatekeeper)
+ ooGkClientInit(gRasGkMode, gGatekeeper, 0);
+ else if (gRasGkMode == RasDiscoverGatekeeper)
+ ooGkClientInit(gRasGkMode, 0, 0);
+
+ /* Register callbacks */
+ ooH323EpSetH323Callbacks(h323Callbacks);
+
+ /* Add endpoint capabilities */
+ if (ooh323c_set_capability(&gPrefs, gCapability, gDTMFMode) < 0) {
+ ast_log(LOG_ERROR, "Capabilities failure for OOH323. OOH323 Disabled.\n");
+ return 1;
+ }
+
+
+ /* Create H.323 listener */
+ if (ooCreateH323Listener() != OO_OK) {
+ ast_log(LOG_ERROR, "OOH323 Listener Creation failure. OOH323 DISABLED\n");
+
+ ooH323EpDestroy();
+ return 1;
+ }
+
+ if (ooh323c_start_stack_thread() < 0) {
+ ast_log(LOG_ERROR, "Failed to start OOH323 stack thread. OOH323 DISABLED\n");
+ ooH323EpDestroy();
+ return 1;
+ }
+ /* And start the monitor for the first time */
+ restart_monitor();
+ }
+
+ return 0;
+}
+
+
+static void *do_monitor(void *data)
+{
+ int res;
+ int reloading;
+ struct ooh323_pvt *h323 = NULL;
+ time_t t;
+
+ for (;;) {
+ struct ooh323_pvt *h323_next;
+ /* Check for a reload request */
+ ast_mutex_lock(&h323_reload_lock);
+ reloading = h323_reloading;
+ h323_reloading = 0;
+ ast_mutex_unlock(&h323_reload_lock);
+ if (reloading) {
+ ast_verb(1, "Reloading H.323\n");
+ ooh323_do_reload();
+ }
+ /* Check for interfaces needing to be killed */
+ ast_mutex_lock(&iflock);
+ time(&t);
+ h323 = iflist;
+ while (h323) {
+ h323_next = h323->next;
+
+ /* TODO: Need to add rtptimeout keepalive support */
+ if (ast_test_flag(h323, H323_NEEDDESTROY)) {
+ ooh323_destroy (h323);
+ }
+ h323 = h323_next;
+ }
+ ast_mutex_unlock(&iflock);
+ pthread_testcancel();
+ /* Wait for sched or io */
+ res = ast_sched_wait(sched);
+ if ((res < 0) || (res > 1000)) {
+ res = 1000;
+ }
+ res = ast_io_wait(io, res);
+ pthread_testcancel();
+ ast_mutex_lock(&monlock);
+ if (res >= 0) {
+ ast_sched_runq(sched);
+ }
+ ast_mutex_unlock(&monlock);
+ }
+ /* Never reached */
+ return NULL;
+}
+
+int restart_monitor(void)
+{
+ pthread_attr_t attr;
+
+ /* If we're supposed to be stopped -- stay stopped */
+ if (monitor_thread == AST_PTHREADT_STOP)
+ return 0;
+ if (ast_mutex_lock(&monlock)) {
+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
+ return -1;
+ }
+ if (monitor_thread == pthread_self()) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_WARNING, "Cannot kill myself\n");
+ return -1;
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ /* Wake up the thread */
+ pthread_kill(monitor_thread, SIGURG);
+ } else {
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ /* Start a new monitor */
+ if (ast_pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
+ ast_mutex_unlock(&monlock);
+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+ return -1;
+ }
+ }
+ ast_mutex_unlock(&monlock);
+ return 0;
+}
+
+
+
+int ooh323_destroy(struct ooh323_pvt *p)
+{
+ /* NOTE: Assumes iflock already acquired */
+ struct ooh323_pvt *prev = NULL, *cur = NULL;
+
+
+ if (gH323Debug) {
+ ast_verbose("--- ooh323_destroy \n");
+
+ if (p)
+ ast_verbose(" Destroying %s\n", p->username);
+ }
+
+ cur = iflist;
+ while (cur) {
+ if (cur == p) { break; }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur) {
+ ast_mutex_lock(&cur->lock);
+ if (prev)
+ prev->next = cur->next;
+ else
+ iflist = cur->next;
+
+ if (cur->callToken) {
+ free(cur->callToken);
+ cur->callToken = 0;
+ }
+
+ if (cur->username) {
+ free(cur->username);
+ cur->username = 0;
+ }
+
+ if (cur->host) {
+ free(cur->host);
+ cur->host = 0;
+ }
+
+ if (cur->callerid_name) {
+ free(cur->callerid_name);
+ cur->callerid_name = 0;
+ }
+
+ if (cur->callerid_num) {
+ free(cur->callerid_num);
+ cur->callerid_num = 0;
+ }
+
+
+ if (cur->rtp) {
+ ast_rtp_destroy(cur->rtp);
+ cur->rtp = 0;
+ }
+
+ /* Unlink us from the owner if we have one */
+ if (cur->owner) {
+ ast_channel_lock(cur->owner);
+ ast_debug(1, "Detaching from %s\n", cur->owner->name);
+ cur->owner->tech_pvt = NULL;
+ ast_channel_unlock(cur->owner);
+ cur->owner = NULL;
+ }
+
+ if (cur->vad) {
+ ast_dsp_free(cur->vad);
+ cur->vad = NULL;
+ }
+ ast_mutex_unlock(&cur->lock);
+ ast_mutex_destroy(&cur->lock);
+
+ free(cur);
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ ooh323_destroy\n");
+
+ return 0;
+}
+
+int delete_peers()
+{
+ struct ooh323_peer *cur = NULL, *prev = NULL;
+ ast_mutex_lock(&peerl.lock);
+ cur = peerl.peers;
+ while (cur) {
+ prev = cur;
+ cur = cur->next;
+
+ ast_mutex_destroy(&prev->lock);
+ if (prev->h323id)
+ free(prev->h323id);
+ if (prev->email)
+ free(prev->email);
+ if (prev->url)
+ free(prev->url);
+ if (prev->e164)
+ free(prev->e164);
+ free(prev);
+
+ if (cur == peerl.peers) {
+ break;
+ }
+ }
+ peerl.peers = NULL;
+ ast_mutex_unlock(&peerl.lock);
+ return 0;
+}
+
+int delete_users()
+{
+ struct ooh323_user *cur = NULL, *prev = NULL;
+ ast_mutex_lock(&userl.lock);
+ cur = userl.users;
+ while (cur) {
+ prev = cur;
+ cur = cur->next;
+ ast_mutex_destroy(&prev->lock);
+ free(prev);
+ if (cur == userl.users) {
+ break;
+ }
+ }
+ userl.users = NULL;
+ ast_mutex_unlock(&userl.lock);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ struct ooh323_pvt *p;
+ struct ooAliases *cur = NULL, *prev = NULL;
+
+ if (gH323Debug) {
+ ast_verbose("--- ooh323 unload_module \n");
+ }
+ /* First, take us out of the channel loop */
+ ast_cli_unregister_multiple(cli_ooh323, sizeof(cli_ooh323) / sizeof(struct ast_cli_entry));
+ ast_rtp_proto_unregister(&ooh323_rtp);
+ ast_channel_unregister(&ooh323_tech);
+
+#if 0
+ ast_unregister_atexit(&ast_ooh323c_exit);
+#endif
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - hanging up all interfaces\n");
+ }
+ if (!ast_mutex_lock(&iflock)) {
+ /* Hangup all interfaces if they have an owner */
+ p = iflist;
+ while (p) {
+ if (p->owner) {
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ }
+ p = p->next;
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ return -1;
+ }
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - stopping monitor thread\n");
+ }
+ if (monitor_thread != AST_PTHREADT_NULL) {
+ if (!ast_mutex_lock(&monlock)) {
+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
+ pthread_cancel(monitor_thread);
+ pthread_kill(monitor_thread, SIGURG);
+ pthread_join(monitor_thread, NULL);
+ }
+ monitor_thread = AST_PTHREADT_STOP;
+ ast_mutex_unlock(&monlock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+ return -1;
+ }
+ }
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - stopping stack thread\n");
+ }
+ ooh323c_stop_stack_thread();
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - freeing up memory used by interfaces\n");
+ }
+ if (!ast_mutex_lock(&iflock)) {
+ struct ooh323_pvt *pl;
+
+ /* Destroy all the interfaces and free their memory */
+ p = iflist;
+ while (p) {
+ pl = p;
+ p = p->next;
+ /* Free associated memory */
+ ooh323_destroy(pl);
+ }
+ iflist = NULL;
+ ast_mutex_unlock(&iflock);
+ } else {
+ ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+ return -1;
+ }
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - deleting users\n");
+ }
+ delete_users();
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - deleting peers\n");
+ }
+ delete_peers();
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module - Freeing up alias list\n");
+ }
+ cur = gAliasList;
+ while (cur) {
+ prev = cur;
+ cur = cur->next;
+ free(prev->value);
+ free(prev);
+ }
+ gAliasList = NULL;
+
+
+ if (gH323Debug) {
+ ast_verbose(" unload_module- destroying OOH323 endpoint \n");
+ }
+ ooH323EpDestroy();
+
+ if (gH323Debug) {
+ ast_verbose("+++ ooh323 unload_module \n");
+ }
+
+ return 0;
+}
+
+
+
+static enum ast_rtp_get_result ooh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct ooh323_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = (struct ooh323_pvt *) chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = p->rtp;
+
+ if (!(p->rtp)) {
+ return AST_RTP_GET_FAILED;
+ }
+ res = AST_RTP_TRY_NATIVE;
+
+ return res;
+}
+
+static enum ast_rtp_get_result ooh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
+{
+ struct ooh323_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = (struct ooh323_pvt *) chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = p->vrtp;
+
+ if (!(p->rtp)) {
+ return AST_RTP_GET_FAILED;
+ }
+ res = AST_RTP_TRY_NATIVE;
+
+ return res;
+}
+
+
+int ooh323_update_capPrefsOrderForCall
+ (ooCallData *call, struct ast_codec_pref *prefs)
+{
+ int i = 0;
+ int codec = ast_codec_pref_index(prefs, i);
+
+ ooResetCapPrefs(call);
+ while (codec) {
+ ooAppendCapToCapPrefs(call, ooh323_convertAsteriskCapToH323Cap(codec));
+ codec = ast_codec_pref_index(prefs, ++i);
+ }
+
+ return 0;
+}
+
+
+int ooh323_convertAsteriskCapToH323Cap(int cap)
+{
+ char formats[512];
+ switch (cap) {
+ case AST_FORMAT_ULAW:
+ return OO_G711ULAW64K;
+ case AST_FORMAT_ALAW:
+ return OO_G711ALAW64K;
+ case AST_FORMAT_GSM:
+ return OO_GSMFULLRATE;
+ case AST_FORMAT_G729A:
+ return OO_G729A;
+ case AST_FORMAT_G723_1:
+ return OO_G7231;
+ case AST_FORMAT_H263:
+ return OO_H263VIDEO;
+ default:
+ ast_log(LOG_NOTICE, "Don't know how to deal with mode %s\n",
+ ast_getformatname_multiple(formats, sizeof(formats), cap));
+ return -1;
+ }
+}
+
+static int ooh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp,
+ struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+{
+ /* XXX Deal with Video */
+ struct ooh323_pvt *p;
+ struct sockaddr_in them;
+ struct sockaddr_in us;
+ int mode;
+
+ if (gH323Debug)
+ ast_verbose("--- ooh323_set_peer - %s\n", chan->name);
+
+ if (!rtp) {
+ return 0;
+ }
+
+ mode = ooh323_convertAsteriskCapToH323Cap(chan->writeformat);
+ p = (struct ooh323_pvt *) chan->tech_pvt;
+ if (!p) {
+ ast_log(LOG_ERROR, "No Private Structure, this is bad\n");
+ return -1;
+ }
+ ast_rtp_get_peer(rtp, &them);
+ ast_rtp_get_us(rtp, &us);
+ return 0;
+}
+
+
+
+
+int configure_local_rtp(struct ooh323_pvt *p, ooCallData *call)
+{
+ struct sockaddr_in us;
+ ooMediaInfo mediaInfo;
+ int x, format = 0;
+
+ if (gH323Debug)
+ ast_verbose("--- configure_local_rtp\n");
+
+ if (p->rtp) {
+ ast_rtp_codec_setpref(p->rtp, &p->prefs);
+ }
+
+ /* figure out our local RTP port and tell the H.323 stack about it*/
+ ast_rtp_get_us(p->rtp, &us);
+
+ ast_copy_string(mediaInfo.lMediaIP, ast_inet_ntoa(us.sin_addr), sizeof(mediaInfo.lMediaIP));
+ mediaInfo.lMediaPort = ntohs(us.sin_port);
+ mediaInfo.lMediaCntrlPort = mediaInfo.lMediaPort +1;
+ for (x = 0; 0 != (format = ast_codec_pref_index(&p->prefs, x)); x++) {
+ strcpy(mediaInfo.dir, "transmit");
+ mediaInfo.cap = ooh323_convertAsteriskCapToH323Cap(format);
+ ooAddMediaInfo(call, mediaInfo);
+ strcpy(mediaInfo.dir, "receive");
+ ooAddMediaInfo(call, mediaInfo);
+ if (mediaInfo.cap == OO_G729A) {
+ strcpy(mediaInfo.dir, "transmit");
+ mediaInfo.cap = OO_G729;
+ ooAddMediaInfo(call, mediaInfo);
+ strcpy(mediaInfo.dir, "receive");
+ ooAddMediaInfo(call, mediaInfo);
+ }
+ }
+
+ if (gH323Debug)
+ ast_verbose("+++ configure_local_rtp\n");
+
+ return 1;
+}
+
+void setup_rtp_connection(ooCallData *call, const char *remoteIp,
+ int remotePort)
+{
+ struct ooh323_pvt *p = NULL;
+ struct sockaddr_in them;
+
+ if (gH323Debug)
+ ast_verbose("--- setup_rtp_connection\n");
+
+ /* Find the call or allocate a private structure if call not found */
+ p = find_call(call);
+
+ if (!p) {
+ ast_log(LOG_ERROR, "Something is wrong: rtp\n");
+ return;
+ }
+
+ them.sin_family = AF_INET;
+ them.sin_addr.s_addr = inet_addr(remoteIp); /* only works for IPv4 */
+ them.sin_port = htons(remotePort);
+ ast_rtp_set_peer(p->rtp, &them);
+
+ if (gH323Debug) {
+ ast_verbose("+++ setup_rtp_connection\n");
+ }
+
+ return;
+}
+
+void close_rtp_connection(ooCallData *call)
+{
+ struct ooh323_pvt *p = NULL;
+
+ if (gH323Debug) {
+ ast_verbose("--- close_rtp_connection\n");
+ }
+
+ p = find_call(call);
+ if (!p) {
+ ast_log(LOG_ERROR, "Couldn't find matching call to close rtp connection\n");
+ return;
+ }
+ ast_mutex_lock(&p->lock);
+ if (p->rtp) {
+ ast_rtp_stop(p->rtp);
+ }
+ ast_mutex_unlock(&p->lock);
+
+ if (gH323Debug) {
+ ast_verbose("+++ close_rtp_connection\n");
+ }
+
+ return;
+}
+
+
+int update_our_aliases(ooCallData *call, struct ooh323_pvt *p)
+{
+ int updated = -1;
+ ooAliases *psAlias = NULL;
+
+ if (!call->ourAliases)
+ return updated;
+ for (psAlias = call->ourAliases; psAlias; psAlias = psAlias->next) {
+ if (psAlias->type == T_H225AliasAddress_h323_ID) {
+ ast_copy_string(p->callee_h323id, psAlias->value, sizeof(p->callee_h323id));
+ updated = 1;
+ }
+ if (psAlias->type == T_H225AliasAddress_dialedDigits) {
+ ast_copy_string(p->callee_dialedDigits, psAlias->value, sizeof(p->callee_dialedDigits));
+ updated = 1;
+ }
+ if (psAlias->type == T_H225AliasAddress_url_ID) {
+ ast_copy_string(p->callee_url, psAlias->value, sizeof(p->callee_url));
+ updated = 1;
+ }
+ if (psAlias->type == T_H225AliasAddress_email_ID) {
+ ast_copy_string(p->callee_email, psAlias->value, sizeof(p->callee_email));
+ updated = 1;
+ }
+ }
+ return updated;
+}
+
+struct ast_frame *ooh323_rtp_read(struct ast_channel *ast, struct ooh323_pvt *p)
+{
+ /* Retrieve audio/etc from channel. Assumes p->lock is already held. */
+ struct ast_frame *f;
+ static struct ast_frame null_frame = { AST_FRAME_NULL, };
+ switch (ast->fdno) {
+ case 0:
+ f = ast_rtp_read(p->rtp); /* RTP Audio */
+ break;
+ case 1:
+ f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */
+ break;
+ case 2:
+ f = ast_rtp_read(p->vrtp); /* RTP Video */
+ break;
+ case 3:
+ f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */
+ break;
+ default:
+ f = &null_frame;
+ }
+ /* Don't send RFC2833 if we're not supposed to */
+ if (f && (f->frametype == AST_FRAME_DTMF) && !(p->dtmfmode & H323_DTMF_RFC2833)) {
+ return &null_frame;
+ }
+ if (p->owner) {
+ /* We already hold the channel lock */
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass != p->owner->nativeformats) {
+ ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
+ p->owner->nativeformats = f->subclass;
+ ast_set_read_format(p->owner, p->owner->readformat);
+ ast_set_write_format(p->owner, p->owner->writeformat);
+ }
+ if ((p->dtmfmode & H323_DTMF_INBAND) && p->vad) {
+ f = ast_dsp_process(p->owner, p->vad, f);
+ if (f && (f->frametype == AST_FRAME_DTMF)) {
+ ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
+ }
+ }
+ }
+ }
+ return f;
+}
+
+
+int ooh323_convert_hangupcause_asteriskToH323(int cause)
+{
+ switch (cause) {
+ case AST_CAUSE_CALL_REJECTED:
+ return OO_REASON_REMOTE_REJECTED;
+ case AST_CAUSE_UNALLOCATED:
+ return OO_REASON_NOUSER;
+ case AST_CAUSE_BUSY:
+ return OO_REASON_REMOTE_BUSY;
+ case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL:
+ return OO_REASON_NOCOMMON_CAPABILITIES;
+ case AST_CAUSE_CONGESTION:
+ return OO_REASON_REMOTE_BUSY;
+ case AST_CAUSE_NO_ANSWER:
+ return OO_REASON_REMOTE_NOANSWER;
+ case AST_CAUSE_NORMAL:
+ return OO_REASON_REMOTE_CLEARED;
+ case AST_CAUSE_FAILURE:
+ default:
+ return OO_REASON_UNKNOWN;
+ }
+
+ return 0;
+}
+
+int ooh323_convert_hangupcause_h323ToAsterisk(int cause)
+{
+ switch (cause) {
+ case OO_REASON_REMOTE_REJECTED:
+ return AST_CAUSE_CALL_REJECTED;
+ case OO_REASON_NOUSER:
+ return AST_CAUSE_UNALLOCATED;
+ case OO_REASON_REMOTE_BUSY:
+ case OO_REASON_LOCAL_BUSY:
+ return AST_CAUSE_BUSY;
+ case OO_REASON_NOCOMMON_CAPABILITIES: /* No codecs approved */
+ return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ case OO_REASON_REMOTE_CONGESTED:
+ case OO_REASON_LOCAL_CONGESTED:
+ return AST_CAUSE_CONGESTION;
+ case OO_REASON_REMOTE_NOANSWER:
+ return AST_CAUSE_NO_ANSWER;
+ case OO_REASON_UNKNOWN:
+ case OO_REASON_INVALIDMESSAGE:
+ case OO_REASON_TRANSPORTFAILURE:
+ return AST_CAUSE_FAILURE;
+ case OO_REASON_REMOTE_CLEARED:
+ return AST_CAUSE_NORMAL;
+ default:
+ return AST_CAUSE_NORMAL;
+ }
+ /* Never reached */
+ return 0;
+}
+
+#if 0
+void ast_ooh323c_exit()
+{
+ ooGkClientDestroy();
+}
+#endif
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Objective Systems H323 Channel");
diff --git a/addons/chan_ooh323.h b/addons/chan_ooh323.h
new file mode 100644
index 000000000..bce62bd11
--- /dev/null
+++ b/addons/chan_ooh323.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+#ifndef _OO_CHAN_H323_H_
+#define _OO_CHAN_H323_H_
+
+#include <asterisk.h>
+#undef PACKAGE_NAME
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#undef PACKAGE_STRING
+#undef PACKAGE_BUGREPORT
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/signal.h>
+
+#include <asterisk/lock.h>
+#include <asterisk/channel.h>
+#include <asterisk/config.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/utils.h>
+#include <asterisk/options.h>
+#include <asterisk/sched.h>
+#include <asterisk/io.h>
+#include <asterisk/causes.h>
+#include <asterisk/rtp.h>
+#include <asterisk/acl.h>
+#include <asterisk/callerid.h>
+#include <asterisk/file.h>
+#include <asterisk/cli.h>
+#include <asterisk/app.h>
+#include <asterisk/musiconhold.h>
+#include <asterisk/manager.h>
+#include <asterisk/dsp.h>
+#include <asterisk/stringfields.h>
+
+#include "ootypes.h"
+#include "ooCapability.h"
+#include "oochannels.h"
+#include "ooh323ep.h"
+#include "ooh323cDriver.h"
+#include "ooCalls.h"
+#include "ooq931.h"
+#include "ooStackCmds.h"
+#include "ooCapability.h"
+#include "ooGkClient.h"
+
+
+struct ooh323_pvt;
+struct ooh323_user;
+struct ooh323_peer;
+/* Helper functions */
+struct ooh323_user *find_user(const char * name, const char *ip);
+struct ooh323_peer *find_peer(const char * name, int port);
+void ooh323_delete_peer(struct ooh323_peer *peer);
+
+int delete_users(void);
+int delete_peers(void);
+
+int ooh323_destroy(struct ooh323_pvt *p);
+int reload_config(int reload);
+int restart_monitor(void);
+
+int configure_local_rtp(struct ooh323_pvt *p, ooCallData* call);
+void setup_rtp_connection(ooCallData *call, const char *remoteIp,
+ int remotePort);
+void close_rtp_connection(ooCallData *call);
+struct ast_frame *ooh323_rtp_read
+ (struct ast_channel *ast, struct ooh323_pvt *p);
+
+void ooh323_set_write_format(ooCallData *call, int fmt);
+void ooh323_set_read_format(ooCallData *call, int fmt);
+
+int ooh323_update_capPrefsOrderForCall
+ (ooCallData *call, struct ast_codec_pref *prefs);
+
+int ooh323_convertAsteriskCapToH323Cap(int cap);
+
+int ooh323_convert_hangupcause_asteriskToH323(int cause);
+int ooh323_convert_hangupcause_h323ToAsterisk(int cause);
+int update_our_aliases(ooCallData *call, struct ooh323_pvt *p);
+
+/* h323 msg callbacks */
+int ooh323_onReceivedSetup(ooCallData *call, Q931Message *pmsg);
+int ooh323_onReceivedDigit(OOH323CallData *call, const char* digit);
+#endif
diff --git a/addons/format_mp3.c b/addons/format_mp3.c
new file mode 100644
index 000000000..2c27243e2
--- /dev/null
+++ b/addons/format_mp3.c
@@ -0,0 +1,336 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Anthony Minessale <anthmct@yahoo.com>
+ *
+ * Derived from other asterisk sound formats by
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * Thanks to mpglib from http://www.mpg123.org/
+ * and Chris Stenton [jacs@gnome.co.uk]
+ * for coding the ability to play stereo and non-8khz files
+
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief MP3 Format Handler
+ * \ingroup formats
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "mp3/mpg123.h"
+#include "mp3/mpglib.h"
+
+#include "asterisk/module.h"
+#include "asterisk/mod_format.h"
+#include "asterisk/logger.h"
+
+#define MP3_BUFLEN 320
+#define MP3_SCACHE 16384
+#define MP3_DCACHE 8192
+
+struct mp3_private {
+ char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
+ char empty; /* Empty character */
+ int lasttimeout;
+ int maxlen;
+ struct timeval last;
+ struct mpstr mp;
+ char sbuf[MP3_SCACHE];
+ char dbuf[MP3_DCACHE];
+ int buflen;
+ int sbuflen;
+ int dbuflen;
+ int dbufoffset;
+ int sbufoffset;
+ int lastseek;
+ int offset;
+ long seek;
+};
+
+static const char name[] = "mp3";
+
+#define BLOCKSIZE 160
+#define OUTSCALE 4096
+
+#define GAIN -4 /* 2^GAIN is the multiple to increase the volume by */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htoll(b) (b)
+#define htols(b) (b)
+#define ltohl(b) (b)
+#define ltohs(b) (b)
+#else
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htoll(b) \
+ (((((b) ) & 0xFF) << 24) | \
+ ((((b) >> 8) & 0xFF) << 16) | \
+ ((((b) >> 16) & 0xFF) << 8) | \
+ ((((b) >> 24) & 0xFF) ))
+#define htols(b) \
+ (((((b) ) & 0xFF) << 8) | \
+ ((((b) >> 8) & 0xFF) ))
+#define ltohl(b) htoll(b)
+#define ltohs(b) htols(b)
+#else
+#error "Endianess not defined"
+#endif
+#endif
+
+
+static int mp3_open(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+
+ InitMP3(&p->mp, OUTSCALE);
+ p->dbuflen = 0;
+ s->fr.data.ptr = s->buf;
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_SLINEAR;
+ /* datalen will vary for each frame */
+ s->fr.src = name;
+ s->fr.mallocd = 0;
+ p->offset = 0;
+ return 0;
+}
+
+
+static void mp3_close(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+
+ ExitMP3(&p->mp);
+ return;
+}
+
+static int mp3_squeue(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+ int res=0;
+
+ p->lastseek = ftell(s->f);
+ p->sbuflen = fread(p->sbuf, 1, MP3_SCACHE, s->f);
+ if(p->sbuflen < 0) {
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", p->sbuflen, strerror(errno));
+ return -1;
+ }
+ res = decodeMP3(&p->mp,p->sbuf,p->sbuflen,p->dbuf,MP3_DCACHE,&p->dbuflen);
+ if(res != MP3_OK)
+ return -1;
+ p->sbuflen -= p->dbuflen;
+ p->dbufoffset = 0;
+ return 0;
+}
+
+static int mp3_dqueue(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+ int res=0;
+
+ if((res = decodeMP3(&p->mp,NULL,0,p->dbuf,MP3_DCACHE,&p->dbuflen)) == MP3_OK) {
+ p->sbuflen -= p->dbuflen;
+ p->dbufoffset = 0;
+ }
+ return res;
+}
+
+static int mp3_queue(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+ int res = 0, bytes = 0;
+
+ if(p->seek) {
+ ExitMP3(&p->mp);
+ InitMP3(&p->mp, OUTSCALE);
+ fseek(s->f, 0, SEEK_SET);
+ p->sbuflen = p->dbuflen = p->offset = 0;
+ while(p->offset < p->seek) {
+ if(mp3_squeue(s))
+ return -1;
+ while(p->offset < p->seek && ((res = mp3_dqueue(s))) == MP3_OK) {
+ for(bytes = 0 ; bytes < p->dbuflen ; bytes++) {
+ p->dbufoffset++;
+ p->offset++;
+ if(p->offset >= p->seek)
+ break;
+ }
+ }
+ if(res == MP3_ERR)
+ return -1;
+ }
+
+ p->seek = 0;
+ return 0;
+ }
+ if(p->dbuflen == 0) {
+ if(p->sbuflen) {
+ res = mp3_dqueue(s);
+ if(res == MP3_ERR)
+ return -1;
+ }
+ if(! p->sbuflen || res != MP3_OK) {
+ if(mp3_squeue(s))
+ return -1;
+ }
+
+ }
+
+ return 0;
+}
+
+static struct ast_frame *mp3_read(struct ast_filestream *s, int *whennext)
+{
+
+ struct mp3_private *p = s->_private;
+ int delay =0;
+ int save=0;
+
+ /* Send a frame from the file to the appropriate channel */
+
+ if(mp3_queue(s))
+ return NULL;
+
+ if(p->dbuflen) {
+ for(p->buflen=0; p->buflen < MP3_BUFLEN && p->buflen < p->dbuflen; p->buflen++) {
+ s->buf[p->buflen] = p->dbuf[p->buflen+p->dbufoffset];
+ p->sbufoffset++;
+ }
+ p->dbufoffset += p->buflen;
+ p->dbuflen -= p->buflen;
+
+ if(p->buflen < MP3_BUFLEN) {
+ if(mp3_queue(s))
+ return NULL;
+
+ for(save = p->buflen; p->buflen < MP3_BUFLEN; p->buflen++) {
+ s->buf[p->buflen] = p->dbuf[(p->buflen-save)+p->dbufoffset];
+ p->sbufoffset++;
+ }
+ p->dbufoffset += (MP3_BUFLEN - save);
+ p->dbuflen -= (MP3_BUFLEN - save);
+
+ }
+
+ }
+
+ p->offset += p->buflen;
+ delay = p->buflen/2;
+ s->fr.frametype = AST_FRAME_VOICE;
+ s->fr.subclass = AST_FORMAT_SLINEAR;
+ s->fr.offset = AST_FRIENDLY_OFFSET;
+ s->fr.datalen = p->buflen;
+ s->fr.data.ptr = s->buf;
+ s->fr.mallocd = 0;
+ s->fr.samples = delay;
+ *whennext = delay;
+ return &s->fr;
+}
+
+
+static int mp3_write(struct ast_filestream *fs, struct ast_frame *f)
+{
+ ast_log(LOG_ERROR,"I Can't write MP3 only read them.\n");
+ return -1;
+
+}
+
+
+static int mp3_seek(struct ast_filestream *s, off_t sample_offset, int whence)
+{
+ struct mp3_private *p = s->_private;
+ off_t min,max,cur;
+ long offset=0,samples;
+ samples = sample_offset * 2;
+
+ min = 0;
+ fseek(s->f, 0, SEEK_END);
+ max = ftell(s->f) * 100;
+ cur = p->offset;
+
+ if (whence == SEEK_SET)
+ offset = samples + min;
+ else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
+ offset = samples + cur;
+ else if (whence == SEEK_END)
+ offset = max - samples;
+ if (whence != SEEK_FORCECUR) {
+ offset = (offset > max)?max:offset;
+ }
+
+ p->seek = offset;
+ return p->seek;
+
+}
+
+static int mp3_rewrite(struct ast_filestream *s, const char *comment)
+{
+ ast_log(LOG_ERROR,"I Can't write MP3 only read them.\n");
+ return -1;
+}
+
+static int mp3_trunc(struct ast_filestream *s)
+{
+
+ ast_log(LOG_ERROR,"I Can't write MP3 only read them.\n");
+ return -1;
+}
+
+static off_t mp3_tell(struct ast_filestream *s)
+{
+ struct mp3_private *p = s->_private;
+
+ return p->offset/2;
+}
+
+static char *mp3_getcomment(struct ast_filestream *s)
+{
+ return NULL;
+}
+
+static const struct ast_format mp3_f = {
+ .name = "mp3",
+ .exts = "mp3",
+ .format = AST_FORMAT_SLINEAR,
+ .open = mp3_open,
+ .write = mp3_write,
+ .rewrite = mp3_rewrite,
+ .seek = mp3_seek,
+ .trunc = mp3_trunc,
+ .tell = mp3_tell,
+ .read = mp3_read,
+ .close = mp3_close,
+ .getcomment = mp3_getcomment,
+ .buf_size = MP3_BUFLEN + AST_FRIENDLY_OFFSET,
+ .desc_size = sizeof(struct mp3_private),
+};
+
+
+static int load_module(void)
+{
+ InitMP3Constants();
+ return ast_format_register(&mp3_f);
+}
+
+static int unload_module(void)
+{
+ return ast_format_unregister(name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "MP3 format [Any rate but 8000hz mono is optimal]");
diff --git a/addons/mp3/MPGLIB_README b/addons/mp3/MPGLIB_README
new file mode 100644
index 000000000..3c5f33749
--- /dev/null
+++ b/addons/mp3/MPGLIB_README
@@ -0,0 +1,39 @@
+MP3 library
+-----------
+Version 0.2
+
+This decoder is a 'light' version (thrown out all unnecessay parts)
+from the mpg123 package. I made this for a company.
+
+Currently only Layer3 is enabled to save some space. Layer1,2 isn't
+tested at all. The interface will not change significantly.
+A backport to the mpg123 package is planed.
+
+comiled and tested only on Solaris 2.6
+main.c contains a simple demo application for library.
+
+COPYING: you may use this source under GPL terms!
+
+PLEASE NOTE: This software may contain patented alogrithm (at least
+ patented in some countries). It may be not allowed to sell/use products
+ based on this source code in these countries. Check this out first!
+
+COPYRIGHT of MP3 music:
+ Please note, that the duplicating of copyrighted music without explicit
+ permission violates the rights of the owner.
+
+SENDING PATCHES:
+ Maybe I change the copyright policy (ie some kind of more free BSD licencse).
+ Please consider this when sending patches/changes.
+ I also want to have the freedom to sell the code to companies that
+ can not use the code under GPL. So, if you send me significant patches,
+ I need your explicit permission to do this. Of course, there will also
+ be the GPLed open source version of the 100% same code.
+ For the case you cannot accept this: the code is GPL, it's your freedom
+ to distribute your changes again under GPL.
+
+FEEDBACK:
+ I'm interessted to here from you, when you use this package as part
+ of another project.
+
+
diff --git a/addons/mp3/MPGLIB_TODO b/addons/mp3/MPGLIB_TODO
new file mode 100644
index 000000000..403711010
--- /dev/null
+++ b/addons/mp3/MPGLIB_TODO
@@ -0,0 +1,2 @@
+
+apply 'VBR' bugfix
diff --git a/addons/mp3/Makefile b/addons/mp3/Makefile
new file mode 100644
index 000000000..7cd0b58d1
--- /dev/null
+++ b/addons/mp3/Makefile
@@ -0,0 +1,24 @@
+MP3OBJS=common.o dct64_i386.o decode_ntom.o layer3.o tabinit.o interface.o
+
+ifeq ($(OSARCH),FreeBSD)
+OSVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk)
+CFLAGS+=$(if $(OSVERSION)<500016,-D_THREAD_SAFE)
+LIBS+=$(if $(OSVERSION)<502102,-lc_r,-pthread)
+INCLUDE+=-I/usr/local/include
+CFLAGS+=$(shell if [ -d /usr/local/include/spandsp ]; then echo "-I/usr/local/include/spandsp"; fi)
+endif # FreeBSD
+
+ifeq ($(OSARCH),NetBSD)
+CFLAGS+=-pthread
+INCLUDE+=-I/usr/local/include
+endif
+
+ifeq ($(OSARCH),OpenBSD)
+CFLAGS+=-pthread
+endif
+
+all: $(MP3OBJS)
+
+clean:
+ rm -f *.o *.so *~
+ rm -f .*.o.d
diff --git a/addons/mp3/README b/addons/mp3/README
new file mode 100644
index 000000000..9263da487
--- /dev/null
+++ b/addons/mp3/README
@@ -0,0 +1,24 @@
+This is a module for asterisk to play mp3 natively.
+They *SHOULD* be already at 8khz and *SHOULD* be mono.
+otherwise they will be consuming CPU alot more than need be.
+
+Convert them to 8k mono like this:
+lame -q -p --mp3input -a --preset 8 in.mp3 8kout.mp3
+
+just run
+# make clean install
+to install
+
+or
+# make clean autoload
+to install and autoload at the same time
+
+
+Comments or improvements
+Anthony Minessale <anthmct@yahoo.com>
+
+Donations Welcomed at paypal:jillkm3@yahoo.com
+
+
+
+
diff --git a/addons/mp3/common.c b/addons/mp3/common.c
new file mode 100644
index 000000000..d8db2a3d2
--- /dev/null
+++ b/addons/mp3/common.c
@@ -0,0 +1,267 @@
+#include <asterisk.h>
+#include <asterisk/logger.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "mpg123.h"
+#include "mpglib.h"
+
+struct parameter param = { 1 , 1 , 0 , 0 };
+
+int tabsel_123[2][3][16] = {
+ { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
+ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
+ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },
+
+ { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} }
+};
+
+long freqs[9] = { 44100, 48000, 32000,
+ 22050, 24000, 16000 ,
+ 11025 , 12000 , 8000 };
+
+
+#if 0
+static void get_II_stuff(struct frame *fr)
+{
+ static int translate[3][2][16] =
+ { { { 0,2,2,2,2,2,2,0,0,0,1,1,1,1,1,0 } ,
+ { 0,2,2,0,0,0,1,1,1,1,1,1,1,1,1,0 } } ,
+ { { 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 } ,
+ { 0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0 } } ,
+ { { 0,3,3,3,3,3,3,0,0,0,1,1,1,1,1,0 } ,
+ { 0,3,3,0,0,0,1,1,1,1,1,1,1,1,1,0 } } };
+
+ int table,sblim;
+ static struct al_table *tables[5] =
+ { alloc_0, alloc_1, alloc_2, alloc_3 , alloc_4 };
+ static int sblims[5] = { 27 , 30 , 8, 12 , 30 };
+
+ if(fr->lsf)
+ table = 4;
+ else
+ table = translate[fr->sampling_frequency][2-fr->stereo][fr->bitrate_index];
+ sblim = sblims[table];
+
+ fr->alloc = tables[table];
+ fr->II_sblimit = sblim;
+}
+#endif
+
+#define HDRCMPMASK 0xfffffd00
+
+#if 0
+int head_check(unsigned long head)
+{
+ if( (head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if(!((head>>17)&3))
+ return FALSE;
+ if( ((head>>12)&0xf) == 0xf)
+ return FALSE;
+ if( ((head>>10)&0x3) == 0x3 )
+ return FALSE;
+ return TRUE;
+}
+#endif
+
+
+/*
+ * the code a header and write the information
+ * into the frame structure
+ */
+int decode_header(struct frame *fr,unsigned long newhead)
+{
+ if( newhead & (1<<20) ) {
+ fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
+ fr->mpeg25 = 0;
+ }
+ else {
+ fr->lsf = 1;
+ fr->mpeg25 = 1;
+ }
+
+ fr->lay = 4-((newhead>>17)&3);
+ if( ((newhead>>10)&0x3) == 0x3) {
+ ast_log(LOG_WARNING,"Stream error\n");
+ return (0);
+ }
+ if(fr->mpeg25) {
+ fr->sampling_frequency = 6 + ((newhead>>10)&0x3);
+ }
+ else
+ fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);
+ fr->error_protection = ((newhead>>16)&0x1)^0x1;
+
+ if(fr->mpeg25) /* allow Bitrate change for 2.5 ... */
+ fr->bitrate_index = ((newhead>>12)&0xf);
+
+ fr->bitrate_index = ((newhead>>12)&0xf);
+ fr->padding = ((newhead>>9)&0x1);
+ fr->extension = ((newhead>>8)&0x1);
+ fr->mode = ((newhead>>6)&0x3);
+ fr->mode_ext = ((newhead>>4)&0x3);
+ fr->copyright = ((newhead>>3)&0x1);
+ fr->original = ((newhead>>2)&0x1);
+ fr->emphasis = newhead & 0x3;
+
+ fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
+
+ if(!fr->bitrate_index)
+ {
+ ast_log(LOG_WARNING,"Free format not supported.\n");
+ return (0);
+ }
+
+ switch(fr->lay)
+ {
+ case 1:
+#if 0
+ fr->do_layer = do_layer1;
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
+ (fr->mode_ext<<2)+4 : 32;
+ fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize = ((fr->framesize+fr->padding)<<2)-4;
+#else
+ ast_log(LOG_WARNING,"Layer 1 not supported!\n");
+#endif
+ break;
+ case 2:
+#if 0
+ fr->do_layer = do_layer2;
+ get_II_stuff(fr);
+ fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ?
+ (fr->mode_ext<<2)+4 : fr->II_sblimit;
+ fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize += fr->padding - 4;
+#else
+ ast_log(LOG_WARNING,"Layer 2 not supported!\n");
+#endif
+ break;
+ case 3:
+#if 0
+ fr->do_layer = do_layer3;
+ if(fr->lsf)
+ ssize = (fr->stereo == 1) ? 9 : 17;
+ else
+ ssize = (fr->stereo == 1) ? 17 : 32;
+#endif
+
+#if 0
+ if(fr->error_protection)
+ ssize += 2;
+#endif
+ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
+ fr->framesize = fr->framesize + fr->padding - 4;
+ break;
+ default:
+ ast_log(LOG_WARNING,"Sorry, unknown layer type.\n");
+ return (0);
+ }
+ return 1;
+}
+
+#if 0
+void print_header(struct frame *fr)
+{
+ static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ ast_log(LOG_WARNING,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF : %d\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],freqs[fr->sampling_frequency],
+ modes[fr->mode],fr->mode_ext,fr->framesize+4);
+ ast_log(LOG_WARNING,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n",
+ fr->stereo,fr->copyright?"Yes":"No",
+ fr->original?"Yes":"No",fr->error_protection?"Yes":"No",
+ fr->emphasis);
+ ast_log(LOG_WARNING,"Bitrate: %d Kbits/s, Extension value: %d\n",
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension);
+}
+
+void print_header_compact(struct frame *fr)
+{
+ static char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ ast_log(LOG_WARNING,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
+ freqs[fr->sampling_frequency], modes[fr->mode]);
+}
+
+#endif
+
+unsigned int getbits(struct mpstr *mp, int number_of_bits)
+{
+ unsigned long rval;
+
+ if(!number_of_bits)
+ return 0;
+
+ {
+ rval = (mp->worksample).wordpointer[0];
+ rval <<= 8;
+ rval |= (mp->worksample).wordpointer[1];
+ rval <<= 8;
+ rval |= (mp->worksample).wordpointer[2];
+ rval <<= (mp->worksample).bitindex;
+ rval &= 0xffffff;
+
+ (mp->worksample).bitindex += number_of_bits;
+
+ rval >>= (24-number_of_bits);
+
+ (mp->worksample).wordpointer += ((mp->worksample).bitindex>>3);
+ (mp->worksample).bitindex &= 7;
+ }
+ return rval;
+}
+
+unsigned int getbits_fast(struct mpstr *mp, int number_of_bits)
+{
+ unsigned long rval;
+
+ {
+ rval = (mp->worksample).wordpointer[0];
+ rval <<= 8;
+ rval |= (mp->worksample).wordpointer[1];
+ rval <<= (mp->worksample).bitindex;
+ rval &= 0xffff;
+ (mp->worksample).bitindex += number_of_bits;
+
+ rval >>= (16-number_of_bits);
+
+ (mp->worksample).wordpointer += ((mp->worksample).bitindex>>3);
+ (mp->worksample).bitindex &= 7;
+ }
+ return rval;
+}
+
+unsigned int get1bit(struct mpstr *mp)
+{
+ unsigned char rval;
+
+ rval = *((mp->worksample).wordpointer) << (mp->worksample).bitindex;
+
+ (mp->worksample).bitindex++;
+ (mp->worksample).wordpointer += ((mp->worksample).bitindex>>3);
+ (mp->worksample).bitindex &= 7;
+
+ return rval>>7;
+}
+
+
+
diff --git a/addons/mp3/dct64_i386.c b/addons/mp3/dct64_i386.c
new file mode 100644
index 000000000..c6b5d886c
--- /dev/null
+++ b/addons/mp3/dct64_i386.c
@@ -0,0 +1,335 @@
+
+/*
+ * Discrete Cosine Tansform (DCT) for subband synthesis
+ * optimized for machines with no auto-increment.
+ * The performance is highly compiler dependend. Maybe
+ * the dct64.c version for 'normal' processor may be faster
+ * even for Intel processors.
+ */
+
+#include "mpg123.h"
+
+/*
+ * the call via dct64 is a trick to force GCC to use
+ * (new) registers for the b1,b2 pointer to the bufs[xx] field
+ */
+void dct64(real *a,real *b,real *c);
+
+static void dct64_1(real *out0,real *out1,real *b1,real *b2,real *samples)
+{
+ {
+ register real *costab = pnts[0];
+
+ b1[0x00] = samples[0x00] + samples[0x1F];
+ b1[0x01] = samples[0x01] + samples[0x1E];
+ b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0];
+ b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1];
+
+ b1[0x02] = samples[0x02] + samples[0x1D];
+ b1[0x03] = samples[0x03] + samples[0x1C];
+ b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2];
+ b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3];
+
+ b1[0x04] = samples[0x04] + samples[0x1B];
+ b1[0x05] = samples[0x05] + samples[0x1A];
+ b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4];
+ b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5];
+
+ b1[0x06] = samples[0x06] + samples[0x19];
+ b1[0x07] = samples[0x07] + samples[0x18];
+ b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6];
+ b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7];
+
+ b1[0x08] = samples[0x08] + samples[0x17];
+ b1[0x09] = samples[0x09] + samples[0x16];
+ b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8];
+ b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9];
+
+ b1[0x0A] = samples[0x0A] + samples[0x15];
+ b1[0x0B] = samples[0x0B] + samples[0x14];
+ b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA];
+ b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB];
+
+ b1[0x0C] = samples[0x0C] + samples[0x13];
+ b1[0x0D] = samples[0x0D] + samples[0x12];
+ b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC];
+ b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD];
+
+ b1[0x0E] = samples[0x0E] + samples[0x11];
+ b1[0x0F] = samples[0x0F] + samples[0x10];
+ b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE];
+ b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF];
+
+ }
+
+
+ {
+ register real *costab = pnts[1];
+
+ b2[0x00] = b1[0x00] + b1[0x0F];
+ b2[0x01] = b1[0x01] + b1[0x0E];
+ b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0];
+ b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1];
+
+ b2[0x02] = b1[0x02] + b1[0x0D];
+ b2[0x03] = b1[0x03] + b1[0x0C];
+ b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2];
+ b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3];
+
+ b2[0x04] = b1[0x04] + b1[0x0B];
+ b2[0x05] = b1[0x05] + b1[0x0A];
+ b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4];
+ b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5];
+
+ b2[0x06] = b1[0x06] + b1[0x09];
+ b2[0x07] = b1[0x07] + b1[0x08];
+ b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6];
+ b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7];
+
+ /* */
+
+ b2[0x10] = b1[0x10] + b1[0x1F];
+ b2[0x11] = b1[0x11] + b1[0x1E];
+ b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0];
+ b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1];
+
+ b2[0x12] = b1[0x12] + b1[0x1D];
+ b2[0x13] = b1[0x13] + b1[0x1C];
+ b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2];
+ b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3];
+
+ b2[0x14] = b1[0x14] + b1[0x1B];
+ b2[0x15] = b1[0x15] + b1[0x1A];
+ b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4];
+ b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5];
+
+ b2[0x16] = b1[0x16] + b1[0x19];
+ b2[0x17] = b1[0x17] + b1[0x18];
+ b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6];
+ b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7];
+ }
+
+ {
+ register real *costab = pnts[2];
+
+ b1[0x00] = b2[0x00] + b2[0x07];
+ b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0];
+ b1[0x01] = b2[0x01] + b2[0x06];
+ b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1];
+ b1[0x02] = b2[0x02] + b2[0x05];
+ b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2];
+ b1[0x03] = b2[0x03] + b2[0x04];
+ b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3];
+
+ b1[0x08] = b2[0x08] + b2[0x0F];
+ b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0];
+ b1[0x09] = b2[0x09] + b2[0x0E];
+ b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1];
+ b1[0x0A] = b2[0x0A] + b2[0x0D];
+ b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2];
+ b1[0x0B] = b2[0x0B] + b2[0x0C];
+ b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3];
+
+ b1[0x10] = b2[0x10] + b2[0x17];
+ b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0];
+ b1[0x11] = b2[0x11] + b2[0x16];
+ b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1];
+ b1[0x12] = b2[0x12] + b2[0x15];
+ b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2];
+ b1[0x13] = b2[0x13] + b2[0x14];
+ b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3];
+
+ b1[0x18] = b2[0x18] + b2[0x1F];
+ b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0];
+ b1[0x19] = b2[0x19] + b2[0x1E];
+ b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1];
+ b1[0x1A] = b2[0x1A] + b2[0x1D];
+ b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2];
+ b1[0x1B] = b2[0x1B] + b2[0x1C];
+ b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3];
+ }
+
+ {
+ register real const cos0 = pnts[3][0];
+ register real const cos1 = pnts[3][1];
+
+ b2[0x00] = b1[0x00] + b1[0x03];
+ b2[0x03] = (b1[0x00] - b1[0x03]) * cos0;
+ b2[0x01] = b1[0x01] + b1[0x02];
+ b2[0x02] = (b1[0x01] - b1[0x02]) * cos1;
+
+ b2[0x04] = b1[0x04] + b1[0x07];
+ b2[0x07] = (b1[0x07] - b1[0x04]) * cos0;
+ b2[0x05] = b1[0x05] + b1[0x06];
+ b2[0x06] = (b1[0x06] - b1[0x05]) * cos1;
+
+ b2[0x08] = b1[0x08] + b1[0x0B];
+ b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0;
+ b2[0x09] = b1[0x09] + b1[0x0A];
+ b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1;
+
+ b2[0x0C] = b1[0x0C] + b1[0x0F];
+ b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0;
+ b2[0x0D] = b1[0x0D] + b1[0x0E];
+ b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1;
+
+ b2[0x10] = b1[0x10] + b1[0x13];
+ b2[0x13] = (b1[0x10] - b1[0x13]) * cos0;
+ b2[0x11] = b1[0x11] + b1[0x12];
+ b2[0x12] = (b1[0x11] - b1[0x12]) * cos1;
+
+ b2[0x14] = b1[0x14] + b1[0x17];
+ b2[0x17] = (b1[0x17] - b1[0x14]) * cos0;
+ b2[0x15] = b1[0x15] + b1[0x16];
+ b2[0x16] = (b1[0x16] - b1[0x15]) * cos1;
+
+ b2[0x18] = b1[0x18] + b1[0x1B];
+ b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0;
+ b2[0x19] = b1[0x19] + b1[0x1A];
+ b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1;
+
+ b2[0x1C] = b1[0x1C] + b1[0x1F];
+ b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0;
+ b2[0x1D] = b1[0x1D] + b1[0x1E];
+ b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1;
+ }
+
+ {
+ register real const cos0 = pnts[4][0];
+
+ b1[0x00] = b2[0x00] + b2[0x01];
+ b1[0x01] = (b2[0x00] - b2[0x01]) * cos0;
+ b1[0x02] = b2[0x02] + b2[0x03];
+ b1[0x03] = (b2[0x03] - b2[0x02]) * cos0;
+ b1[0x02] += b1[0x03];
+
+ b1[0x04] = b2[0x04] + b2[0x05];
+ b1[0x05] = (b2[0x04] - b2[0x05]) * cos0;
+ b1[0x06] = b2[0x06] + b2[0x07];
+ b1[0x07] = (b2[0x07] - b2[0x06]) * cos0;
+ b1[0x06] += b1[0x07];
+ b1[0x04] += b1[0x06];
+ b1[0x06] += b1[0x05];
+ b1[0x05] += b1[0x07];
+
+ b1[0x08] = b2[0x08] + b2[0x09];
+ b1[0x09] = (b2[0x08] - b2[0x09]) * cos0;
+ b1[0x0A] = b2[0x0A] + b2[0x0B];
+ b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0;
+ b1[0x0A] += b1[0x0B];
+
+ b1[0x0C] = b2[0x0C] + b2[0x0D];
+ b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0;
+ b1[0x0E] = b2[0x0E] + b2[0x0F];
+ b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0;
+ b1[0x0E] += b1[0x0F];
+ b1[0x0C] += b1[0x0E];
+ b1[0x0E] += b1[0x0D];
+ b1[0x0D] += b1[0x0F];
+
+ b1[0x10] = b2[0x10] + b2[0x11];
+ b1[0x11] = (b2[0x10] - b2[0x11]) * cos0;
+ b1[0x12] = b2[0x12] + b2[0x13];
+ b1[0x13] = (b2[0x13] - b2[0x12]) * cos0;
+ b1[0x12] += b1[0x13];
+
+ b1[0x14] = b2[0x14] + b2[0x15];
+ b1[0x15] = (b2[0x14] - b2[0x15]) * cos0;
+ b1[0x16] = b2[0x16] + b2[0x17];
+ b1[0x17] = (b2[0x17] - b2[0x16]) * cos0;
+ b1[0x16] += b1[0x17];
+ b1[0x14] += b1[0x16];
+ b1[0x16] += b1[0x15];
+ b1[0x15] += b1[0x17];
+
+ b1[0x18] = b2[0x18] + b2[0x19];
+ b1[0x19] = (b2[0x18] - b2[0x19]) * cos0;
+ b1[0x1A] = b2[0x1A] + b2[0x1B];
+ b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0;
+ b1[0x1A] += b1[0x1B];
+
+ b1[0x1C] = b2[0x1C] + b2[0x1D];
+ b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0;
+ b1[0x1E] = b2[0x1E] + b2[0x1F];
+ b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0;
+ b1[0x1E] += b1[0x1F];
+ b1[0x1C] += b1[0x1E];
+ b1[0x1E] += b1[0x1D];
+ b1[0x1D] += b1[0x1F];
+ }
+
+ out0[0x10*16] = b1[0x00];
+ out0[0x10*12] = b1[0x04];
+ out0[0x10* 8] = b1[0x02];
+ out0[0x10* 4] = b1[0x06];
+ out0[0x10* 0] = b1[0x01];
+ out1[0x10* 0] = b1[0x01];
+ out1[0x10* 4] = b1[0x05];
+ out1[0x10* 8] = b1[0x03];
+ out1[0x10*12] = b1[0x07];
+
+#if 1
+ out0[0x10*14] = b1[0x08] + b1[0x0C];
+ out0[0x10*10] = b1[0x0C] + b1[0x0a];
+ out0[0x10* 6] = b1[0x0A] + b1[0x0E];
+ out0[0x10* 2] = b1[0x0E] + b1[0x09];
+ out1[0x10* 2] = b1[0x09] + b1[0x0D];
+ out1[0x10* 6] = b1[0x0D] + b1[0x0B];
+ out1[0x10*10] = b1[0x0B] + b1[0x0F];
+ out1[0x10*14] = b1[0x0F];
+#else
+ b1[0x08] += b1[0x0C];
+ out0[0x10*14] = b1[0x08];
+ b1[0x0C] += b1[0x0a];
+ out0[0x10*10] = b1[0x0C];
+ b1[0x0A] += b1[0x0E];
+ out0[0x10* 6] = b1[0x0A];
+ b1[0x0E] += b1[0x09];
+ out0[0x10* 2] = b1[0x0E];
+ b1[0x09] += b1[0x0D];
+ out1[0x10* 2] = b1[0x09];
+ b1[0x0D] += b1[0x0B];
+ out1[0x10* 6] = b1[0x0D];
+ b1[0x0B] += b1[0x0F];
+ out1[0x10*10] = b1[0x0B];
+ out1[0x10*14] = b1[0x0F];
+#endif
+
+ {
+ real tmp;
+ tmp = b1[0x18] + b1[0x1C];
+ out0[0x10*15] = tmp + b1[0x10];
+ out0[0x10*13] = tmp + b1[0x14];
+ tmp = b1[0x1C] + b1[0x1A];
+ out0[0x10*11] = tmp + b1[0x14];
+ out0[0x10* 9] = tmp + b1[0x12];
+ tmp = b1[0x1A] + b1[0x1E];
+ out0[0x10* 7] = tmp + b1[0x12];
+ out0[0x10* 5] = tmp + b1[0x16];
+ tmp = b1[0x1E] + b1[0x19];
+ out0[0x10* 3] = tmp + b1[0x16];
+ out0[0x10* 1] = tmp + b1[0x11];
+ tmp = b1[0x19] + b1[0x1D];
+ out1[0x10* 1] = tmp + b1[0x11];
+ out1[0x10* 3] = tmp + b1[0x15];
+ tmp = b1[0x1D] + b1[0x1B];
+ out1[0x10* 5] = tmp + b1[0x15];
+ out1[0x10* 7] = tmp + b1[0x13];
+ tmp = b1[0x1B] + b1[0x1F];
+ out1[0x10* 9] = tmp + b1[0x13];
+ out1[0x10*11] = tmp + b1[0x17];
+ out1[0x10*13] = b1[0x17] + b1[0x1F];
+ out1[0x10*15] = b1[0x1F];
+ }
+}
+
+/*
+ * the call via dct64 is a trick to force GCC to use
+ * (new) registers for the b1,b2 pointer to the bufs[xx] field
+ */
+void dct64(real *a,real *b,real *c)
+{
+ real bufs[0x40];
+ dct64_1(a,b,bufs,bufs+0x20,c);
+}
+
diff --git a/addons/mp3/decode_i386.c b/addons/mp3/decode_i386.c
new file mode 100644
index 000000000..83e054422
--- /dev/null
+++ b/addons/mp3/decode_i386.c
@@ -0,0 +1,153 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ *
+ * slighlty optimized for machines without autoincrement/decrement.
+ * The performance is highly compiler dependend. Maybe
+ * the decode.c version for 'normal' processor may be faster
+ * even for Intel processors.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+
+#include "mpglib.h"
+
+
+
+ /* old WRITE_SAMPLE */
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+
+int synth_1to1_mono(struct mpstr *mp,real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_1to1(mp,bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<32;i++) {
+ *( (short *) samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += 64;
+
+ return ret;
+}
+
+
+int synth_1to1(struct mpstr *mp, real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static const int step = 2;
+ int bo;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+
+ bo = mp->synth_bo;
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = mp->synth_buffs[0];
+ }
+ else {
+ samples++;
+ buf = mp->synth_buffs[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+ mp->synth_bo = bo;
+
+ {
+ register int j;
+ real *window = decwin + 16 - bo1;
+
+ for (j=16;j;j--,b0+=0x10,window+=0x20,samples+=step)
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum -= window[0x1] * b0[0x1];
+ sum += window[0x2] * b0[0x2];
+ sum -= window[0x3] * b0[0x3];
+ sum += window[0x4] * b0[0x4];
+ sum -= window[0x5] * b0[0x5];
+ sum += window[0x6] * b0[0x6];
+ sum -= window[0x7] * b0[0x7];
+ sum += window[0x8] * b0[0x8];
+ sum -= window[0x9] * b0[0x9];
+ sum += window[0xA] * b0[0xA];
+ sum -= window[0xB] * b0[0xB];
+ sum += window[0xC] * b0[0xC];
+ sum -= window[0xD] * b0[0xD];
+ sum += window[0xE] * b0[0xE];
+ sum -= window[0xF] * b0[0xF];
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+ WRITE_SAMPLE(samples,sum,clip);
+ b0-=0x10,window-=0x20,samples+=step;
+ }
+ window += bo1<<1;
+
+ for (j=15;j;j--,b0-=0x10,window-=0x20,samples+=step)
+ {
+ real sum;
+ sum = -window[-0x1] * b0[0x0];
+ sum -= window[-0x2] * b0[0x1];
+ sum -= window[-0x3] * b0[0x2];
+ sum -= window[-0x4] * b0[0x3];
+ sum -= window[-0x5] * b0[0x4];
+ sum -= window[-0x6] * b0[0x5];
+ sum -= window[-0x7] * b0[0x6];
+ sum -= window[-0x8] * b0[0x7];
+ sum -= window[-0x9] * b0[0x8];
+ sum -= window[-0xA] * b0[0x9];
+ sum -= window[-0xB] * b0[0xA];
+ sum -= window[-0xC] * b0[0xB];
+ sum -= window[-0xD] * b0[0xC];
+ sum -= window[-0xE] * b0[0xD];
+ sum -= window[-0xF] * b0[0xE];
+ sum -= window[-0x0] * b0[0xF];
+
+ WRITE_SAMPLE(samples,sum,clip);
+ }
+ }
+ *pnt += 128;
+
+ return clip;
+}
+
diff --git a/addons/mp3/decode_ntom.c b/addons/mp3/decode_ntom.c
new file mode 100644
index 000000000..b231f439f
--- /dev/null
+++ b/addons/mp3/decode_ntom.c
@@ -0,0 +1,219 @@
+/*
+ * Mpeg Layer-1,2,3 audio decoder
+ * ------------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved.
+ * See also 'README'
+ *
+ * N->M down/up sampling. Not optimized for speed.
+ */
+
+#include <asterisk.h>
+#include <asterisk/logger.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mpg123.h"
+#include "mpglib.h"
+
+#define WRITE_SAMPLE(samples,sum,clip) \
+ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \
+ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \
+ else { *(samples) = sum; }
+
+#define NTOM_MUL (32768)
+static unsigned long ntom_val[2] = { NTOM_MUL>>1,NTOM_MUL>>1 };
+static unsigned long ntom_step = NTOM_MUL;
+
+
+int synth_ntom_set_step(long m,long n)
+{
+ if(param.verbose > 1)
+ ast_log(LOG_WARNING,"Init rate converter: %ld->%ld\n",m,n);
+
+ if(n >= 96000 || m >= 96000 || m == 0 || n == 0) {
+ ast_log(LOG_WARNING,"NtoM converter: illegal rates\n");
+ return (1);
+ }
+
+ n *= NTOM_MUL;
+ ntom_step = n / m;
+
+ if(ntom_step > 8*NTOM_MUL) {
+ ast_log(LOG_WARNING,"max. 1:8 conversion allowed!\n");
+ return (1);
+ }
+
+ ntom_val[0] = ntom_val[1] = NTOM_MUL>>1;
+
+ return (0);
+
+}
+
+
+int synth_ntom_mono (struct mpstr *mp, real *bandPtr,unsigned char *samples,int *pnt)
+{
+ short samples_tmp[8*64];
+ short *tmp1 = samples_tmp;
+ int i,ret;
+ int pnt1 = 0;
+
+ ret = synth_ntom(mp, bandPtr,0,(unsigned char *) samples_tmp,&pnt1);
+ samples += *pnt;
+
+ for(i=0;i<(pnt1>>2);i++) {
+ *( (short *)samples) = *tmp1;
+ samples += 2;
+ tmp1 += 2;
+ }
+ *pnt += pnt1 >> 1;
+
+ return ret;
+}
+
+
+
+int synth_ntom(struct mpstr *mp, real *bandPtr,int channel,unsigned char *out,int *pnt)
+{
+ static const int step = 2;
+ int bo;
+ short *samples = (short *) (out + *pnt);
+
+ real *b0,(*buf)[0x110];
+ int clip = 0;
+ int bo1;
+ int ntom;
+
+ bo = mp->synth_bo;
+
+ if(!channel) {
+ bo--;
+ bo &= 0xf;
+ buf = mp->synth_buffs[0];
+ ntom = ntom_val[1] = ntom_val[0];
+ }
+ else {
+ samples++;
+ out += 2; /* to compute the right *pnt value */
+ buf = mp->synth_buffs[1];
+ ntom = ntom_val[1];
+ }
+
+ if(bo & 0x1) {
+ b0 = buf[0];
+ bo1 = bo;
+ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr);
+ }
+ else {
+ b0 = buf[1];
+ bo1 = bo+1;
+ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr);
+ }
+
+ mp->synth_bo = bo;
+
+ {
+ register int j;
+ real *window = (mp->decwin) + 16 - bo1;
+
+ for (j=16;j;j--,window+=0x10)
+ {
+ real sum;
+
+ ntom += ntom_step;
+ if(ntom < NTOM_MUL) {
+ window += 16;
+ b0 += 16;
+ continue;
+ }
+
+ sum = *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+ sum += *window++ * *b0++;
+ sum -= *window++ * *b0++;
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+
+ ntom += ntom_step;
+ if(ntom >= NTOM_MUL)
+ {
+ real sum;
+ sum = window[0x0] * b0[0x0];
+ sum += window[0x2] * b0[0x2];
+ sum += window[0x4] * b0[0x4];
+ sum += window[0x6] * b0[0x6];
+ sum += window[0x8] * b0[0x8];
+ sum += window[0xA] * b0[0xA];
+ sum += window[0xC] * b0[0xC];
+ sum += window[0xE] * b0[0xE];
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+
+ b0-=0x10,window-=0x20;
+ window += bo1<<1;
+
+ for (j=15;j;j--,b0-=0x20,window-=0x10)
+ {
+ real sum;
+
+ ntom += ntom_step;
+ if(ntom < NTOM_MUL) {
+ window -= 16;
+ b0 += 16;
+ continue;
+ }
+
+ sum = -*(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+ sum -= *(--window) * *b0++;
+
+ while(ntom >= NTOM_MUL) {
+ WRITE_SAMPLE(samples,sum,clip);
+ samples += step;
+ ntom -= NTOM_MUL;
+ }
+ }
+ }
+
+ ntom_val[channel] = ntom;
+ *pnt = ((unsigned char *) samples - out);
+
+ return clip;
+}
+
+
diff --git a/addons/mp3/huffman.h b/addons/mp3/huffman.h
new file mode 100644
index 000000000..7fec0d589
--- /dev/null
+++ b/addons/mp3/huffman.h
@@ -0,0 +1,332 @@
+/*
+ * huffman tables ... recalcualted to work with my optimzed
+ * decoder scheme (MH)
+ *
+ * probably we could save a few bytes of memory, because the
+ * smaller tables are often the part of a bigger table
+ */
+
+struct newhuff
+{
+ unsigned int linbits;
+ short *table;
+};
+
+static short tab0[] =
+{
+ 0
+};
+
+static short tab1[] =
+{
+ -5, -3, -1, 17, 1, 16, 0
+};
+
+static short tab2[] =
+{
+ -15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1,
+ 16, 0
+};
+
+static short tab3[] =
+{
+ -13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1,
+ 1, 0
+};
+
+static short tab5[] =
+{
+ -29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19,
+ 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16,
+ 0
+};
+
+static short tab6[] =
+{
+ -25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19,
+ 49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16,
+ 0
+};
+
+static short tab7[] =
+{
+ -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83,
+ -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1,
+ 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7,
+ -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18,
+ -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab8[] =
+{
+ -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83,
+ -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52,
+ 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4,
+ 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1,
+ 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0
+};
+
+static short tab9[] =
+{
+ -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1,
+ 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67,
+ -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5,
+ -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2,
+ 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0
+};
+
+static short tab10[] =
+{
+-125,-121,-111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118,
+ 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3,
+ -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1,
+ 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23,
+ -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81,
+ -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7,
+ -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1,
+ 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1,
+ 2, 32, 17, -1, 1, 16, 0
+};
+
+static short tab11[] =
+{
+-121,-113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117,
+ -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55,
+ -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114,
+ -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96,
+ -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38,
+ 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1,
+ 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50,
+ -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2,
+ 32, 17, -3, -1, 1, 16, 0
+};
+
+static short tab12[] =
+{
+-115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87,
+ 117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115,
+ 85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7,
+ 112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5,
+ -1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37,
+ 82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4,
+ 36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3,
+ -1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1,
+ 2, 32, 0, 17, -1, 1, 16
+};
+
+static short tab13[] =
+{
+-509,-503,-475,-405,-333,-265,-205,-153,-115, -83, -53, -35, -21, -13, -9,
+ -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238,
+ 207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1,
+ 236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249,
+ 234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158,
+ -5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1,
+ 203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245,
+ 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1,
+ 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15,
+ -5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1,
+ 200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1,
+ 240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1,
+ 46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3,
+ -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1,
+ 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5,
+ -1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167,
+ 151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76,
+ 196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137,
+ 28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106,
+ -5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43,
+ -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178,
+ -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1,
+ 58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161,
+ -3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88,
+ -1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1,
+ 131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25,
+ 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100,
+ 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113,
+ -1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38,
+ -1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6,
+ 96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81,
+ -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11,
+ -5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3,
+ -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab15[] =
+{
+-495,-445,-355,-263,-183,-115, -77, -43, -27, -13, -7, -3, -1, 255, 239,
+ -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237,
+ 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3,
+ -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219,
+ -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173,
+ -3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246,
+ -3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9,
+ -3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243,
+ 216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1,
+ 31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1,
+ 125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3,
+ -1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5,
+ -1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124,
+ 199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1,
+ 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183,
+ -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76,
+ -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1,
+ 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15,
+ -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106,
+ -5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5,
+ -1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74,
+ -1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1,
+ 42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134,
+ 73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29,
+ -13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7,
+ -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7,
+ -3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86,
+ -3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100,
+ 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69,
+ -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9,
+ -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1,
+ 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20,
+ 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48,
+ 34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16,
+ 0
+};
+
+static short tab16[] =
+{
+-509,-503,-461,-323,-103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223,
+ 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3,
+ -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5,
+ -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19,
+ -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1,
+ 238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5,
+ -1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125,
+ 94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13,
+ -5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3,
+ -1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186,
+ -1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1,
+ 214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169,
+ -5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213,
+ -3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154,
+ 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1,
+ 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1,
+ 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45,
+ -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107,
+ -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12,
+ -1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1,
+ 178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74,
+ 164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33,
+ -19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3,
+ -1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147,
+ -1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1,
+ 145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3,
+ -1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1,
+ 8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3,
+ -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1,
+ 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9,
+ -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33,
+ -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20,
+ -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1,
+ 3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16,
+ 0
+};
+
+static short tab24[] =
+{
+-451,-117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1,
+ 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9,
+ -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79,
+ 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31,
+ 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1,
+ 236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3,
+ -1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3,
+ -1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255,-235,
+-143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3,
+ -1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9,
+ -5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1,
+ 78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185,
+ 170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199,
+ 77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3,
+ -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3,
+ -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196,
+ -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1,
+ 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1,
+ 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10,
+ 26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9,
+ 144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165,
+ 27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135,
+ -1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104,
+ -1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3,
+ -1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3,
+ -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7,
+ -3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86,
+ 101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15,
+ -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84,
+ -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1,
+ 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5,
+ 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5,
+ -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1,
+ 3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16,
+ 0
+};
+
+static short tab_c0[] =
+{
+ -29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5,
+ 9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8,
+ 0
+};
+
+static short tab_c1[] =
+{
+ -15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9,
+ 8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1,
+ 0
+};
+
+
+
+static struct newhuff ht[] =
+{
+ { /* 0 */ 0 , tab0 } ,
+ { /* 2 */ 0 , tab1 } ,
+ { /* 3 */ 0 , tab2 } ,
+ { /* 3 */ 0 , tab3 } ,
+ { /* 0 */ 0 , tab0 } ,
+ { /* 4 */ 0 , tab5 } ,
+ { /* 4 */ 0 , tab6 } ,
+ { /* 6 */ 0 , tab7 } ,
+ { /* 6 */ 0 , tab8 } ,
+ { /* 6 */ 0 , tab9 } ,
+ { /* 8 */ 0 , tab10 } ,
+ { /* 8 */ 0 , tab11 } ,
+ { /* 8 */ 0 , tab12 } ,
+ { /* 16 */ 0 , tab13 } ,
+ { /* 0 */ 0 , tab0 } ,
+ { /* 16 */ 0 , tab15 } ,
+
+ { /* 16 */ 1 , tab16 } ,
+ { /* 16 */ 2 , tab16 } ,
+ { /* 16 */ 3 , tab16 } ,
+ { /* 16 */ 4 , tab16 } ,
+ { /* 16 */ 6 , tab16 } ,
+ { /* 16 */ 8 , tab16 } ,
+ { /* 16 */ 10, tab16 } ,
+ { /* 16 */ 13, tab16 } ,
+ { /* 16 */ 4 , tab24 } ,
+ { /* 16 */ 5 , tab24 } ,
+ { /* 16 */ 6 , tab24 } ,
+ { /* 16 */ 7 , tab24 } ,
+ { /* 16 */ 8 , tab24 } ,
+ { /* 16 */ 9 , tab24 } ,
+ { /* 16 */ 11, tab24 } ,
+ { /* 16 */ 13, tab24 }
+};
+
+static struct newhuff htc[] =
+{
+ { /* 1 , 1 , */ 0 , tab_c0 } ,
+ { /* 1 , 1 , */ 0 , tab_c1 }
+};
+
+
diff --git a/addons/mp3/interface.c b/addons/mp3/interface.c
new file mode 100644
index 000000000..9ced53342
--- /dev/null
+++ b/addons/mp3/interface.c
@@ -0,0 +1,323 @@
+#include <asterisk.h>
+#include <asterisk/logger.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mpg123.h"
+#include "mpglib.h"
+
+
+void InitMP3Constants(void)
+{
+ init_layer3_const();
+ make_decode_tables_const();
+
+}
+
+
+BOOL InitMP3(struct mpstr *mp, long outscale)
+{
+ /* quiet 4096 med 8192 */
+
+ memset(mp,0,sizeof(struct mpstr));
+
+ mp->framesize = 0;
+ mp->fsizeold = -1;
+ mp->bsize = 0;
+ mp->head = mp->tail = NULL;
+ mp->fr.single = 3; /* force mono */
+ mp->bsnum = 0;
+ mp->synth_bo = 1;
+ mp->outsamplerate = 8000;
+
+ make_decode_tables_scale(mp, outscale);
+
+ init_layer3_sample_limits(mp, SBLIMIT);
+
+ return !0;
+}
+
+void ExitMP3(struct mpstr *mp)
+{
+ struct buf *b,*bn;
+
+ b = mp->tail;
+ while(b) {
+ free(b->pnt);
+ bn = b->next;
+ free(b);
+ b = bn;
+ }
+}
+
+static struct buf *addbuf(struct mpstr *mp,char *buf,int size)
+{
+ struct buf *nbuf;
+
+ nbuf = malloc( sizeof(struct buf) );
+ if(!nbuf) {
+ ast_log(LOG_WARNING,"Out of memory!\n");
+ return NULL;
+ }
+ nbuf->pnt = malloc(size);
+ if(!nbuf->pnt) {
+ free(nbuf);
+ return NULL;
+ }
+ nbuf->size = size;
+ memcpy(nbuf->pnt,buf,size);
+ nbuf->next = NULL;
+ nbuf->prev = mp->head;
+ nbuf->pos = 0;
+
+ if(!mp->tail) {
+ mp->tail = nbuf;
+ }
+ else {
+ mp->head->next = nbuf;
+ }
+
+ mp->head = nbuf;
+ mp->bsize += size;
+
+ return nbuf;
+}
+
+static void remove_buf(struct mpstr *mp)
+{
+ struct buf *buf = mp->tail;
+
+ mp->tail = buf->next;
+ if(mp->tail)
+ mp->tail->prev = NULL;
+ else {
+ mp->tail = mp->head = NULL;
+ }
+
+ free(buf->pnt);
+ free(buf);
+
+}
+
+static int read_buf_byte(int *error, struct mpstr *mp)
+{
+ unsigned int b;int pos;
+
+ pos = mp->tail->pos;
+ while(pos >= mp->tail->size) {
+ remove_buf(mp);
+ pos = mp->tail->pos;
+ if(!mp->tail) {
+ /* We may pick up this error a few times*/
+ /* But things have gone pear shaped */
+ ast_log(LOG_WARNING,"Fatal Buffer error!\n");
+ *error = 1;
+ return (0);
+ }
+ }
+
+ b = mp->tail->pnt[pos];
+ mp->bsize--;
+ mp->tail->pos++;
+
+
+ return b;
+}
+
+static int read_head(struct mpstr *mp)
+{
+ unsigned long head;
+ int error=0;
+
+
+ head = read_buf_byte(&error, mp);
+ head <<= 8;
+ head |= read_buf_byte(&error, mp);
+ head <<= 8;
+ head |= read_buf_byte(&error, mp);
+ head <<= 8;
+ head |= read_buf_byte(&error, mp);
+
+ mp->header = head;
+
+ if(error){
+ return (1);
+ }else
+ return (0);
+
+}
+
+static int head_check(unsigned long head)
+{
+ if( (head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if(!((head>>17)&3))
+ return FALSE;
+ if( ((head>>12)&0xf) == 0xf || ((head>>12)&0xf) == 0)
+ return FALSE;
+ if( ((head>>10)&0x3) == 0x3 )
+ return FALSE;
+ if ((head & 0xffff0000) == 0xfffe0000)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int head_shift(struct mpstr *mp)
+{
+ unsigned long head;
+ int error = 0;
+
+ head = mp->header;
+
+ head <<= 8;
+ head |= read_buf_byte(&error, mp);
+
+ mp->header = head;
+
+ if (error){
+ return (1);
+ }else
+ return (0);
+
+}
+
+
+int decodeMP3(struct mpstr *mp,char *in,int isize,char *out,
+ int osize,int *done)
+{
+ int len;
+ long n,m;
+ int down_sample_sblimit;
+
+ if(osize < 4608) {
+ ast_log(LOG_WARNING,"To less out space\n");
+ return MP3_ERR;
+ }
+
+ if(in) {
+ if(addbuf(mp,in,isize) == NULL) {
+ return MP3_ERR;
+ }
+ }
+
+ /* First decode header */
+ if(mp->framesize == 0) {
+ if(mp->bsize < 4) {
+ return MP3_NEED_MORE;
+ }
+ if (read_head(mp))
+ return MP3_ERR;
+
+ if(!head_check(mp->header) ) {
+ int i;
+
+ ast_log(LOG_WARNING,"Junk at the beginning of frame %08lx\n",mp->header);
+
+ /* step in byte steps through next 64K */
+ for(i=0;i<65536;i++) {
+ if(!mp->bsize)
+ return MP3_NEED_MORE;
+
+ if(head_shift(mp))
+ return MP3_ERR;
+
+ if(head_check(mp->header))
+ break;
+ }
+ if(i == 65536) {
+ ast_log(LOG_WARNING,"Giving up searching valid MPEG header\n");
+ return MP3_ERR;
+ }
+ }
+
+ decode_header(&mp->fr,mp->header);
+ mp->framesize = mp->fr.framesize;
+
+ if (!mp->initmp3){
+ mp->initmp3 = 1;
+
+ n = freqs[mp->fr.sampling_frequency];
+ if (mp->outsamplerate) {
+ m = mp->outsamplerate;
+ }
+ else {
+ m =n;
+ }
+
+ if (synth_ntom_set_step(n,m))
+ return MP3_ERR;
+
+
+ if(n>m) {
+ down_sample_sblimit = SBLIMIT * m;
+ down_sample_sblimit /= n;
+ }
+ else {
+ down_sample_sblimit = SBLIMIT;
+ }
+
+ init_layer3_sample_limits(mp, down_sample_sblimit);
+
+ }
+ }
+
+
+ if(mp->fr.framesize > mp->bsize)
+ return MP3_NEED_MORE;
+
+ (mp->worksample).wordpointer = mp->bsspace[mp->bsnum] + 512;
+ mp->bsnum = (mp->bsnum + 1) & 0x1;
+ (mp->worksample).bitindex = 0;
+
+ len = 0;
+ while(len < mp->framesize) {
+ int nlen;
+ int blen = mp->tail->size - mp->tail->pos;
+ if( (mp->framesize - len) <= blen) {
+ nlen = mp->framesize-len;
+ }
+ else {
+ nlen = blen;
+ }
+ memcpy((mp->worksample).wordpointer+len,mp->tail->pnt+mp->tail->pos,nlen);
+ len += nlen;
+ mp->tail->pos += nlen;
+ mp->bsize -= nlen;
+ if(mp->tail->pos == mp->tail->size) {
+ remove_buf(mp);
+ }
+ }
+
+ *done = 0;
+ if(mp->fr.error_protection)
+ getbits(mp, 16);
+
+ if (do_layer3(mp,(unsigned char *) out,done))
+ return MP3_ERR;
+
+ mp->fsizeold = mp->framesize;
+ mp->framesize = 0;
+
+ return MP3_OK;
+}
+
+int set_pointer(struct mpstr *mp, long backstep)
+{
+ unsigned char *bsbufold;
+ if(mp->fsizeold < 0 && backstep > 0) {
+ ast_log(LOG_WARNING,"Can't step back %ld!\n",backstep);
+ return MP3_ERR;
+ }
+ bsbufold = mp->bsspace[mp->bsnum] + 512;
+ (mp->worksample).wordpointer -= backstep;
+ if (backstep)
+ memcpy((mp->worksample).wordpointer,bsbufold+mp->fsizeold-backstep,backstep);
+ (mp->worksample).bitindex = 0;
+ return MP3_OK;
+}
+
+
+
+
diff --git a/addons/mp3/layer3.c b/addons/mp3/layer3.c
new file mode 100644
index 000000000..793c99633
--- /dev/null
+++ b/addons/mp3/layer3.c
@@ -0,0 +1,2029 @@
+/*
+ * Mpeg Layer-3 audio decoder
+ * --------------------------
+ * copyright (c) 1995,1996,1997 by Michael Hipp.
+ * All rights reserved. See also 'README'
+ */
+
+#include <asterisk.h>
+#include <asterisk/logger.h>
+#include <stdlib.h>
+#include "mpg123.h"
+#include "mpglib.h"
+#include "huffman.h"
+
+#define MPEG1
+
+/* These should all be constants setup once using init_layer3_const */
+static real ispow[8207];
+static real aa_ca[8],aa_cs[8];
+static real COS1[12][6];
+static real win[4][36];
+static real win1[4][36];
+static real gainpow2[256+118+4];
+static real COS9[9];
+static real COS6_1,COS6_2;
+static real tfcos36[9];
+static real tfcos12[3];
+
+struct bandInfoStruct {
+ short longIdx[23];
+ short longDiff[22];
+ short shortIdx[14];
+ short shortDiff[13];
+};
+
+
+struct bandInfoStruct bandInfo[9] = {
+
+/* MPEG 1.0 */
+ { {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576},
+ {4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158},
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3},
+ {4,4,4,4,6,8,10,12,14,18,22,30,56} } ,
+
+ { {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576},
+ {4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192},
+ {0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3},
+ {4,4,4,4,6,6,10,12,14,16,20,26,66} } ,
+
+ { {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} ,
+ {4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} ,
+ {0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} ,
+ {4,4,4,4,6,8,12,16,20,26,34,42,12} } ,
+
+/* MPEG 2.0 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } ,
+ {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} ,
+ {4,4,4,6,6,8,10,14,18,26,32,42,18 } } ,
+
+ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576},
+ {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,52,64,70,76,36 } ,
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} ,
+ {4,4,4,6,8,10,12,14,18,24,32,44,12 } } ,
+
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 },
+ {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18 } } ,
+/* MPEG 2.5 */
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} ,
+ {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54},
+ {0,12,24,36,54,78,108,144,186,240,312,402,522,576},
+ {4,4,4,6,8,10,12,14,18,24,30,40,18} },
+ { {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576},
+ {12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2},
+ {0, 24, 48, 72,108,156,216,288,372,480,486,492,498,576},
+ {8,8,8,12,16,20,24,28,36,2,2,2,26} } ,
+};
+
+static int mapbuf0[9][152];
+static int mapbuf1[9][156];
+static int mapbuf2[9][44];
+static int *map[9][3];
+static int *mapend[9][3];
+
+static unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */
+static unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */
+
+static real tan1_1[16],tan2_1[16],tan1_2[16],tan2_2[16];
+static real pow1_1[2][16],pow2_1[2][16],pow1_2[2][16],pow2_2[2][16];
+
+/*
+ * init constant tables for layer-3
+ */
+void init_layer3_const(void)
+{
+ int i,j,k,l;
+
+ for(i=-256;i<118+4;i++)
+ gainpow2[i+256] = pow((double)2.0,-0.25 * (double) (i+210) );
+
+ for(i=0;i<8207;i++)
+ ispow[i] = pow((double)i,(double)4.0/3.0);
+
+ for (i=0;i<8;i++)
+ {
+ static double Ci[8]={-0.6,-0.535,-0.33,-0.185,-0.095,-0.041,-0.0142,-0.0037};
+ double sq=sqrt(1.0+Ci[i]*Ci[i]);
+ aa_cs[i] = 1.0/sq;
+ aa_ca[i] = Ci[i]/sq;
+ }
+
+ for(i=0;i<18;i++)
+ {
+ win[0][i] = win[1][i] = 0.5 * sin( M_PI / 72.0 * (double) (2*(i+0) +1) ) / cos ( M_PI * (double) (2*(i+0) +19) / 72.0 );
+ win[0][i+18] = win[3][i+18] = 0.5 * sin( M_PI / 72.0 * (double) (2*(i+18)+1) ) / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 );
+ }
+ for(i=0;i<6;i++)
+ {
+ win[1][i+18] = 0.5 / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 );
+ win[3][i+12] = 0.5 / cos ( M_PI * (double) (2*(i+12)+19) / 72.0 );
+ win[1][i+24] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+13) ) / cos ( M_PI * (double) (2*(i+24)+19) / 72.0 );
+ win[1][i+30] = win[3][i] = 0.0;
+ win[3][i+6 ] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*(i+6 )+19) / 72.0 );
+ }
+
+ for(i=0;i<9;i++)
+ COS9[i] = cos( M_PI / 18.0 * (double) i);
+
+ for(i=0;i<9;i++)
+ tfcos36[i] = 0.5 / cos ( M_PI * (double) (i*2+1) / 36.0 );
+ for(i=0;i<3;i++)
+ tfcos12[i] = 0.5 / cos ( M_PI * (double) (i*2+1) / 12.0 );
+
+ COS6_1 = cos( M_PI / 6.0 * (double) 1);
+ COS6_2 = cos( M_PI / 6.0 * (double) 2);
+
+ for(i=0;i<12;i++)
+ {
+ win[2][i] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*i+7) / 24.0 );
+ for(j=0;j<6;j++)
+ COS1[i][j] = cos( M_PI / 24.0 * (double) ((2*i+7)*(2*j+1)) );
+ }
+
+ for(j=0;j<4;j++) {
+ static int len[4] = { 36,36,12,36 };
+ for(i=0;i<len[j];i+=2)
+ win1[j][i] = + win[j][i];
+ for(i=1;i<len[j];i+=2)
+ win1[j][i] = - win[j][i];
+ }
+
+ for(i=0;i<16;i++)
+ {
+ double t = tan( (double) i * M_PI / 12.0 );
+ tan1_1[i] = t / (1.0+t);
+ tan2_1[i] = 1.0 / (1.0 + t);
+ tan1_2[i] = M_SQRT2 * t / (1.0+t);
+ tan2_2[i] = M_SQRT2 / (1.0 + t);
+
+ for(j=0;j<2;j++) {
+ double base = pow(2.0,-0.25*(j+1.0));
+ double p1=1.0,p2=1.0;
+ if(i > 0) {
+ if( i & 1 )
+ p1 = pow(base,(i+1.0)*0.5);
+ else
+ p2 = pow(base,i*0.5);
+ }
+ pow1_1[j][i] = p1;
+ pow2_1[j][i] = p2;
+ pow1_2[j][i] = M_SQRT2 * p1;
+ pow2_2[j][i] = M_SQRT2 * p2;
+ }
+ }
+
+ for(j=0;j<9;j++)
+ {
+ struct bandInfoStruct *bi = &bandInfo[j];
+ int *mp;
+ int cb,lwin;
+ short *bdf;
+
+ mp = map[j][0] = mapbuf0[j];
+ bdf = bi->longDiff;
+ for(i=0,cb = 0; cb < 8 ; cb++,i+=*bdf++) {
+ *mp++ = (*bdf) >> 1;
+ *mp++ = i;
+ *mp++ = 3;
+ *mp++ = cb;
+ }
+ bdf = bi->shortDiff+3;
+ for(cb=3;cb<13;cb++) {
+ int l = (*bdf++) >> 1;
+ for(lwin=0;lwin<3;lwin++) {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6*l;
+ }
+ mapend[j][0] = mp;
+
+ mp = map[j][1] = mapbuf1[j];
+ bdf = bi->shortDiff+0;
+ for(i=0,cb=0;cb<13;cb++) {
+ int l = (*bdf++) >> 1;
+ for(lwin=0;lwin<3;lwin++) {
+ *mp++ = l;
+ *mp++ = i + lwin;
+ *mp++ = lwin;
+ *mp++ = cb;
+ }
+ i += 6*l;
+ }
+ mapend[j][1] = mp;
+
+ mp = map[j][2] = mapbuf2[j];
+ bdf = bi->longDiff;
+ for(cb = 0; cb < 22 ; cb++) {
+ *mp++ = (*bdf++) >> 1;
+ *mp++ = cb;
+ }
+ mapend[j][2] = mp;
+
+ }
+
+ for(i=0;i<5;i++) {
+ for(j=0;j<6;j++) {
+ for(k=0;k<6;k++) {
+ int n = k + j * 6 + i * 36;
+ i_slen2[n] = i|(j<<3)|(k<<6)|(3<<12);
+ }
+ }
+ }
+ for(i=0;i<4;i++) {
+ for(j=0;j<4;j++) {
+ for(k=0;k<4;k++) {
+ int n = k + j * 4 + i * 16;
+ i_slen2[n+180] = i|(j<<3)|(k<<6)|(4<<12);
+ }
+ }
+ }
+ for(i=0;i<4;i++) {
+ for(j=0;j<3;j++) {
+ int n = j + i * 3;
+ i_slen2[n+244] = i|(j<<3) | (5<<12);
+ n_slen2[n+500] = i|(j<<3) | (2<<12) | (1<<15);
+ }
+ }
+
+ for(i=0;i<5;i++) {
+ for(j=0;j<5;j++) {
+ for(k=0;k<4;k++) {
+ for(l=0;l<4;l++) {
+ int n = l + k * 4 + j * 16 + i * 80;
+ n_slen2[n] = i|(j<<3)|(k<<6)|(l<<9)|(0<<12);
+ }
+ }
+ }
+ }
+ for(i=0;i<5;i++) {
+ for(j=0;j<5;j++) {
+ for(k=0;k<4;k++) {
+ int n = k + j * 4 + i * 20;
+ n_slen2[n+400] = i|(j<<3)|(k<<6)|(1<<12);
+ }
+ }
+ }
+}
+
+/* MP3 file specific rates */
+void init_layer3_sample_limits(struct mpstr *mp, int down_sample_sblimit)
+{
+ int i,j;
+ for(j=0;j<9;j++) {
+ for(i=0;i<23;i++) {
+ (mp->longLimit)[j][i] = (bandInfo[j].longIdx[i] - 1 + 8) / 18 + 1;
+ if((mp->longLimit)[j][i] > (down_sample_sblimit) )
+ (mp->longLimit)[j][i] = down_sample_sblimit;
+ }
+ for(i=0;i<14;i++) {
+ (mp->shortLimit)[j][i] = (bandInfo[j].shortIdx[i] - 1) / 18 + 1;
+ if((mp->shortLimit)[j][i] > (down_sample_sblimit) )
+ (mp->shortLimit)[j][i] = down_sample_sblimit;
+ }
+ }
+}
+
+
+
+/*
+ * read additional side information
+ */
+#ifdef MPEG1
+static int III_get_side_info_1(struct mpstr *mp, struct III_sideinfo *si,int stereo,
+ int ms_stereo,long sfreq,int single)
+{
+ int ch, gr;
+ int powdiff = (single == 3) ? 4 : 0;
+
+ si->main_data_begin = getbits(mp, 9);
+ if (stereo == 1)
+ si->private_bits = getbits_fast(mp, 5);
+ else
+ si->private_bits = getbits_fast(mp, 3);
+
+ for (ch=0; ch<stereo; ch++) {
+ si->ch[ch].gr[0].scfsi = -1;
+ si->ch[ch].gr[1].scfsi = getbits_fast(mp, 4);
+ }
+
+ for (gr=0; gr<2; gr++)
+ {
+ for (ch=0; ch<stereo; ch++)
+ {
+ register struct gr_info_s *gr_info = &(si->ch[ch].gr[gr]);
+
+ gr_info->part2_3_length = getbits(mp, 12);
+ gr_info->big_values = getbits_fast(mp, 9);
+ if(gr_info->big_values > 288) {
+ ast_log(LOG_WARNING,"big_values too large!\n");
+ gr_info->big_values = 288;
+ }
+ gr_info->pow2gain = gainpow2+256 - getbits_fast(mp, 8) + powdiff;
+ if(ms_stereo)
+ gr_info->pow2gain += 2;
+ gr_info->scalefac_compress = getbits_fast(mp, 4);
+/* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */
+ if(get1bit(mp))
+ {
+ int i;
+ gr_info->block_type = getbits_fast(mp, 2);
+ gr_info->mixed_block_flag = get1bit(mp);
+ gr_info->table_select[0] = getbits_fast(mp, 5);
+ gr_info->table_select[1] = getbits_fast(mp, 5);
+ /*
+ * table_select[2] not needed, because there is no region2,
+ * but to satisfy some verifications tools we set it either.
+ */
+ gr_info->table_select[2] = 0;
+ for(i=0;i<3;i++)
+ gr_info->full_gain[i] = gr_info->pow2gain + (getbits_fast(mp, 3)<<3);
+
+ if(gr_info->block_type == 0) {
+ ast_log(LOG_WARNING,"Blocktype == 0 and window-switching == 1 not allowed.\n");
+ return (1);
+ }
+ /* region_count/start parameters are implicit in this case. */
+ gr_info->region1start = 36>>1;
+ gr_info->region2start = 576>>1;
+ }
+ else
+ {
+ int i,r0c,r1c;
+ for (i=0; i<3; i++)
+ gr_info->table_select[i] = getbits_fast(mp, 5);
+ r0c = getbits_fast(mp, 4);
+ r1c = getbits_fast(mp, 3);
+ gr_info->region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ;
+ gr_info->region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1;
+ gr_info->block_type = 0;
+ gr_info->mixed_block_flag = 0;
+ }
+ gr_info->preflag = get1bit(mp);
+ gr_info->scalefac_scale = get1bit(mp);
+ gr_info->count1table_select = get1bit(mp);
+ }
+ }
+ return (0);
+
+}
+#endif
+
+/*
+ * Side Info for MPEG 2.0 / LSF
+ */
+static int III_get_side_info_2(struct mpstr *mp, struct III_sideinfo *si,int stereo,
+ int ms_stereo,long sfreq,int single)
+{
+ int ch;
+ int powdiff = (single == 3) ? 4 : 0;
+
+ si->main_data_begin = getbits(mp, 8);
+ if (stereo == 1)
+ si->private_bits = get1bit(mp);
+ else
+ si->private_bits = getbits_fast(mp, 2);
+
+ for (ch=0; ch<stereo; ch++)
+ {
+ register struct gr_info_s *gr_info = &(si->ch[ch].gr[0]);
+
+ gr_info->part2_3_length = getbits(mp, 12);
+ gr_info->big_values = getbits_fast(mp, 9);
+ if(gr_info->big_values > 288) {
+ ast_log(LOG_WARNING,"big_values too large!\n");
+ gr_info->big_values = 288;
+ }
+ gr_info->pow2gain = gainpow2+256 - getbits_fast(mp, 8) + powdiff;
+ if(ms_stereo)
+ gr_info->pow2gain += 2;
+ gr_info->scalefac_compress = getbits(mp, 9);
+/* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */
+ if(get1bit(mp))
+ {
+ int i;
+ gr_info->block_type = getbits_fast(mp, 2);
+ gr_info->mixed_block_flag = get1bit(mp);
+ gr_info->table_select[0] = getbits_fast(mp, 5);
+ gr_info->table_select[1] = getbits_fast(mp, 5);
+ /*
+ * table_select[2] not needed, because there is no region2,
+ * but to satisfy some verifications tools we set it either.
+ */
+ gr_info->table_select[2] = 0;
+ for(i=0;i<3;i++)
+ gr_info->full_gain[i] = gr_info->pow2gain + (getbits_fast(mp, 3)<<3);
+
+ if(gr_info->block_type == 0) {
+ ast_log(LOG_WARNING,"Blocktype == 0 and window-switching == 1 not allowed.\n");
+ return (1);
+ }
+ /* region_count/start parameters are implicit in this case. */
+/* check this again! */
+ if(gr_info->block_type == 2)
+ gr_info->region1start = 36>>1;
+ else if(sfreq == 8)
+/* check this for 2.5 and sfreq=8 */
+ gr_info->region1start = 108>>1;
+ else
+ gr_info->region1start = 54>>1;
+ gr_info->region2start = 576>>1;
+ }
+ else
+ {
+ int i,r0c,r1c;
+ for (i=0; i<3; i++)
+ gr_info->table_select[i] = getbits_fast(mp, 5);
+ r0c = getbits_fast(mp, 4);
+ r1c = getbits_fast(mp, 3);
+ gr_info->region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ;
+ gr_info->region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1;
+ gr_info->block_type = 0;
+ gr_info->mixed_block_flag = 0;
+ }
+ gr_info->scalefac_scale = get1bit(mp);
+ gr_info->count1table_select = get1bit(mp);
+ }
+ return (0);
+}
+
+/*
+ * read scalefactors
+ */
+#ifdef MPEG1
+static int III_get_scale_factors_1(struct mpstr *mp, int *scf,struct gr_info_s *gr_info)
+{
+ static unsigned char slen[2][16] = {
+ {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},
+ {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}
+ };
+ int numbits;
+ int num0 = slen[0][gr_info->scalefac_compress];
+ int num1 = slen[1][gr_info->scalefac_compress];
+
+ if (gr_info->block_type == 2)
+ {
+ int i=18;
+ numbits = (num0 + num1) * 18;
+
+ if (gr_info->mixed_block_flag) {
+ for (i=8;i;i--)
+ *scf++ = getbits_fast(mp, num0);
+ i = 9;
+ numbits -= num0; /* num0 * 17 + num1 * 18 */
+ }
+
+ for (;i;i--)
+ *scf++ = getbits_fast(mp, num0);
+ for (i = 18; i; i--)
+ *scf++ = getbits_fast(mp, num1);
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* short[13][0..2] = 0 */
+ }
+ else
+ {
+ int i;
+ int scfsi = gr_info->scfsi;
+
+ if(scfsi < 0) { /* scfsi < 0 => granule == 0 */
+ for(i=11;i;i--)
+ *scf++ = getbits_fast(mp, num0);
+ for(i=10;i;i--)
+ *scf++ = getbits_fast(mp, num1);
+ numbits = (num0 + num1) * 10 + num0;
+ }
+ else {
+ numbits = 0;
+ if(!(scfsi & 0x8)) {
+ for (i=6;i;i--)
+ *scf++ = getbits_fast(mp, num0);
+ numbits += num0 * 6;
+ }
+ else {
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* set to ZERO necessary? */
+ *scf++ = 0; *scf++ = 0; *scf++ = 0;
+ }
+
+ if(!(scfsi & 0x4)) {
+ for (i=5;i;i--)
+ *scf++ = getbits_fast(mp, num0);
+ numbits += num0 * 5;
+ }
+ else {
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* set to ZERO necessary? */
+ *scf++ = 0; *scf++ = 0;
+ }
+
+ if(!(scfsi & 0x2)) {
+ for(i=5;i;i--)
+ *scf++ = getbits_fast(mp, num1);
+ numbits += num1 * 5;
+ }
+ else {
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* set to ZERO necessary? */
+ *scf++ = 0; *scf++ = 0;
+ }
+
+ if(!(scfsi & 0x1)) {
+ for (i=5;i;i--)
+ *scf++ = getbits_fast(mp, num1);
+ numbits += num1 * 5;
+ }
+ else {
+ *scf++ = 0; *scf++ = 0; *scf++ = 0; /* set to ZERO necessary? */
+ *scf++ = 0; *scf++ = 0;
+ }
+ }
+
+ *scf++ = 0; /* no l[21] in original sources */
+ }
+ return numbits;
+}
+#endif
+
+static int III_get_scale_factors_2(struct mpstr *mp, int *scf,struct gr_info_s *gr_info,int i_stereo)
+{
+ unsigned char *pnt;
+ int i,j;
+ unsigned int slen;
+ int n = 0;
+ int numbits = 0;
+
+ static unsigned char stab[3][6][4] = {
+ { { 6, 5, 5,5 } , { 6, 5, 7,3 } , { 11,10,0,0} ,
+ { 7, 7, 7,0 } , { 6, 6, 6,3 } , { 8, 8,5,0} } ,
+ { { 9, 9, 9,9 } , { 9, 9,12,6 } , { 18,18,0,0} ,
+ {12,12,12,0 } , {12, 9, 9,6 } , { 15,12,9,0} } ,
+ { { 6, 9, 9,9 } , { 6, 9,12,6 } , { 15,18,0,0} ,
+ { 6,15,12,0 } , { 6,12, 9,6 } , { 6,18,9,0} } };
+
+ if(i_stereo) /* i_stereo AND second channel -> do_layer3() checks this */
+ slen = i_slen2[gr_info->scalefac_compress>>1];
+ else
+ slen = n_slen2[gr_info->scalefac_compress];
+
+ gr_info->preflag = (slen>>15) & 0x1;
+
+ n = 0;
+ if( gr_info->block_type == 2 ) {
+ n++;
+ if(gr_info->mixed_block_flag)
+ n++;
+ }
+
+ pnt = stab[n][(slen>>12)&0x7];
+
+ for(i=0;i<4;i++) {
+ int num = slen & 0x7;
+ slen >>= 3;
+ if(num) {
+ for(j=0;j<(int)(pnt[i]);j++)
+ *scf++ = getbits_fast(mp, num);
+ numbits += pnt[i] * num;
+ }
+ else {
+ for(j=0;j<(int)(pnt[i]);j++)
+ *scf++ = 0;
+ }
+ }
+
+ n = (n << 1) + 1;
+ for(i=0;i<n;i++)
+ *scf++ = 0;
+
+ return numbits;
+}
+
+static int pretab1[22] = {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0};
+static int pretab2[22] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/*
+ * don't forget to apply the same changes to III_dequantize_sample_ms() !!!
+ */
+static int III_dequantize_sample(struct mpstr *mp, real xr[SBLIMIT][SSLIMIT],int *scf,
+ struct gr_info_s *gr_info,int sfreq,int part2bits)
+{
+ int shift = 1 + gr_info->scalefac_scale;
+ real *xrpnt = (real *) xr;
+ int l[3],l3;
+ int part2remain = gr_info->part2_3_length - part2bits;
+ int *me;
+
+ {
+ int bv = gr_info->big_values;
+ int region1 = gr_info->region1start;
+ int region2 = gr_info->region2start;
+
+ l3 = ((576>>1)-bv)>>1;
+/*
+ * we may lose the 'odd' bit here !!
+ * check this later again
+ */
+ if(bv <= region1) {
+ l[0] = bv; l[1] = 0; l[2] = 0;
+ }
+ else {
+ l[0] = region1;
+ if(bv <= region2) {
+ l[1] = bv - l[0]; l[2] = 0;
+ }
+ else {
+ l[1] = region2 - l[0]; l[2] = bv - region2;
+ }
+ }
+ }
+
+ if(gr_info->block_type == 2) {
+ /*
+ * decoding with short or mixed mode BandIndex table
+ */
+ int i,max[4];
+ int step=0,lwin=0,cb=0;
+ register real v = 0.0;
+ register int *m,mc;
+
+ if(gr_info->mixed_block_flag) {
+ max[3] = -1;
+ max[0] = max[1] = max[2] = 2;
+ m = map[sfreq][0];
+ me = mapend[sfreq][0];
+ }
+ else {
+ max[0] = max[1] = max[2] = max[3] = -1;
+ /* max[3] not really needed in this case */
+ m = map[sfreq][1];
+ me = mapend[sfreq][1];
+ }
+
+ mc = 0;
+ for(i=0;i<2;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+ for(;lp;lp--,mc--) {
+ register int x,y;
+ if( (!mc) ) {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ {
+ register short *val = h->table;
+ while((y=*val++)<0) {
+ if (get1bit(mp))
+ val -= y;
+ part2remain--;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if(x == 15) {
+ max[lwin] = cb;
+ part2remain -= h->linbits+1;
+ x += getbits(mp, h->linbits);
+ if(get1bit(mp))
+ *xrpnt = -ispow[x] * v;
+ else
+ *xrpnt = ispow[x] * v;
+ }
+ else if(x) {
+ max[lwin] = cb;
+ if(get1bit(mp))
+ *xrpnt = -ispow[x] * v;
+ else
+ *xrpnt = ispow[x] * v;
+ part2remain--;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ if(y == 15) {
+ max[lwin] = cb;
+ part2remain -= h->linbits+1;
+ y += getbits(mp, h->linbits);
+ if(get1bit(mp))
+ *xrpnt = -ispow[y] * v;
+ else
+ *xrpnt = ispow[y] * v;
+ }
+ else if(y) {
+ max[lwin] = cb;
+ if(get1bit(mp))
+ *xrpnt = -ispow[y] * v;
+ else
+ *xrpnt = ispow[y] * v;
+ part2remain--;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ }
+ }
+ for(;l3 && (part2remain > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ while((a=*val++)<0) {
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ a = 0;
+ break;
+ }
+ if (get1bit(mp))
+ val -= a;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr) + (*m++);
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ mc--;
+ }
+ if( (a & (0x8>>i)) ) {
+ max[lwin] = cb;
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ break;
+ }
+ if(get1bit(mp))
+ *xrpnt = -v;
+ else
+ *xrpnt = v;
+ }
+ else
+ *xrpnt = 0.0;
+ xrpnt += step;
+ }
+ }
+
+ while( m < me ) {
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr) + *m++;
+ if( (*m++) == 3)
+ step = 1;
+ else
+ step = 3;
+ m++; /* cb */
+ }
+ mc--;
+ *xrpnt = 0.0;
+ xrpnt += step;
+ *xrpnt = 0.0;
+ xrpnt += step;
+/* we could add a little opt. here:
+ * if we finished a band for window 3 or a long band
+ * further bands could copied in a simple loop without a
+ * special 'map' decoding
+ */
+ }
+
+ gr_info->maxband[0] = max[0]+1;
+ gr_info->maxband[1] = max[1]+1;
+ gr_info->maxband[2] = max[2]+1;
+ gr_info->maxbandl = max[3]+1;
+
+ {
+ int rmax = max[0] > max[1] ? max[0] : max[1];
+ rmax = (rmax > max[2] ? rmax : max[2]) + 1;
+ gr_info->maxb = rmax ? (mp->shortLimit)[sfreq][rmax] : (mp->longLimit)[sfreq][max[3]+1];
+ }
+
+ }
+ else {
+ /*
+ * decoding with 'long' BandIndex table (block_type != 2)
+ */
+ int *pretab = gr_info->preflag ? pretab1 : pretab2;
+ int i,max = -1;
+ int cb = 0;
+ register int *m = map[sfreq][2];
+ register real v = 0.0;
+ register int mc = 0;
+#if 0
+ me = mapend[sfreq][2];
+#endif
+
+ /*
+ * long hash table values
+ */
+ for(i=0;i<3;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+
+ for(;lp;lp--,mc--) {
+ int x,y;
+
+ if(!mc) {
+ mc = *m++;
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ cb = *m++;
+ }
+ {
+ register short *val = h->table;
+ while((y=*val++)<0) {
+ if (get1bit(mp))
+ val -= y;
+ part2remain--;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if (x == 15) {
+ max = cb;
+ part2remain -= h->linbits+1;
+ x += getbits(mp, h->linbits);
+ if(get1bit(mp))
+ *xrpnt++ = -ispow[x] * v;
+ else
+ *xrpnt++ = ispow[x] * v;
+ }
+ else if(x) {
+ max = cb;
+ if(get1bit(mp))
+ *xrpnt++ = -ispow[x] * v;
+ else
+ *xrpnt++ = ispow[x] * v;
+ part2remain--;
+ }
+ else
+ *xrpnt++ = 0.0;
+
+ if (y == 15) {
+ max = cb;
+ part2remain -= h->linbits+1;
+ y += getbits(mp, h->linbits);
+ if(get1bit(mp))
+ *xrpnt++ = -ispow[y] * v;
+ else
+ *xrpnt++ = ispow[y] * v;
+ }
+ else if(y) {
+ max = cb;
+ if(get1bit(mp))
+ *xrpnt++ = -ispow[y] * v;
+ else
+ *xrpnt++ = ispow[y] * v;
+ part2remain--;
+ }
+ else
+ *xrpnt++ = 0.0;
+ }
+ }
+
+ /*
+ * short (count1table) values
+ */
+ for(;l3 && (part2remain > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ while((a=*val++)<0) {
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ a = 0;
+ break;
+ }
+ if (get1bit(mp))
+ val -= a;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ cb = *m++;
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ }
+ mc--;
+ }
+ if ( (a & (0x8>>i)) ) {
+ max = cb;
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ break;
+ }
+ if(get1bit(mp))
+ *xrpnt++ = -v;
+ else
+ *xrpnt++ = v;
+ }
+ else
+ *xrpnt++ = 0.0;
+ }
+ }
+
+ /*
+ * zero part
+ */
+ for(i=(&xr[SBLIMIT][0]-xrpnt)>>1;i;i--) {
+ *xrpnt++ = 0.0;
+ *xrpnt++ = 0.0;
+ }
+
+ gr_info->maxbandl = max+1;
+ gr_info->maxb = (mp->longLimit)[sfreq][gr_info->maxbandl];
+ }
+
+ while( part2remain > 16 ) {
+ getbits(mp, 16); /* Dismiss stuffing Bits */
+ part2remain -= 16;
+ }
+ if(part2remain > 0)
+ getbits(mp, part2remain);
+ else if(part2remain < 0) {
+ ast_log(LOG_WARNING,"mpg123: Can't rewind stream by %d bits!\n",-part2remain);
+ return 1; /* -> error */
+ }
+ return 0;
+}
+
+#if 0
+static int III_dequantize_sample_ms(real xr[2][SBLIMIT][SSLIMIT],int *scf,
+ struct gr_info_s *gr_info,int sfreq,int part2bits)
+{
+ int shift = 1 + gr_info->scalefac_scale;
+ real *xrpnt = (real *) xr[1];
+ real *xr0pnt = (real *) xr[0];
+ int l[3],l3;
+ int part2remain = gr_info->part2_3_length - part2bits;
+ int *me;
+
+ {
+ int bv = gr_info->big_values;
+ int region1 = gr_info->region1start;
+ int region2 = gr_info->region2start;
+
+ l3 = ((576>>1)-bv)>>1;
+/*
+ * we may lose the 'odd' bit here !!
+ * check this later gain
+ */
+ if(bv <= region1) {
+ l[0] = bv; l[1] = 0; l[2] = 0;
+ }
+ else {
+ l[0] = region1;
+ if(bv <= region2) {
+ l[1] = bv - l[0]; l[2] = 0;
+ }
+ else {
+ l[1] = region2 - l[0]; l[2] = bv - region2;
+ }
+ }
+ }
+
+ if(gr_info->block_type == 2) {
+ int i,max[4];
+ int step=0,lwin=0,cb=0;
+ register real v = 0.0;
+ register int *m,mc = 0;
+
+ if(gr_info->mixed_block_flag) {
+ max[3] = -1;
+ max[0] = max[1] = max[2] = 2;
+ m = map[sfreq][0];
+ me = mapend[sfreq][0];
+ }
+ else {
+ max[0] = max[1] = max[2] = max[3] = -1;
+ /* max[3] not really needed in this case */
+ m = map[sfreq][1];
+ me = mapend[sfreq][1];
+ }
+
+ for(i=0;i<2;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+ for(;lp;lp--,mc--) {
+ int x,y;
+
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr[1]) + *m;
+ xr0pnt = ((real *) xr[0]) + *m++;
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ {
+ register short *val = h->table;
+ while((y=*val++)<0) {
+ if (get1bit(mp))
+ val -= y;
+ part2remain--;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if(x == 15) {
+ max[lwin] = cb;
+ part2remain -= h->linbits+1;
+ x += getbits(mp, h->linbits);
+ if(get1bit(mp)) {
+ real a = ispow[x] * v;
+ *xrpnt = *xr0pnt + a;
+ *xr0pnt -= a;
+ }
+ else {
+ real a = ispow[x] * v;
+ *xrpnt = *xr0pnt - a;
+ *xr0pnt += a;
+ }
+ }
+ else if(x) {
+ max[lwin] = cb;
+ if(get1bit(mp)) {
+ real a = ispow[x] * v;
+ *xrpnt = *xr0pnt + a;
+ *xr0pnt -= a;
+ }
+ else {
+ real a = ispow[x] * v;
+ *xrpnt = *xr0pnt - a;
+ *xr0pnt += a;
+ }
+ part2remain--;
+ }
+ else
+ *xrpnt = *xr0pnt;
+ xrpnt += step;
+ xr0pnt += step;
+
+ if(y == 15) {
+ max[lwin] = cb;
+ part2remain -= h->linbits+1;
+ y += getbits(mp, h->linbits);
+ if(get1bit(mp)) {
+ real a = ispow[y] * v;
+ *xrpnt = *xr0pnt + a;
+ *xr0pnt -= a;
+ }
+ else {
+ real a = ispow[y] * v;
+ *xrpnt = *xr0pnt - a;
+ *xr0pnt += a;
+ }
+ }
+ else if(y) {
+ max[lwin] = cb;
+ if(get1bit(mp)) {
+ real a = ispow[y] * v;
+ *xrpnt = *xr0pnt + a;
+ *xr0pnt -= a;
+ }
+ else {
+ real a = ispow[y] * v;
+ *xrpnt = *xr0pnt - a;
+ *xr0pnt += a;
+ }
+ part2remain--;
+ }
+ else
+ *xrpnt = *xr0pnt;
+ xrpnt += step;
+ xr0pnt += step;
+ }
+ }
+
+ for(;l3 && (part2remain > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ while((a=*val++)<0) {
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ a = 0;
+ break;
+ }
+ if (get1bit(mp))
+ val -= a;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr[1]) + *m;
+ xr0pnt = ((real *) xr[0]) + *m++;
+ lwin = *m++;
+ cb = *m++;
+ if(lwin == 3) {
+ v = gr_info->pow2gain[(*scf++) << shift];
+ step = 1;
+ }
+ else {
+ v = gr_info->full_gain[lwin][(*scf++) << shift];
+ step = 3;
+ }
+ }
+ mc--;
+ }
+ if( (a & (0x8>>i)) ) {
+ max[lwin] = cb;
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ break;
+ }
+ if(get1bit(mp)) {
+ *xrpnt = *xr0pnt + v;
+ *xr0pnt -= v;
+ }
+ else {
+ *xrpnt = *xr0pnt - v;
+ *xr0pnt += v;
+ }
+ }
+ else
+ *xrpnt = *xr0pnt;
+ xrpnt += step;
+ xr0pnt += step;
+ }
+ }
+
+ while( m < me ) {
+ if(!mc) {
+ mc = *m++;
+ xrpnt = ((real *) xr[1]) + *m;
+ xr0pnt = ((real *) xr[0]) + *m++;
+ if(*m++ == 3)
+ step = 1;
+ else
+ step = 3;
+ m++; /* cb */
+ }
+ mc--;
+ *xrpnt = *xr0pnt;
+ xrpnt += step;
+ xr0pnt += step;
+ *xrpnt = *xr0pnt;
+ xrpnt += step;
+ xr0pnt += step;
+/* we could add a little opt. here:
+ * if we finished a band for window 3 or a long band
+ * further bands could copied in a simple loop without a
+ * special 'map' decoding
+ */
+ }
+
+ gr_info->maxband[0] = max[0]+1;
+ gr_info->maxband[1] = max[1]+1;
+ gr_info->maxband[2] = max[2]+1;
+ gr_info->maxbandl = max[3]+1;
+
+ {
+ int rmax = max[0] > max[1] ? max[0] : max[1];
+ rmax = (rmax > max[2] ? rmax : max[2]) + 1;
+ gr_info->maxb = rmax ? (mp->shortLimit)[sfreq][rmax] : (mp->longLimit)[sfreq][max[3]+1];
+ }
+ }
+ else {
+ int *pretab = gr_info->preflag ? pretab1 : pretab2;
+ int i,max = -1;
+ int cb = 0;
+ register int mc=0,*m = map[sfreq][2];
+ register real v = 0.0;
+#if 0
+ me = mapend[sfreq][2];
+#endif
+
+ for(i=0;i<3;i++) {
+ int lp = l[i];
+ struct newhuff *h = ht+gr_info->table_select[i];
+
+ for(;lp;lp--,mc--) {
+ int x,y;
+ if(!mc) {
+ mc = *m++;
+ cb = *m++;
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ }
+ {
+ register short *val = h->table;
+ while((y=*val++)<0) {
+ if (get1bit(mp))
+ val -= y;
+ part2remain--;
+ }
+ x = y >> 4;
+ y &= 0xf;
+ }
+ if (x == 15) {
+ max = cb;
+ part2remain -= h->linbits+1;
+ x += getbits(mp, h->linbits);
+ if(get1bit(mp)) {
+ real a = ispow[x] * v;
+ *xrpnt++ = *xr0pnt + a;
+ *xr0pnt++ -= a;
+ }
+ else {
+ real a = ispow[x] * v;
+ *xrpnt++ = *xr0pnt - a;
+ *xr0pnt++ += a;
+ }
+ }
+ else if(x) {
+ max = cb;
+ if(get1bit(mp)) {
+ real a = ispow[x] * v;
+ *xrpnt++ = *xr0pnt + a;
+ *xr0pnt++ -= a;
+ }
+ else {
+ real a = ispow[x] * v;
+ *xrpnt++ = *xr0pnt - a;
+ *xr0pnt++ += a;
+ }
+ part2remain--;
+ }
+ else
+ *xrpnt++ = *xr0pnt++;
+
+ if (y == 15) {
+ max = cb;
+ part2remain -= h->linbits+1;
+ y += getbits(mp, h->linbits);
+ if(get1bit(mp)) {
+ real a = ispow[y] * v;
+ *xrpnt++ = *xr0pnt + a;
+ *xr0pnt++ -= a;
+ }
+ else {
+ real a = ispow[y] * v;
+ *xrpnt++ = *xr0pnt - a;
+ *xr0pnt++ += a;
+ }
+ }
+ else if(y) {
+ max = cb;
+ if(get1bit(mp)) {
+ real a = ispow[y] * v;
+ *xrpnt++ = *xr0pnt + a;
+ *xr0pnt++ -= a;
+ }
+ else {
+ real a = ispow[y] * v;
+ *xrpnt++ = *xr0pnt - a;
+ *xr0pnt++ += a;
+ }
+ part2remain--;
+ }
+ else
+ *xrpnt++ = *xr0pnt++;
+ }
+ }
+
+ for(;l3 && (part2remain > 0);l3--) {
+ struct newhuff *h = htc+gr_info->count1table_select;
+ register short *val = h->table,a;
+
+ while((a=*val++)<0) {
+ part2remain--;
+ if(part2remain < 0) {
+ part2remain++;
+ a = 0;
+ break;
+ }
+ if (get1bit(mp))
+ val -= a;
+ }
+
+ for(i=0;i<4;i++) {
+ if(!(i & 1)) {
+ if(!mc) {
+ mc = *m++;
+ cb = *m++;
+ v = gr_info->pow2gain[((*scf++) + (*pretab++)) << shift];
+ }
+ mc--;
+ }
+ if ( (a & (0x8>>i)) ) {
+ max = cb;
+ part2remain--;
+ if(part2remain <= 0) {
+ part2remain++;
+ break;
+ }
+ if(get1bit(mp)) {
+ *xrpnt++ = *xr0pnt + v;
+ *xr0pnt++ -= v;
+ }
+ else {
+ *xrpnt++ = *xr0pnt - v;
+ *xr0pnt++ += v;
+ }
+ }
+ else
+ *xrpnt++ = *xr0pnt++;
+ }
+ }
+ for(i=(&xr[1][SBLIMIT][0]-xrpnt)>>1;i;i--) {
+ *xrpnt++ = *xr0pnt++;
+ *xrpnt++ = *xr0pnt++;
+ }
+
+ gr_info->maxbandl = max+1;
+ gr_info->maxb = longLimit[sfreq][gr_info->maxbandl];
+ }
+
+ while ( part2remain > 16 ) {
+ getbits(mp, 16); /* Dismiss stuffing Bits */
+ part2remain -= 16;
+ }
+ if(part2remain > 0 )
+ getbits(mp, part2remain);
+ else if(part2remain < 0) {
+ ast_log(LOG_WARNING,"mpg123_ms: Can't rewind stream by %d bits!\n",-part2remain);
+ return 1; /* -> error */
+ }
+ return 0;
+}
+#endif
+
+/*
+ * III_stereo: calculate real channel values for Joint-I-Stereo-mode
+ */
+static void III_i_stereo(real xr_buf[2][SBLIMIT][SSLIMIT],int *scalefac,
+ struct gr_info_s *gr_info,int sfreq,int ms_stereo,int lsf)
+{
+ real (*xr)[SBLIMIT*SSLIMIT] = (real (*)[SBLIMIT*SSLIMIT] ) xr_buf;
+ struct bandInfoStruct *bi = &bandInfo[sfreq];
+ real *tab1,*tab2;
+
+ if(lsf) {
+ int p = gr_info->scalefac_compress & 0x1;
+ if(ms_stereo) {
+ tab1 = pow1_2[p]; tab2 = pow2_2[p];
+ }
+ else {
+ tab1 = pow1_1[p]; tab2 = pow2_1[p];
+ }
+ }
+ else {
+ if(ms_stereo) {
+ tab1 = tan1_2; tab2 = tan2_2;
+ }
+ else {
+ tab1 = tan1_1; tab2 = tan2_1;
+ }
+ }
+
+ if (gr_info->block_type == 2)
+ {
+ int lwin,do_l = 0;
+ if( gr_info->mixed_block_flag )
+ do_l = 1;
+
+ for (lwin=0;lwin<3;lwin++) /* process each window */
+ {
+ /* get first band with zero values */
+ int is_p,sb,idx,sfb = gr_info->maxband[lwin]; /* sfb is minimal 3 for mixed mode */
+ if(sfb > 3)
+ do_l = 0;
+
+ for(;sfb<12;sfb++)
+ {
+ is_p = scalefac[sfb*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ sb = bi->shortDiff[sfb];
+ idx = bi->shortIdx[sfb] + lwin;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for (; sb > 0; sb--,idx+=3)
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ }
+
+#if 1
+/* in the original: copy 10 to 11 , here: copy 11 to 12
+maybe still wrong??? (copy 12 to 13?) */
+ is_p = scalefac[11*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[12];
+ idx = bi->shortIdx[12] + lwin;
+#else
+ is_p = scalefac[10*3+lwin-gr_info->mixed_block_flag]; /* scale: 0-15 */
+ sb = bi->shortDiff[11];
+ idx = bi->shortIdx[11] + lwin;
+#endif
+ if(is_p != 7)
+ {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx+=3 )
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ } /* end for(lwin; .. ; . ) */
+
+ if (do_l)
+ {
+/* also check l-part, if ALL bands in the three windows are 'empty'
+ * and mode = mixed_mode
+ */
+ int sfb = gr_info->maxbandl;
+ int idx = bi->longIdx[sfb];
+
+ for ( ; sfb<8; sfb++ )
+ {
+ int sb = bi->longDiff[sfb];
+ int is_p = scalefac[sfb]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx++)
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ else
+ idx += sb;
+ }
+ }
+ }
+ else /* ((gr_info->block_type != 2)) */
+ {
+ int sfb = gr_info->maxbandl;
+ int is_p,idx = bi->longIdx[sfb];
+ for ( ; sfb<21; sfb++)
+ {
+ int sb = bi->longDiff[sfb];
+ is_p = scalefac[sfb]; /* scale: 0-15 */
+ if(is_p != 7) {
+ real t1,t2;
+ t1 = tab1[is_p]; t2 = tab2[is_p];
+ for ( ; sb > 0; sb--,idx++)
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ else
+ idx += sb;
+ }
+
+ is_p = scalefac[20]; /* copy l-band 20 to l-band 21 */
+ if(is_p != 7)
+ {
+ int sb;
+ real t1 = tab1[is_p],t2 = tab2[is_p];
+
+ for ( sb = bi->longDiff[21]; sb > 0; sb--,idx++ )
+ {
+ real v = xr[0][idx];
+ xr[0][idx] = v * t1;
+ xr[1][idx] = v * t2;
+ }
+ }
+ } /* ... */
+}
+
+static void III_antialias(real xr[SBLIMIT][SSLIMIT],struct gr_info_s *gr_info)
+{
+ int sblim;
+
+ if(gr_info->block_type == 2)
+ {
+ if(!gr_info->mixed_block_flag)
+ return;
+ sblim = 1;
+ }
+ else {
+ sblim = gr_info->maxb-1;
+ }
+
+ /* 31 alias-reduction operations between each pair of sub-bands */
+ /* with 8 butterflies between each pair */
+
+ {
+ int sb;
+ real *xr1=(real *) xr[1];
+
+ for(sb=sblim;sb;sb--,xr1+=10)
+ {
+ int ss;
+ real *cs=aa_cs,*ca=aa_ca;
+ real *xr2 = xr1;
+
+ for(ss=7;ss>=0;ss--)
+ { /* upper and lower butterfly inputs */
+ register real bu = *--xr2,bd = *xr1;
+ *xr2 = (bu * (*cs) ) - (bd * (*ca) );
+ *xr1++ = (bd * (*cs++) ) + (bu * (*ca++) );
+ }
+ }
+ }
+}
+
+/*
+ DCT insipired by Jeff Tsay's DCT from the maplay package
+ this is an optimized version with manual unroll.
+
+ References:
+ [1] S. Winograd: "On Computing the Discrete Fourier Transform",
+ Mathematics of Computation, Volume 32, Number 141, January 1978,
+ Pages 175-199
+*/
+
+static void dct36(real *inbuf,real *o1,real *o2,real *wintab,real *tsbuf)
+{
+ {
+ register real *in = inbuf;
+
+ in[17]+=in[16]; in[16]+=in[15]; in[15]+=in[14];
+ in[14]+=in[13]; in[13]+=in[12]; in[12]+=in[11];
+ in[11]+=in[10]; in[10]+=in[9]; in[9] +=in[8];
+ in[8] +=in[7]; in[7] +=in[6]; in[6] +=in[5];
+ in[5] +=in[4]; in[4] +=in[3]; in[3] +=in[2];
+ in[2] +=in[1]; in[1] +=in[0];
+
+ in[17]+=in[15]; in[15]+=in[13]; in[13]+=in[11]; in[11]+=in[9];
+ in[9] +=in[7]; in[7] +=in[5]; in[5] +=in[3]; in[3] +=in[1];
+
+
+ {
+
+#define MACRO0(v) { \
+ real tmp; \
+ out2[9+(v)] = (tmp = sum0 + sum1) * w[27+(v)]; \
+ out2[8-(v)] = tmp * w[26-(v)]; } \
+ sum0 -= sum1; \
+ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + sum0 * w[8-(v)]; \
+ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + sum0 * w[9+(v)];
+#define MACRO1(v) { \
+ real sum0,sum1; \
+ sum0 = tmp1a + tmp2a; \
+ sum1 = (tmp1b + tmp2b) * tfcos36[(v)]; \
+ MACRO0(v); }
+#define MACRO2(v) { \
+ real sum0,sum1; \
+ sum0 = tmp2a - tmp1a; \
+ sum1 = (tmp2b - tmp1b) * tfcos36[(v)]; \
+ MACRO0(v); }
+
+ register const real *c = COS9;
+ register real *out2 = o2;
+ register real *w = wintab;
+ register real *out1 = o1;
+ register real *ts = tsbuf;
+
+ real ta33,ta66,tb33,tb66;
+
+ ta33 = in[2*3+0] * c[3];
+ ta66 = in[2*6+0] * c[6];
+ tb33 = in[2*3+1] * c[3];
+ tb66 = in[2*6+1] * c[6];
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = in[2*1+0] * c[1] + ta33 + in[2*5+0] * c[5] + in[2*7+0] * c[7];
+ tmp1b = in[2*1+1] * c[1] + tb33 + in[2*5+1] * c[5] + in[2*7+1] * c[7];
+ tmp2a = in[2*0+0] + in[2*2+0] * c[2] + in[2*4+0] * c[4] + ta66 + in[2*8+0] * c[8];
+ tmp2b = in[2*0+1] + in[2*2+1] * c[2] + in[2*4+1] * c[4] + tb66 + in[2*8+1] * c[8];
+
+ MACRO1(0);
+ MACRO2(8);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = ( in[2*1+0] - in[2*5+0] - in[2*7+0] ) * c[3];
+ tmp1b = ( in[2*1+1] - in[2*5+1] - in[2*7+1] ) * c[3];
+ tmp2a = ( in[2*2+0] - in[2*4+0] - in[2*8+0] ) * c[6] - in[2*6+0] + in[2*0+0];
+ tmp2b = ( in[2*2+1] - in[2*4+1] - in[2*8+1] ) * c[6] - in[2*6+1] + in[2*0+1];
+
+ MACRO1(1);
+ MACRO2(7);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = in[2*1+0] * c[5] - ta33 - in[2*5+0] * c[7] + in[2*7+0] * c[1];
+ tmp1b = in[2*1+1] * c[5] - tb33 - in[2*5+1] * c[7] + in[2*7+1] * c[1];
+ tmp2a = in[2*0+0] - in[2*2+0] * c[8] - in[2*4+0] * c[2] + ta66 + in[2*8+0] * c[4];
+ tmp2b = in[2*0+1] - in[2*2+1] * c[8] - in[2*4+1] * c[2] + tb66 + in[2*8+1] * c[4];
+
+ MACRO1(2);
+ MACRO2(6);
+ }
+
+ {
+ real tmp1a,tmp2a,tmp1b,tmp2b;
+ tmp1a = in[2*1+0] * c[7] - ta33 + in[2*5+0] * c[1] - in[2*7+0] * c[5];
+ tmp1b = in[2*1+1] * c[7] - tb33 + in[2*5+1] * c[1] - in[2*7+1] * c[5];
+ tmp2a = in[2*0+0] - in[2*2+0] * c[4] + in[2*4+0] * c[8] + ta66 - in[2*8+0] * c[2];
+ tmp2b = in[2*0+1] - in[2*2+1] * c[4] + in[2*4+1] * c[8] + tb66 - in[2*8+1] * c[2];
+
+ MACRO1(3);
+ MACRO2(5);
+ }
+
+ {
+ real sum0,sum1;
+ sum0 = in[2*0+0] - in[2*2+0] + in[2*4+0] - in[2*6+0] + in[2*8+0];
+ sum1 = (in[2*0+1] - in[2*2+1] + in[2*4+1] - in[2*6+1] + in[2*8+1] ) * tfcos36[4];
+ MACRO0(4);
+ }
+ }
+
+ }
+}
+
+/*
+ * new DCT12
+ */
+static void dct12(real *in,real *rawout1,real *rawout2,register real *wi,register real *ts)
+{
+#define DCT12_PART1 \
+ in5 = in[5*3]; \
+ in5 += (in4 = in[4*3]); \
+ in4 += (in3 = in[3*3]); \
+ in3 += (in2 = in[2*3]); \
+ in2 += (in1 = in[1*3]); \
+ in1 += (in0 = in[0*3]); \
+ \
+ in5 += in3; in3 += in1; \
+ \
+ in2 *= COS6_1; \
+ in3 *= COS6_1; \
+
+#define DCT12_PART2 \
+ in0 += in4 * COS6_2; \
+ \
+ in4 = in0 + in2; \
+ in0 -= in2; \
+ \
+ in1 += in5 * COS6_2; \
+ \
+ in5 = (in1 + in3) * tfcos12[0]; \
+ in1 = (in1 - in3) * tfcos12[2]; \
+ \
+ in3 = in4 + in5; \
+ in4 -= in5; \
+ \
+ in2 = in0 + in1; \
+ in0 -= in1;
+
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out1 = rawout1;
+ ts[SBLIMIT*0] = out1[0]; ts[SBLIMIT*1] = out1[1]; ts[SBLIMIT*2] = out1[2];
+ ts[SBLIMIT*3] = out1[3]; ts[SBLIMIT*4] = out1[4]; ts[SBLIMIT*5] = out1[5];
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ ts[(17-1)*SBLIMIT] = out1[17-1] + tmp0 * wi[11-1];
+ ts[(12+1)*SBLIMIT] = out1[12+1] + tmp0 * wi[6+1];
+ ts[(6 +1)*SBLIMIT] = out1[6 +1] + tmp1 * wi[1];
+ ts[(11-1)*SBLIMIT] = out1[11-1] + tmp1 * wi[5-1];
+ }
+
+ DCT12_PART2
+
+ ts[(17-0)*SBLIMIT] = out1[17-0] + in2 * wi[11-0];
+ ts[(12+0)*SBLIMIT] = out1[12+0] + in2 * wi[6+0];
+ ts[(12+2)*SBLIMIT] = out1[12+2] + in3 * wi[6+2];
+ ts[(17-2)*SBLIMIT] = out1[17-2] + in3 * wi[11-2];
+
+ ts[(6+0)*SBLIMIT] = out1[6+0] + in0 * wi[0];
+ ts[(11-0)*SBLIMIT] = out1[11-0] + in0 * wi[5-0];
+ ts[(6+2)*SBLIMIT] = out1[6+2] + in4 * wi[2];
+ ts[(11-2)*SBLIMIT] = out1[11-2] + in4 * wi[5-2];
+ }
+
+ in++;
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out2 = rawout2;
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[5-1] = tmp0 * wi[11-1];
+ out2[0+1] = tmp0 * wi[6+1];
+ ts[(12+1)*SBLIMIT] += tmp1 * wi[1];
+ ts[(17-1)*SBLIMIT] += tmp1 * wi[5-1];
+ }
+
+ DCT12_PART2
+
+ out2[5-0] = in2 * wi[11-0];
+ out2[0+0] = in2 * wi[6+0];
+ out2[0+2] = in3 * wi[6+2];
+ out2[5-2] = in3 * wi[11-2];
+
+ ts[(12+0)*SBLIMIT] += in0 * wi[0];
+ ts[(17-0)*SBLIMIT] += in0 * wi[5-0];
+ ts[(12+2)*SBLIMIT] += in4 * wi[2];
+ ts[(17-2)*SBLIMIT] += in4 * wi[5-2];
+ }
+
+ in++;
+
+ {
+ real in0,in1,in2,in3,in4,in5;
+ register real *out2 = rawout2;
+ out2[12]=out2[13]=out2[14]=out2[15]=out2[16]=out2[17]=0.0;
+
+ DCT12_PART1
+
+ {
+ real tmp0,tmp1 = (in0 - in4);
+ {
+ real tmp2 = (in1 - in5) * tfcos12[1];
+ tmp0 = tmp1 + tmp2;
+ tmp1 -= tmp2;
+ }
+ out2[11-1] = tmp0 * wi[11-1];
+ out2[6 +1] = tmp0 * wi[6+1];
+ out2[0+1] += tmp1 * wi[1];
+ out2[5-1] += tmp1 * wi[5-1];
+ }
+
+ DCT12_PART2
+
+ out2[11-0] = in2 * wi[11-0];
+ out2[6 +0] = in2 * wi[6+0];
+ out2[6 +2] = in3 * wi[6+2];
+ out2[11-2] = in3 * wi[11-2];
+
+ out2[0+0] += in0 * wi[0];
+ out2[5-0] += in0 * wi[5-0];
+ out2[0+2] += in4 * wi[2];
+ out2[5-2] += in4 * wi[5-2];
+ }
+}
+
+/*
+ * III_hybrid
+ */
+static void III_hybrid(struct mpstr *mp, real fsIn[SBLIMIT][SSLIMIT],real tsOut[SSLIMIT][SBLIMIT],
+ int ch,struct gr_info_s *gr_info)
+{
+ real *tspnt = (real *) tsOut;
+ real (*block)[2][SBLIMIT*SSLIMIT] = mp->hybrid_block;
+ int *blc = mp->hybrid_blc;
+ real *rawout1,*rawout2;
+ int bt;
+ int sb = 0;
+
+ {
+ int b = blc[ch];
+ rawout1=block[b][ch];
+ b=-b+1;
+ rawout2=block[b][ch];
+ blc[ch] = b;
+ }
+
+
+ if(gr_info->mixed_block_flag) {
+ sb = 2;
+ dct36(fsIn[0],rawout1,rawout2,win[0],tspnt);
+ dct36(fsIn[1],rawout1+18,rawout2+18,win1[0],tspnt+1);
+ rawout1 += 36; rawout2 += 36; tspnt += 2;
+ }
+
+ bt = gr_info->block_type;
+ if(bt == 2) {
+ for (; sb<gr_info->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) {
+ dct12(fsIn[sb],rawout1,rawout2,win[2],tspnt);
+ dct12(fsIn[sb+1],rawout1+18,rawout2+18,win1[2],tspnt+1);
+ }
+ }
+ else {
+ for (; sb<gr_info->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) {
+ dct36(fsIn[sb],rawout1,rawout2,win[bt],tspnt);
+ dct36(fsIn[sb+1],rawout1+18,rawout2+18,win1[bt],tspnt+1);
+ }
+ }
+
+ for(;sb<SBLIMIT;sb++,tspnt++) {
+ int i;
+ for(i=0;i<SSLIMIT;i++) {
+ tspnt[i*SBLIMIT] = *rawout1++;
+ *rawout2++ = 0.0;
+ }
+ }
+}
+
+/*
+ * main layer3 handler
+ */
+int do_layer3(struct mpstr *mp,unsigned char *pcm_sample,int *pcm_point)
+{
+
+ int gr, ch, ss,clip=0;
+ int scalefacs[39]; /* max 39 for short[13][3] mode, mixed: 38, long: 22 */
+ struct III_sideinfo sideinfo;
+ struct frame *fr;
+ int stereo, single, sfreq;
+ int ms_stereo,i_stereo;
+ int stereo1,granules;
+
+ fr = &(mp->fr);
+ stereo = fr->stereo;
+ single = fr->single;
+ sfreq = fr->sampling_frequency;
+
+
+
+ if(stereo == 1) { /* stream is mono */
+ stereo1 = 1;
+ single = 0;
+ }
+ else if(single >= 0) /* stream is stereo, but force to mono */
+ stereo1 = 1;
+ else
+ stereo1 = 2;
+
+ if(fr->mode == MPG_MD_JOINT_STEREO) {
+ ms_stereo = fr->mode_ext & 0x2;
+ i_stereo = fr->mode_ext & 0x1;
+ }
+ else
+ ms_stereo = i_stereo = 0;
+
+ if(fr->lsf) {
+ granules = 1;
+ if (III_get_side_info_2(mp, &sideinfo,stereo,ms_stereo,sfreq,single))
+ return (MP3_ERR);
+ }
+ else {
+ granules = 2;
+#ifdef MPEG1
+ if ( III_get_side_info_1(mp, &sideinfo,stereo,ms_stereo,sfreq,single))
+ return (MP3_ERR);
+
+#else
+ ast_log(LOG_WARNING,"Not supported\n");
+#endif
+ }
+
+ if(set_pointer(mp, sideinfo.main_data_begin) == MP3_ERR)
+ return 0;
+
+ for (gr=0;gr<granules;gr++)
+ {
+ static real hybridIn[2][SBLIMIT][SSLIMIT];
+ static real hybridOut[2][SSLIMIT][SBLIMIT];
+
+ {
+ struct gr_info_s *gr_info = &(sideinfo.ch[0].gr[gr]);
+ long part2bits;
+ if(fr->lsf)
+ part2bits = III_get_scale_factors_2(mp, scalefacs,gr_info,0);
+ else {
+#ifdef MPEG1
+ part2bits = III_get_scale_factors_1(mp, scalefacs,gr_info);
+#else
+ ast_log(LOG_WARNING,"Not supported\n");
+#endif
+ }
+ if(III_dequantize_sample(mp, hybridIn[0], scalefacs,gr_info,sfreq,part2bits))
+ return (MP3_ERR);
+ }
+ if(stereo == 2) {
+ struct gr_info_s *gr_info = &(sideinfo.ch[1].gr[gr]);
+ long part2bits;
+ if(fr->lsf)
+ part2bits = III_get_scale_factors_2(mp, scalefacs,gr_info,i_stereo);
+ else {
+#ifdef MPEG1
+ part2bits = III_get_scale_factors_1(mp, scalefacs,gr_info);
+#else
+ ast_log(LOG_WARNING,"Not supported\n");
+#endif
+ }
+
+ if(III_dequantize_sample(mp, hybridIn[1],scalefacs,gr_info,sfreq,part2bits))
+ return (MP3_ERR);
+
+ if(ms_stereo) {
+ int i;
+ for(i=0;i<SBLIMIT*SSLIMIT;i++) {
+ real tmp0,tmp1;
+ tmp0 = ((real *) hybridIn[0])[i];
+ tmp1 = ((real *) hybridIn[1])[i];
+ ((real *) hybridIn[1])[i] = tmp0 - tmp1;
+ ((real *) hybridIn[0])[i] = tmp0 + tmp1;
+ }
+ }
+
+ if(i_stereo)
+ III_i_stereo(hybridIn,scalefacs,gr_info,sfreq,ms_stereo,fr->lsf);
+
+ if(ms_stereo || i_stereo || (single == 3) ) {
+ if(gr_info->maxb > sideinfo.ch[0].gr[gr].maxb)
+ sideinfo.ch[0].gr[gr].maxb = gr_info->maxb;
+ else
+ gr_info->maxb = sideinfo.ch[0].gr[gr].maxb;
+ }
+
+ switch(single) {
+ case 3:
+ {
+ register int i;
+ register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1];
+ for(i=0;i<SSLIMIT*gr_info->maxb;i++,in0++)
+ *in0 = (*in0 + *in1++); /* *0.5 done by pow-scale */
+ }
+ break;
+ case 1:
+ {
+ register int i;
+ register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1];
+ for(i=0;i<SSLIMIT*gr_info->maxb;i++)
+ *in0++ = *in1++;
+ }
+ break;
+ }
+ }
+
+ for(ch=0;ch<stereo1;ch++) {
+ struct gr_info_s *gr_info = &(sideinfo.ch[ch].gr[gr]);
+ III_antialias(hybridIn[ch],gr_info);
+ III_hybrid(mp, hybridIn[ch], hybridOut[ch], ch,gr_info);
+ }
+
+ for(ss=0;ss<SSLIMIT;ss++) {
+ if(single >= 0) {
+ clip += synth_ntom_mono(mp,hybridOut[0][ss],pcm_sample,pcm_point);
+ }
+ else {
+ int p1 = *pcm_point;
+ clip += synth_ntom(mp,hybridOut[0][ss],0,pcm_sample,&p1);
+ clip += synth_ntom(mp,hybridOut[1][ss],1,pcm_sample,pcm_point);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
diff --git a/addons/mp3/mpg123.h b/addons/mp3/mpg123.h
new file mode 100644
index 000000000..e607cc277
--- /dev/null
+++ b/addons/mp3/mpg123.h
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#ifndef WIN32
+#include <sys/signal.h>
+#include <unistd.h>
+#endif
+
+#include <math.h>
+
+#ifdef _WIN32
+# undef WIN32
+# define WIN32
+
+# define M_PI 3.14159265358979323846
+# define M_SQRT2 1.41421356237309504880
+# define REAL_IS_FLOAT
+# define NEW_DCT9
+
+# define random rand
+# define srandom srand
+
+#endif
+
+#ifdef REAL_IS_FLOAT
+# define real float
+#elif defined(REAL_IS_LONG_DOUBLE)
+# define real long double
+#else
+# define real double
+#endif
+
+#ifdef __GNUC__
+#define INLINE inline
+#else
+#define INLINE
+#endif
+
+/* AUDIOBUFSIZE = n*64 with n=1,2,3 ... */
+#define AUDIOBUFSIZE 16384
+
+#define FALSE 0
+#define TRUE 1
+
+#define SBLIMIT 32
+#define SSLIMIT 18
+
+#define MPG_MD_STEREO 0
+#define MPG_MD_JOINT_STEREO 1
+#define MPG_MD_DUAL_CHANNEL 2
+#define MPG_MD_MONO 3
+
+#define MAXFRAMESIZE 1792
+
+
+/* Pre Shift fo 16 to 8 bit converter table */
+#define AUSHIFT (3)
+
+struct frame {
+ int stereo;
+ int jsbound;
+ int single;
+ int lsf;
+ int mpeg25;
+ int header_change;
+ int lay;
+ int error_protection;
+ int bitrate_index;
+ int sampling_frequency;
+ int padding;
+ int extension;
+ int mode;
+ int mode_ext;
+ int copyright;
+ int original;
+ int emphasis;
+ int framesize; /* computed framesize */
+};
+
+struct parameter {
+ int quiet; /* shut up! */
+ int tryresync; /* resync stream after error */
+ int verbose; /* verbose level */
+ int checkrange;
+};
+
+extern int decode_header(struct frame *fr,unsigned long newhead);
+
+
+
+struct gr_info_s {
+ int scfsi;
+ unsigned part2_3_length;
+ unsigned big_values;
+ unsigned scalefac_compress;
+ unsigned block_type;
+ unsigned mixed_block_flag;
+ unsigned table_select[3];
+ unsigned subblock_gain[3];
+ unsigned maxband[3];
+ unsigned maxbandl;
+ unsigned maxb;
+ unsigned region1start;
+ unsigned region2start;
+ unsigned preflag;
+ unsigned scalefac_scale;
+ unsigned count1table_select;
+ real *full_gain[3];
+ real *pow2gain;
+};
+
+struct III_sideinfo
+{
+ unsigned main_data_begin;
+ unsigned private_bits;
+ struct {
+ struct gr_info_s gr[2];
+ } ch[2];
+};
+
+struct pcm_workingsample
+{
+ int bitindex;
+ unsigned char *wordpointer;
+};
+
+
+extern long freqs[9];
+extern struct parameter param;
+extern real *pnts[5];
+
diff --git a/addons/mp3/mpglib.h b/addons/mp3/mpglib.h
new file mode 100644
index 000000000..0613222e6
--- /dev/null
+++ b/addons/mp3/mpglib.h
@@ -0,0 +1,75 @@
+
+struct buf {
+ unsigned char *pnt;
+ long size;
+ long pos;
+ struct buf *next;
+ struct buf *prev;
+};
+
+struct framebuf {
+ struct buf *buf;
+ long pos;
+ struct frame *next;
+ struct frame *prev;
+};
+
+struct mpstr {
+ struct buf *head,*tail;
+ int bsize;
+ int framesize;
+ int fsizeold;
+ struct frame fr;
+ unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
+ real hybrid_block[2][2][SBLIMIT*SSLIMIT];
+ int hybrid_blc[2];
+ unsigned long header;
+ int bsnum;
+ real synth_buffs[2][2][0x110];
+ int synth_bo;
+ long outscale; /* volume control default value 32768 */
+ long outsamplerate; /* raw output rate default same as mp3 sample rate*/
+ struct pcm_workingsample worksample; /* keep the state of the working sample for threads */
+ int initmp3; /* flag for first initialisation */
+ int longLimit[9][23]; /*sample limits re setting volume */
+ int shortLimit[9][14];
+ real decwin[512+32]; /* scale table */
+
+ };
+
+#define BOOL int
+
+#define MP3_ERR -1
+#define MP3_OK 0
+#define MP3_NEED_MORE 1
+
+
+void InitMP3Constants(void);
+BOOL InitMP3(struct mpstr *mp, long outscale);
+int decodeMP3(struct mpstr *mp,char *inmemory,int inmemsize,
+ char *outmemory,int outmemsize,int *done);
+void ExitMP3(struct mpstr *mp);
+
+extern int synth_ntom_set_step(long,long);
+extern int synth_ntom(struct mpstr *mp, real *bandPtr,int channel,unsigned char *out,int *pnt);
+extern int synth_ntom_mono (struct mpstr *mp, real *bandPtr,unsigned char *samples,int *pnt);
+extern int synth_ntom_8bit (real *,int,unsigned char *,int *);
+extern int synth_ntom_mono2stereo (real *,unsigned char *,int *);
+extern int synth_ntom_8bit_mono (real *,unsigned char *,int *);
+extern int synth_ntom_8bit_mono2stereo (real *,unsigned char *,int *);
+
+extern void init_layer3_sample_limits(struct mpstr *mp, int down_sample_sblimit);
+extern void init_layer3_const(void);
+extern int do_layer3(struct mpstr *mp,unsigned char *,int *);
+
+extern void make_decode_tables_scale(struct mpstr *mp, long scaleval);
+extern void make_decode_tables_const(void);
+extern void make_conv16to8_table(int);
+
+extern void dct64(real *,real *,real *);
+
+extern unsigned int get1bit(struct mpstr *mp);
+extern unsigned int getbits(struct mpstr *mp, int);
+extern unsigned int getbits_fast(struct mpstr *mp, int);
+extern int set_pointer(struct mpstr *mp, long backstep);
+
diff --git a/addons/mp3/tabinit.c b/addons/mp3/tabinit.c
new file mode 100644
index 000000000..d3e49f500
--- /dev/null
+++ b/addons/mp3/tabinit.c
@@ -0,0 +1,81 @@
+
+#include <stdlib.h>
+
+#include "mpg123.h"
+#include "mpglib.h"
+
+static real cos64[16],cos32[8],cos16[4],cos8[2],cos4[1];
+real *pnts[] = { cos64,cos32,cos16,cos8,cos4 };
+
+static long intwinbase[] = {
+ 0, -1, -1, -1, -1, -1, -1, -2, -2, -2,
+ -2, -3, -3, -4, -4, -5, -5, -6, -7, -7,
+ -8, -9, -10, -11, -13, -14, -16, -17, -19, -21,
+ -24, -26, -29, -31, -35, -38, -41, -45, -49, -53,
+ -58, -63, -68, -73, -79, -85, -91, -97, -104, -111,
+ -117, -125, -132, -139, -147, -154, -161, -169, -176, -183,
+ -190, -196, -202, -208, -213, -218, -222, -225, -227, -228,
+ -228, -227, -224, -221, -215, -208, -200, -189, -177, -163,
+ -146, -127, -106, -83, -57, -29, 2, 36, 72, 111,
+ 153, 197, 244, 294, 347, 401, 459, 519, 581, 645,
+ 711, 779, 848, 919, 991, 1064, 1137, 1210, 1283, 1356,
+ 1428, 1498, 1567, 1634, 1698, 1759, 1817, 1870, 1919, 1962,
+ 2001, 2032, 2057, 2075, 2085, 2087, 2080, 2063, 2037, 2000,
+ 1952, 1893, 1822, 1739, 1644, 1535, 1414, 1280, 1131, 970,
+ 794, 605, 402, 185, -45, -288, -545, -814, -1095, -1388,
+ -1692, -2006, -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788,
+ -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, -7910, -8209,
+ -8491, -8755, -8998, -9219, -9416, -9585, -9727, -9838, -9916, -9959,
+ -9966, -9935, -9863, -9750, -9592, -9389, -9139, -8840, -8492, -8092,
+ -7640, -7134, -6574, -5959, -5288, -4561, -3776, -2935, -2037, -1082,
+ -70, 998, 2122, 3300, 4533, 5818, 7154, 8540, 9975, 11455,
+ 12980, 14548, 16155, 17799, 19478, 21189, 22929, 24694, 26482, 28289,
+ 30112, 31947, 33791, 35640, 37489, 39336, 41176, 43006, 44821, 46617,
+ 48390, 50137, 51853, 53534, 55178, 56778, 58333, 59838, 61289, 62684,
+ 64019, 65290, 66494, 67629, 68692, 69679, 70590, 71420, 72169, 72835,
+ 73415, 73908, 74313, 74630, 74856, 74992, 75038 };
+
+void make_decode_tables_const(void)
+{
+ int i,k,kr,divv;
+ real *costab;
+
+ for(i=0;i<5;i++)
+ {
+ kr=0x10>>i; divv=0x40>>i;
+ costab = pnts[i];
+ for(k=0;k<kr;k++)
+ costab[k] = 1.0 / (2.0 * cos(M_PI * ((double) k * 2.0 + 1.0) / (double) divv));
+ }
+
+}
+
+void make_decode_tables_scale(struct mpstr *mp, long scaleval)
+{
+ int i,j;
+ real *table;
+
+ table = mp->decwin;
+ scaleval = -scaleval;
+ for(i=0,j=0;i<256;i++,j++,table+=32)
+ {
+ if(table < (mp->decwin)+512+16)
+ table[16] = table[0] = (double) intwinbase[j] / 65536.0 * (double) scaleval;
+ if(i % 32 == 31)
+ table -= 1023;
+ if(i % 64 == 63)
+ scaleval = - scaleval;
+ }
+
+ for( /* i=256 */ ;i<512;i++,j--,table+=32)
+ {
+ if(table < (mp->decwin)+512+16)
+ table[16] = table[0] = (double) intwinbase[j] / 65536.0 * (double) scaleval;
+ if(i % 32 == 31)
+ table -= 1023;
+ if(i % 64 == 63)
+ scaleval = - scaleval;
+ }
+}
+
+
diff --git a/addons/ooh323c/COPYING b/addons/ooh323c/COPYING
new file mode 100644
index 000000000..b8306825e
--- /dev/null
+++ b/addons/ooh323c/COPYING
@@ -0,0 +1,341 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
diff --git a/addons/ooh323c/README b/addons/ooh323c/README
new file mode 100644
index 000000000..e1a5b20e4
--- /dev/null
+++ b/addons/ooh323c/README
@@ -0,0 +1,95 @@
+Copyright (C) 2004-2005 by Objective Systems, Inc.
+
+Objective Open H.323 README
+
+Introduction
+Objective Open H.323 stack is a simple H.323 implementation in C. This
+package contains the core stack code. For complete H.323 stack package with
+examples or for more information visit www.obj-sys.com/open
+ The ASN.1 messaging code was developed using the Objective Systems ASN1C
+compiler to implement the core H.323 specifications (H.225, H.235,
+and H.245). Additional code was then developed which makes use of
+the compiler generated code for presenting a comparatively high level
+stack API.
+
+Features
+
+Features supported in this package include the following:
+
+* H.225/Q.931 - the following message types are supported (including
+ support for FastStart and H.245 tunneling):
+ - Setup
+ - Connect
+ - Call Proceeding
+ - Alerting
+ - Facility
+ - ReleaseComplete
+
+* H.225/RAS - the following message types are supported
+ - GateKeeperRequest / Response
+ - RegistrationRequest / Response
+ - AdmissionRequest / Response
+ - DisengageRequest / Response
+
+* H.245 - the following message types are supported
+ - MasterSlaveDetermination
+ - MasterSlaveDeterminationAck
+ - MasterSlaveDeterminationReject
+ - MasterSlaveDeterminationRelease
+ - TerminalCapabilitySet
+ - TerminalCapabilitySetReject
+ - TerminalCapabilitySetRelease
+ - TerminalCapabilitySetAck
+ - OpenLogicalChannel
+ - OpenLogicalChannelAck
+ - OpenLogicalChannelReject
+ - CloseLogicalChannel
+ - CloseLogicalChannelAck
+ - RequestChannelClose
+ - RequestChannelCloseAck
+ - RequestChannelCloseReject
+ - RequestChannelCloseRelease
+
+
+To run the stack test application chansetup
+ chansetup - This is a sample program developed for testing multiple calls.
+ This program allows stack testing by placing multiple calls. The number of
+ calls, duration of each call and interval between successive calls are
+ configurable.
+
+1. Two instances of this program have to be run. Can be run on same machine or
+ different machines.
+
+2. First change to chansetup directory.
+
+ cd tests/chansetup
+
+2. For running listener instance,
+
+ ./h323peer [--use-ip ip] [--use-port port]
+
+ where local ip address and port can be specified for listening to incoming
+ calls. By default, application determines ip address and uses default H323
+ port number 1720.
+
+3. For running calling instance
+
+ ./h323peer [--use-ip ip] -n <number of calls> -duration <call duration>
+ -interval <inetrval between successive calls> destination
+
+ where all times are in seconds. Interval of 0 means next call will be placed
+ after current call finishes. "destination" is the dotted ip address of the
+ endpoint running listener instance.
+
+ NOTE: More sample programs are available in the original ooh323c package
+ which can be downloaded from www.obj-sys.com/open
+
+Reporting Problems:
+
+Report problems you encounter by sending E-mail to support@obj-sys.com.
+
+If you have any further questions or comments on what you would like to
+see in the product or what is difficult to use or understand, please
+communicate them to us. Your feedback is important to us. Please let us
+know how it works out for you - either good or bad.
+
diff --git a/addons/ooh323c/src/Makefile.in b/addons/ooh323c/src/Makefile.in
new file mode 100644
index 000000000..d3a96024b
--- /dev/null
+++ b/addons/ooh323c/src/Makefile.in
@@ -0,0 +1,564 @@
+# Makefile.in generated by automake 1.6.3 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+AMTAR = @AMTAR@
+AS = @AS@
+AWK = @AWK@
+CC = @CC@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+INSTALLPREFIX = @INSTALLPREFIX@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+OBJDUMP = @OBJDUMP@
+OOH323CDRIVERDIR = @OOH323CDRIVERDIR@
+OOH323CFLAGS = @OOH323CFLAGS@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+STRIP = @STRIP@
+VERSION = @VERSION@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+AUTOMAKE_OPTIONS = gnu
+CFLAGS = @OOH323CFLAGS@
+
+noinst_LIBRARIES = libooh323c.a
+
+libooh323c_a_SOURCES = ooLogChan.h ooLogChan.c ooUtils.c ooUtils.h ooGkClient.h ooGkClient.c context.c ooCommon.h ooDateTime.h ooDateTime.c decode.c dlist.c encode.c errmgmt.c memheap.c memheap.h ooasn1.h ootrace.h ootrace.c oochannels.c oochannels.h ooh245.c ooh245.h oohdr.h ooper.h ooports.c ooports.h ooq931.c ooq931.h ooCapability.c ooCapability.h ooSocket.c ooSocket.h ootypes.h perutil.c eventHandler.c eventHandler.h ooCalls.c ooCalls.h ooStackCmds.c ooStackCmds.h ooh323.c ooh323.h ooh323ep.c ooh323ep.h printHandler.c printHandler.h rtctype.c rtctype.h ooTimer.c ooTimer.h h323/H235-SECURITY-MESSAGESDec.c h323/H235-SECURITY-MESSAGESEnc.c h323/H235-SECURITY-MESSAGES.h h323/H323-MESSAGES.c h323/H323-MESSAGESDec.c h323/H323-MESSAGESEnc.c h323/H323-MESSAGES.h h323/MULTIMEDIA-SYSTEM-CONTROL.c h323/MULTIMEDIA-SYSTEM-CONTROLDec.c h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c h323/MULTIMEDIA-SYSTEM-CONTROL.h
+
+INCLUDES = -Ih323
+
+
+#include_HEADERS = ooUtils.h memheap.h ooCommon.h ooDateTime.h ooGkClient.h ooasn1.h oochannels.h ootrace.h ooh245.h ooports.h ooq931.h oohdr.h ooper.h ooSocket.h ooTimer.h ootypes.h eventHandler.h ooCapability.h ooCalls.h ooStackCmds.h ooh323.h ooh323ep.h printHandler.h rtctype.h h323/H235-SECURITY-MESSAGES.h h323/H323-MESSAGES.h h323/MULTIMEDIA-SYSTEM-CONTROL.h
+EXTRA_DIST = oostk.dsp
+subdir = ooh323c/src
+mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+
+libooh323c_a_AR = $(AR) cru
+libooh323c_a_LIBADD =
+am_libooh323c_a_OBJECTS = ooLogChan.$(OBJEXT) ooUtils.$(OBJEXT) \
+ ooGkClient.$(OBJEXT) context.$(OBJEXT) ooDateTime.$(OBJEXT) \
+ decode.$(OBJEXT) dlist.$(OBJEXT) encode.$(OBJEXT) \
+ errmgmt.$(OBJEXT) memheap.$(OBJEXT) ootrace.$(OBJEXT) \
+ oochannels.$(OBJEXT) ooh245.$(OBJEXT) ooports.$(OBJEXT) \
+ ooq931.$(OBJEXT) ooCapability.$(OBJEXT) ooSocket.$(OBJEXT) \
+ perutil.$(OBJEXT) eventHandler.$(OBJEXT) ooCalls.$(OBJEXT) \
+ ooStackCmds.$(OBJEXT) ooh323.$(OBJEXT) ooh323ep.$(OBJEXT) \
+ printHandler.$(OBJEXT) rtctype.$(OBJEXT) ooTimer.$(OBJEXT) \
+ H235-SECURITY-MESSAGESDec.$(OBJEXT) \
+ H235-SECURITY-MESSAGESEnc.$(OBJEXT) H323-MESSAGES.$(OBJEXT) \
+ H323-MESSAGESDec.$(OBJEXT) H323-MESSAGESEnc.$(OBJEXT) \
+ MULTIMEDIA-SYSTEM-CONTROL.$(OBJEXT) \
+ MULTIMEDIA-SYSTEM-CONTROLDec.$(OBJEXT) \
+ MULTIMEDIA-SYSTEM-CONTROLEnc.$(OBJEXT)
+libooh323c_a_OBJECTS = $(am_libooh323c_a_OBJECTS)
+
+DEFS = @DEFS@
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/H235-SECURITY-MESSAGESDec.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/H235-SECURITY-MESSAGESEnc.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/H323-MESSAGES.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/H323-MESSAGESDec.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/H323-MESSAGESEnc.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/context.Po ./$(DEPDIR)/decode.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/dlist.Po ./$(DEPDIR)/encode.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/errmgmt.Po ./$(DEPDIR)/eventHandler.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/memheap.Po ./$(DEPDIR)/ooCalls.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooCapability.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooDateTime.Po ./$(DEPDIR)/ooGkClient.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooLogChan.Po ./$(DEPDIR)/ooSocket.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooStackCmds.Po ./$(DEPDIR)/ooTimer.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooUtils.Po ./$(DEPDIR)/oochannels.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooh245.Po ./$(DEPDIR)/ooh323.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooh323ep.Po ./$(DEPDIR)/ooports.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ooq931.Po ./$(DEPDIR)/ootrace.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/perutil.Po ./$(DEPDIR)/printHandler.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/rtctype.Po
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \
+ $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+DIST_SOURCES = $(libooh323c_a_SOURCES)
+DIST_COMMON = Makefile.am Makefile.in
+SOURCES = $(libooh323c_a_SOURCES)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu ooh323c/src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+AR = ar
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+H235-SECURITY-MESSAGESDec.$(OBJEXT): h323/H235-SECURITY-MESSAGESDec.c
+H235-SECURITY-MESSAGESEnc.$(OBJEXT): h323/H235-SECURITY-MESSAGESEnc.c
+H323-MESSAGES.$(OBJEXT): h323/H323-MESSAGES.c
+H323-MESSAGESDec.$(OBJEXT): h323/H323-MESSAGESDec.c
+H323-MESSAGESEnc.$(OBJEXT): h323/H323-MESSAGESEnc.c
+MULTIMEDIA-SYSTEM-CONTROL.$(OBJEXT): h323/MULTIMEDIA-SYSTEM-CONTROL.c
+MULTIMEDIA-SYSTEM-CONTROLDec.$(OBJEXT): \
+ h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+MULTIMEDIA-SYSTEM-CONTROLEnc.$(OBJEXT): \
+ h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+libooh323c.a: $(libooh323c_a_OBJECTS) $(libooh323c_a_DEPENDENCIES)
+ -rm -f libooh323c.a
+ $(libooh323c_a_AR) libooh323c.a $(libooh323c_a_OBJECTS) $(libooh323c_a_LIBADD)
+ $(RANLIB) libooh323c.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H235-SECURITY-MESSAGESDec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H235-SECURITY-MESSAGESEnc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H323-MESSAGES.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H323-MESSAGESDec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/H323-MESSAGESEnc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errmgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eventHandler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memheap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooCalls.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooCapability.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooDateTime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooGkClient.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooLogChan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooSocket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooStackCmds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooTimer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooUtils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oochannels.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooh245.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooh323.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooh323ep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooports.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ooq931.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ootrace.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perutil.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printHandler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtctype.Po@am__quote@
+
+distclean-depend:
+ -rm -rf ./$(DEPDIR)
+
+.c.o:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$<
+
+.c.obj:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(COMPILE) -c `cygpath -w $<`
+
+.c.lo:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
+H235-SECURITY-MESSAGESDec.o: h323/H235-SECURITY-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESDec.c' object='H235-SECURITY-MESSAGESDec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.Po' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESDec.o `test -f 'h323/H235-SECURITY-MESSAGESDec.c' || echo '$(srcdir)/'`h323/H235-SECURITY-MESSAGESDec.c
+
+H235-SECURITY-MESSAGESDec.obj: h323/H235-SECURITY-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESDec.c' object='H235-SECURITY-MESSAGESDec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.Po' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESDec.obj `cygpath -w h323/H235-SECURITY-MESSAGESDec.c`
+
+H235-SECURITY-MESSAGESDec.lo: h323/H235-SECURITY-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESDec.c' object='H235-SECURITY-MESSAGESDec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.Plo' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESDec.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESDec.lo `test -f 'h323/H235-SECURITY-MESSAGESDec.c' || echo '$(srcdir)/'`h323/H235-SECURITY-MESSAGESDec.c
+
+H235-SECURITY-MESSAGESEnc.o: h323/H235-SECURITY-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESEnc.c' object='H235-SECURITY-MESSAGESEnc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.Po' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESEnc.o `test -f 'h323/H235-SECURITY-MESSAGESEnc.c' || echo '$(srcdir)/'`h323/H235-SECURITY-MESSAGESEnc.c
+
+H235-SECURITY-MESSAGESEnc.obj: h323/H235-SECURITY-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESEnc.c' object='H235-SECURITY-MESSAGESEnc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.Po' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESEnc.obj `cygpath -w h323/H235-SECURITY-MESSAGESEnc.c`
+
+H235-SECURITY-MESSAGESEnc.lo: h323/H235-SECURITY-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H235-SECURITY-MESSAGESEnc.c' object='H235-SECURITY-MESSAGESEnc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.Plo' tmpdepfile='$(DEPDIR)/H235-SECURITY-MESSAGESEnc.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H235-SECURITY-MESSAGESEnc.lo `test -f 'h323/H235-SECURITY-MESSAGESEnc.c' || echo '$(srcdir)/'`h323/H235-SECURITY-MESSAGESEnc.c
+
+H323-MESSAGES.o: h323/H323-MESSAGES.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGES.c' object='H323-MESSAGES.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGES.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGES.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGES.o `test -f 'h323/H323-MESSAGES.c' || echo '$(srcdir)/'`h323/H323-MESSAGES.c
+
+H323-MESSAGES.obj: h323/H323-MESSAGES.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGES.c' object='H323-MESSAGES.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGES.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGES.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGES.obj `cygpath -w h323/H323-MESSAGES.c`
+
+H323-MESSAGES.lo: h323/H323-MESSAGES.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGES.c' object='H323-MESSAGES.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGES.Plo' tmpdepfile='$(DEPDIR)/H323-MESSAGES.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGES.lo `test -f 'h323/H323-MESSAGES.c' || echo '$(srcdir)/'`h323/H323-MESSAGES.c
+
+H323-MESSAGESDec.o: h323/H323-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESDec.c' object='H323-MESSAGESDec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESDec.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGESDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESDec.o `test -f 'h323/H323-MESSAGESDec.c' || echo '$(srcdir)/'`h323/H323-MESSAGESDec.c
+
+H323-MESSAGESDec.obj: h323/H323-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESDec.c' object='H323-MESSAGESDec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESDec.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGESDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESDec.obj `cygpath -w h323/H323-MESSAGESDec.c`
+
+H323-MESSAGESDec.lo: h323/H323-MESSAGESDec.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESDec.c' object='H323-MESSAGESDec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESDec.Plo' tmpdepfile='$(DEPDIR)/H323-MESSAGESDec.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESDec.lo `test -f 'h323/H323-MESSAGESDec.c' || echo '$(srcdir)/'`h323/H323-MESSAGESDec.c
+
+H323-MESSAGESEnc.o: h323/H323-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESEnc.c' object='H323-MESSAGESEnc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESEnc.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGESEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESEnc.o `test -f 'h323/H323-MESSAGESEnc.c' || echo '$(srcdir)/'`h323/H323-MESSAGESEnc.c
+
+H323-MESSAGESEnc.obj: h323/H323-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESEnc.c' object='H323-MESSAGESEnc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESEnc.Po' tmpdepfile='$(DEPDIR)/H323-MESSAGESEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESEnc.obj `cygpath -w h323/H323-MESSAGESEnc.c`
+
+H323-MESSAGESEnc.lo: h323/H323-MESSAGESEnc.c
+@AMDEP_TRUE@ source='h323/H323-MESSAGESEnc.c' object='H323-MESSAGESEnc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/H323-MESSAGESEnc.Plo' tmpdepfile='$(DEPDIR)/H323-MESSAGESEnc.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o H323-MESSAGESEnc.lo `test -f 'h323/H323-MESSAGESEnc.c' || echo '$(srcdir)/'`h323/H323-MESSAGESEnc.c
+
+MULTIMEDIA-SYSTEM-CONTROL.o: h323/MULTIMEDIA-SYSTEM-CONTROL.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROL.c' object='MULTIMEDIA-SYSTEM-CONTROL.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROL.o `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROL.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROL.c
+
+MULTIMEDIA-SYSTEM-CONTROL.obj: h323/MULTIMEDIA-SYSTEM-CONTROL.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROL.c' object='MULTIMEDIA-SYSTEM-CONTROL.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROL.obj `cygpath -w h323/MULTIMEDIA-SYSTEM-CONTROL.c`
+
+MULTIMEDIA-SYSTEM-CONTROL.lo: h323/MULTIMEDIA-SYSTEM-CONTROL.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROL.c' object='MULTIMEDIA-SYSTEM-CONTROL.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.Plo' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROL.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROL.lo `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROL.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROL.c
+
+MULTIMEDIA-SYSTEM-CONTROLDec.o: h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLDec.c' object='MULTIMEDIA-SYSTEM-CONTROLDec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLDec.o `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROLDec.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+
+MULTIMEDIA-SYSTEM-CONTROLDec.obj: h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLDec.c' object='MULTIMEDIA-SYSTEM-CONTROLDec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLDec.obj `cygpath -w h323/MULTIMEDIA-SYSTEM-CONTROLDec.c`
+
+MULTIMEDIA-SYSTEM-CONTROLDec.lo: h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLDec.c' object='MULTIMEDIA-SYSTEM-CONTROLDec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.Plo' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLDec.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLDec.lo `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROLDec.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROLDec.c
+
+MULTIMEDIA-SYSTEM-CONTROLEnc.o: h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c' object='MULTIMEDIA-SYSTEM-CONTROLEnc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLEnc.o `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+
+MULTIMEDIA-SYSTEM-CONTROLEnc.obj: h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c' object='MULTIMEDIA-SYSTEM-CONTROLEnc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.Po' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLEnc.obj `cygpath -w h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c`
+
+MULTIMEDIA-SYSTEM-CONTROLEnc.lo: h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+@AMDEP_TRUE@ source='h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c' object='MULTIMEDIA-SYSTEM-CONTROLEnc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.Plo' tmpdepfile='$(DEPDIR)/MULTIMEDIA-SYSTEM-CONTROLEnc.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o MULTIMEDIA-SYSTEM-CONTROLEnc.lo `test -f 'h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c' || echo '$(srcdir)/'`h323/MULTIMEDIA-SYSTEM-CONTROLEnc.c
+CCDEPMODE = @CCDEPMODE@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ETAGS = etags
+ETAGSFLAGS =
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$tags$$unique" \
+ || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ../..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ @list='$(DISTFILES)'; for file in $$list; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+
+installdirs:
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-compile distclean-depend \
+ distclean-generic distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+uninstall-am: uninstall-info-am
+
+.PHONY: GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES distclean distclean-compile \
+ distclean-depend distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am info info-am install \
+ install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ tags uninstall uninstall-am uninstall-info-am
+
+
+opt:
+ $(MAKE) "CFLAGS = -DGNU -D_GNU_SOURCE -D_REENTRANT -O2 -D_COMPACT" all
+
+debug:
+ $(MAKE) "CFLAGS = -g -DGNU -D_GNU_SOURCE -D_REENTRANT" all
+
+profile:
+ $(MAKE) "CFLAGS = -g -pg -DGNU -D_GNU_SOURCE -D_REENTRANT" all
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/addons/ooh323c/src/context.c b/addons/ooh323c/src/context.c
new file mode 100644
index 000000000..d243f88a0
--- /dev/null
+++ b/addons/ooh323c/src/context.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+#include "ooasn1.h"
+#include <stdlib.h>
+
+int initContext (OOCTXT* pctxt)
+{
+ memset (pctxt, 0, sizeof(OOCTXT));
+
+ memHeapCreate (&pctxt->pTypeMemHeap);
+ pctxt->pMsgMemHeap = pctxt->pTypeMemHeap;
+ memHeapAddRef (&pctxt->pMsgMemHeap);
+
+ return ASN_OK;
+}
+
+int initContextBuffer
+(OOCTXT* pctxt, const ASN1OCTET* bufaddr, ASN1UINT bufsiz)
+{
+ if (bufaddr == 0) {
+ /* dynamic buffer */
+ if (bufsiz == 0) bufsiz = ASN_K_ENCBUFSIZ;
+ pctxt->buffer.data = (ASN1OCTET*)
+ memHeapAlloc (&pctxt->pMsgMemHeap, bufsiz);
+ if (!pctxt->buffer.data) return ASN_E_NOMEM;
+ pctxt->buffer.size = bufsiz;
+ pctxt->buffer.dynamic = TRUE;
+ }
+ else {
+ /* static buffer */
+ pctxt->buffer.data = (ASN1OCTET*) bufaddr;
+ pctxt->buffer.size = bufsiz;
+ pctxt->buffer.dynamic = FALSE;
+ }
+
+ pctxt->buffer.byteIndex = 0;
+ pctxt->buffer.bitOffset = 8;
+
+ return ASN_OK;
+}
+
+int initSubContext (OOCTXT* pctxt, OOCTXT* psrc)
+{
+ int stat = ASN_OK;
+ memset (pctxt, 0, sizeof(OOCTXT));
+ pctxt->pTypeMemHeap = psrc->pTypeMemHeap;
+ memHeapAddRef (&pctxt->pTypeMemHeap);
+ pctxt->pMsgMemHeap = psrc->pMsgMemHeap;
+ memHeapAddRef (&pctxt->pMsgMemHeap);
+ pctxt->flags = psrc->flags;
+ pctxt->buffer.dynamic = TRUE;
+ pctxt->buffer.byteIndex = 0;
+ pctxt->buffer.bitOffset = 8;
+ return stat;
+}
+
+void freeContext (OOCTXT* pctxt)
+{
+ ASN1BOOL saveBuf = (pctxt->flags & ASN1SAVEBUF) != 0;
+
+ if (pctxt->buffer.dynamic && pctxt->buffer.data) {
+ if (saveBuf) {
+ memHeapMarkSaved (&pctxt->pMsgMemHeap, pctxt->buffer.data, TRUE);
+ }
+ else {
+ memHeapFreePtr (&pctxt->pMsgMemHeap, pctxt->buffer.data);
+ }
+ }
+
+ errFreeParms (&pctxt->errInfo);
+
+ memHeapRelease (&pctxt->pTypeMemHeap);
+ memHeapRelease (&pctxt->pMsgMemHeap);
+}
+
+void copyContext (OOCTXT* pdest, OOCTXT* psrc)
+{
+ memcpy (&pdest->buffer, &psrc->buffer, sizeof(ASN1BUFFER));
+ pdest->flags = psrc->flags;
+}
+
+void setCtxtFlag (OOCTXT* pctxt, ASN1USINT mask)
+{
+ pctxt->flags |= mask;
+}
+
+void clearCtxtFlag (OOCTXT* pctxt, ASN1USINT mask)
+{
+ pctxt->flags &= ~mask;
+}
+
+int setPERBufferUsingCtxt (OOCTXT* pTarget, OOCTXT* pSource)
+{
+ int stat = initContextBuffer
+ (pTarget, pSource->buffer.data, pSource->buffer.size);
+
+ if (ASN_OK == stat) {
+ pTarget->buffer.byteIndex = pSource->buffer.byteIndex;
+ pTarget->buffer.bitOffset = pSource->buffer.bitOffset;
+ }
+
+ return stat;
+}
+
+int setPERBuffer (OOCTXT* pctxt,
+ ASN1OCTET* bufaddr, ASN1UINT bufsiz, ASN1BOOL aligned)
+{
+ int stat = initContextBuffer (pctxt, bufaddr, bufsiz);
+ if(stat != ASN_OK) return stat;
+
+
+ return ASN_OK;
+}
+
+OOCTXT* newContext ()
+{
+ OOCTXT* pctxt = (OOCTXT*) ASN1CRTMALLOC0 (sizeof(OOCTXT));
+ if (pctxt) {
+ if (initContext(pctxt) != ASN_OK) {
+ ASN1CRTFREE0 (pctxt);
+ pctxt = 0;
+ }
+ pctxt->flags |= ASN1DYNCTXT;
+ }
+ return (pctxt);
+}
diff --git a/addons/ooh323c/src/decode.c b/addons/ooh323c/src/decode.c
new file mode 100644
index 000000000..d09247c94
--- /dev/null
+++ b/addons/ooh323c/src/decode.c
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+#include "ooasn1.h"
+
+static int decode16BitConstrainedString
+(OOCTXT* pctxt, Asn116BitCharString* pString, Asn116BitCharSet* pCharSet);
+
+static int decodeOctets
+(OOCTXT* pctxt, ASN1OCTET* pbuffer, ASN1UINT bufsiz, ASN1UINT nbits);
+
+static int getComponentLength (OOCTXT* pctxt, ASN1UINT itemBits);
+
+int decodeBits (OOCTXT* pctxt, ASN1UINT* pvalue, ASN1UINT nbits)
+{
+ unsigned char mask;
+
+ if (nbits == 0) {
+ *pvalue = 0;
+ return ASN_OK;
+ }
+
+ /* If the number of bits is less than the current bit offset, mask */
+ /* off the required number of bits and return.. */
+
+ if (nbits < (unsigned)pctxt->buffer.bitOffset) {
+ /* Check if buffer contains number of bits requested */
+
+ if (pctxt->buffer.byteIndex >= pctxt->buffer.size)
+ return LOG_ASN1ERR (pctxt, ASN_E_ENDOFBUF);
+
+ pctxt->buffer.bitOffset -= nbits;
+
+ *pvalue = ((pctxt->buffer.data[pctxt->buffer.byteIndex]) >>
+ pctxt->buffer.bitOffset) & ((1 << nbits) - 1);
+
+ return ASN_OK;
+ }
+
+ /* Otherwise, we first need to mask off the remaining bits in the */
+ /* current byte, followed by a loop to extract bits from full bytes, */
+ /* followed by logic to mask of remaining bits from the start of */
+ /* of the last byte.. */
+
+ else {
+ /* Check if buffer contains number of bits requested */
+
+ int nbytes = (((nbits - pctxt->buffer.bitOffset) + 7) / 8);
+
+ if ((pctxt->buffer.byteIndex + nbytes) >= pctxt->buffer.size) {
+ return LOG_ASN1ERR (pctxt, ASN_E_ENDOFBUF);
+ }
+
+ /* first read current byte remaining bits */
+ mask = ((1 << pctxt->buffer.bitOffset) - 1);
+
+ *pvalue = (pctxt->buffer.data[pctxt->buffer.byteIndex]) & mask;
+
+ nbits -= pctxt->buffer.bitOffset;
+ pctxt->buffer.bitOffset = 8;
+ pctxt->buffer.byteIndex++;
+
+ /* second read bytes from next byteIndex */
+ while (nbits >= 8) {
+ *pvalue = (*pvalue << 8) |
+ (pctxt->buffer.data[pctxt->buffer.byteIndex]);
+ pctxt->buffer.byteIndex++;
+ nbits -= 8;
+ }
+
+ /* third read bits & set bitoffset of the byteIndex */
+ if (nbits > 0) {
+ pctxt->buffer.bitOffset = 8 - nbits;
+ *pvalue = (*pvalue << nbits) |
+ ((pctxt->buffer.data[pctxt->buffer.byteIndex]) >>
+ pctxt->buffer.bitOffset);
+ }
+
+ return ASN_OK;
+ }
+}
+
+int decodeBitString
+(OOCTXT* pctxt, ASN1UINT* numbits_p, ASN1OCTET* buffer, ASN1UINT bufsiz)
+{
+ ASN1UINT bitcnt;
+ int lstat, octidx = 0, stat;
+ Asn1SizeCnst* pSizeList = pctxt->pSizeConstraint;
+ ASN1BOOL doAlign;
+
+ for (*numbits_p = 0;;) {
+ lstat = decodeLength (pctxt, &bitcnt);
+ if (lstat < 0) return LOG_ASN1ERR (pctxt, lstat);
+
+ if (bitcnt > 0) {
+ *numbits_p += bitcnt;
+
+ stat = bitAndOctetStringAlignmentTest
+ (pSizeList, bitcnt, TRUE, &doAlign);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (doAlign) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ stat = decodeOctets (pctxt, &buffer[octidx], bufsiz - octidx, bitcnt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ if (lstat == ASN_OK_FRAG) {
+ octidx += (bitcnt / 8);
+ }
+ else break;
+ }
+
+ return ASN_OK;
+}
+
+int decodeBMPString
+(OOCTXT* pctxt, ASN1BMPString* pvalue, Asn116BitCharSet* permCharSet)
+{
+ Asn116BitCharSet charSet;
+ int stat;
+
+ /* Set character set */
+
+ init16BitCharSet (&charSet, BMP_FIRST, BMP_LAST, BMP_ABITS, BMP_UBITS);
+
+ if (permCharSet) {
+ set16BitCharSet (pctxt, &charSet, permCharSet);
+ }
+
+ /* Decode constrained string */
+
+ stat = decode16BitConstrainedString (pctxt, pvalue, &charSet);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return (stat);
+}
+
+int decodeByteAlign (OOCTXT* pctxt)
+{
+ if (pctxt->buffer.bitOffset != 8) {
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.bitOffset = 8;
+ }
+ return ASN_OK;
+}
+
+int decodeConstrainedStringEx
+(OOCTXT* pctxt, const char** string, const char* charSet,
+ ASN1UINT abits, ASN1UINT ubits, ASN1UINT canSetBits)
+{
+ int stat;
+ char* tmpstr;
+
+ ASN1UINT i, idx, len, nbits = abits;
+
+ /* note: need to save size constraint for use in alignCharStr */
+ /* because it will be cleared in decodeLength from the context.. */
+ Asn1SizeCnst* psize = pctxt->pSizeConstraint;
+
+ /* Decode length */
+
+ stat = decodeLength (pctxt, &len);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte-align */
+
+ if (alignCharStr (pctxt, len, nbits, psize)) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Decode data */
+
+ tmpstr = (char*) ASN1MALLOC (pctxt, len+1);
+ if (0 != tmpstr) {
+ if (nbits >= canSetBits && canSetBits > 4) {
+ for (i = 0; i < len; i++) {
+ if ((stat = decodeBits (pctxt, &idx, nbits)) == ASN_OK) {
+ tmpstr[i] = (char) idx;
+ }
+ else break;
+ }
+ }
+ else if (0 != charSet) {
+ ASN1UINT nchars = strlen (charSet);
+ for (i = 0; i < len; i++) {
+ if ((stat = decodeBits (pctxt, &idx, nbits)) == ASN_OK) {
+ if (idx < nchars) {
+ tmpstr[i] = charSet[idx];
+ }
+ else return LOG_ASN1ERR (pctxt, ASN_E_CONSVIO);
+ }
+ else break;
+ }
+ }
+ else stat = ASN_E_INVPARAM;
+
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ tmpstr[i] = '\0'; /* add null-terminator */
+ }
+ else
+ return LOG_ASN1ERR (pctxt, ASN_E_NOMEM);
+
+ *string = tmpstr;
+
+ return ASN_OK;
+}
+
+int decodeConsInteger
+(OOCTXT* pctxt, ASN1INT* pvalue, ASN1INT lower, ASN1INT upper)
+{
+ ASN1UINT range_value = upper - lower;
+ ASN1UINT adjusted_value;
+ int stat = ASN_OK;
+
+ if (range_value != ASN1UINT_MAX) { range_value += 1; }
+
+ if (lower > upper)
+ return ASN_E_RANGERR;
+ else if (lower != upper) {
+ stat = decodeConsWholeNumber (pctxt, &adjusted_value, range_value);
+ if (stat == ASN_OK) {
+ *pvalue = adjusted_value + lower;
+
+ if (*pvalue < lower || *pvalue > upper)
+ stat = ASN_E_CONSVIO;
+ }
+ }
+ else {
+ *pvalue = lower;
+ }
+
+ return stat;
+}
+
+int decodeConsUInt8
+(OOCTXT* pctxt, ASN1UINT8* pvalue, ASN1UINT lower, ASN1UINT upper)
+{
+ ASN1UINT range_value, value;
+ ASN1UINT adjusted_value;
+ int stat = ASN_OK;
+
+ /* Check for special case: if lower is 0 and upper is ASN1UINT_MAX, */
+ /* set range to ASN1UINT_MAX; otherwise to upper - lower + 1 */
+
+ range_value = (lower == 0 && upper == ASN1UINT_MAX) ?
+ ASN1UINT_MAX : upper - lower + 1;
+
+ if (lower != upper) {
+ ASN1UINT range_bitcnt;
+
+ /* If range is <= 255, bit-field case (10.5.7a) */
+
+ if (range_value <= 255) {
+ range_bitcnt = getUIntBitCount (range_value - 1);
+ }
+
+ /* If range is exactly 256, one-octet case (10.5.7b) */
+
+ else if (range_value == 256) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ range_bitcnt = 8;
+ }
+ stat = decodeBits (pctxt, &adjusted_value, range_bitcnt);
+ if (stat == ASN_OK) {
+ value = adjusted_value + lower;
+
+ if (value < lower || value > upper)
+ stat = ASN_E_CONSVIO;
+
+ *pvalue = (ASN1OCTET)value;
+ }
+ }
+ else *pvalue = (ASN1OCTET)lower;
+
+ return stat;
+}
+
+int decodeConsUInt16
+(OOCTXT* pctxt, ASN1USINT* pvalue, ASN1UINT lower, ASN1UINT upper)
+{
+ ASN1UINT range_value, value;
+ ASN1UINT adjusted_value;
+ int stat = ASN_OK;
+
+ /* Check for special case: if lower is 0 and upper is ASN1UINT_MAX, */
+ /* set range to ASN1UINT_MAX; otherwise to upper - lower + 1 */
+
+ range_value = (lower == 0 && upper == ASN1UINT_MAX) ?
+ ASN1UINT_MAX : upper - lower + 1;
+
+ if (lower != upper) {
+ stat = decodeConsWholeNumber (pctxt, &adjusted_value, range_value);
+ if (stat == ASN_OK) {
+ value = adjusted_value + lower;
+
+ /* Verify value is within given range (ED, 1/15/2002) */
+ if (value < lower || value > upper)
+ stat = ASN_E_CONSVIO;
+ *pvalue = (ASN1USINT) value;
+ }
+ }
+ else *pvalue = (ASN1USINT) lower;
+
+ return stat;
+}
+
+int decodeConsUnsigned
+(OOCTXT* pctxt, ASN1UINT* pvalue, ASN1UINT lower, ASN1UINT upper)
+{
+ ASN1UINT range_value;
+ ASN1UINT adjusted_value;
+ int stat = ASN_OK;
+
+ /* Check for special case: if lower is 0 and upper is ASN1UINT_MAX, */
+ /* set range to ASN1UINT_MAX; otherwise to upper - lower + 1 */
+
+ range_value = (lower == 0 && upper == ASN1UINT_MAX) ?
+ ASN1UINT_MAX : upper - lower + 1;
+
+ if (lower != upper) {
+ stat = decodeConsWholeNumber (pctxt, &adjusted_value, range_value);
+ if (stat == ASN_OK) {
+ *pvalue = adjusted_value + lower;
+ if (*pvalue < lower || *pvalue > upper)
+ stat = ASN_E_CONSVIO;
+ }
+ }
+ else *pvalue = lower;
+
+ return stat;
+}
+
+int decodeConsWholeNumber
+(OOCTXT* pctxt, ASN1UINT* padjusted_value, ASN1UINT range_value)
+{
+ ASN1UINT nocts, range_bitcnt;
+ int stat;
+
+ /* If unaligned, decode non-negative binary integer in the minimum */
+ /* number of bits necessary to represent the range (10.5.6) */
+
+ if (!TRUE) {
+ range_bitcnt = getUIntBitCount (range_value - 1);
+ }
+
+ /* If aligned, encoding depended on range value (10.5.7) */
+
+ else { /* aligned */
+
+ /* If range is <= 255, bit-field case (10.5.7a) */
+
+ if (range_value <= 255) {
+ range_bitcnt = getUIntBitCount (range_value - 1);
+ }
+
+ /* If range is exactly 256, one-octet case (10.5.7b) */
+
+ else if (range_value == 256) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ range_bitcnt = 8;
+ }
+
+ /* If range > 256 and <= 64k (65535), two-octet case (10.5.7c) */
+
+ else if (range_value <= 65536) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ range_bitcnt = 16;
+ }
+
+ /* If range > 64k, indefinite-length case (10.5.7d) */
+
+ else {
+ stat = decodeBits (pctxt, &nocts, 2);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ range_bitcnt = (nocts + 1) * 8;
+ }
+ }
+
+ return decodeBits (pctxt, padjusted_value, range_bitcnt);
+}
+
+int decodeDynBitString (OOCTXT* pctxt, ASN1DynBitStr* pBitStr)
+{
+ ASN1UINT nocts;
+ ASN1OCTET* ptmp;
+ int nbits, stat = ASN_OK;
+
+ /* If "fast copy" option is not set (ASN1FATSCOPY) or if constructed,
+ * copy the bit string value into a dynamic memory buffer;
+ * otherwise, store the pointer to the value in the decode
+ * buffer in the data pointer argument. */
+
+ if (pctxt->flags & ASN1FASTCOPY) {
+ /* check is it possible to do optimized decoding */
+
+ ASN1OCTET bit;
+ ASN1UINT byteIndex = pctxt->buffer.byteIndex; /* save byte index */
+ ASN1USINT bitOffset = pctxt->buffer.bitOffset; /* save bit offset */
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = DECODEBIT (pctxt, &bit); /* read first bit of length determinant */
+ if (bit == 1 && stat == ASN_OK)
+ stat = DECODEBIT (pctxt, &bit); /* read second bit */
+
+ pctxt->buffer.byteIndex = byteIndex; /* restore byte index */
+ pctxt->buffer.bitOffset = bitOffset; /* restore bit offset */
+
+ /* if either first or second bit != 0 - not fragmented */
+
+ if (bit == 0 && stat == ASN_OK) {
+ ASN1UINT bitcnt;
+
+ stat = decodeLength (pctxt, &bitcnt);
+ if (stat != 0) return LOG_ASN1ERR (pctxt, stat);
+
+ pBitStr->numbits = bitcnt;
+ if (bitcnt > 0) {
+ pBitStr->data = ASN1BUFPTR (pctxt);
+
+ stat = moveBitCursor (pctxt, bitcnt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else
+ pBitStr->data = 0;
+
+ return stat;
+ }
+ }
+
+ nbits = getComponentLength (pctxt, 1);
+
+ if (nbits < 0) return LOG_ASN1ERR (pctxt, nbits);
+ else if (nbits == 0) {
+ pBitStr->numbits = 0;
+ ptmp = 0;
+ }
+
+ nocts = (nbits + 7) / 8;
+
+ /* Allocate memory for the target string */
+
+ if (nocts > 0) {
+ ptmp = (ASN1OCTET*) ASN1MALLOC (pctxt, nocts);
+ if (0 == ptmp) return LOG_ASN1ERR (pctxt, ASN_E_NOMEM);
+
+ /* Call static bit string decode function */
+
+ stat = decodeBitString (pctxt, &pBitStr->numbits, ptmp, nocts);
+ }
+ pBitStr->data = ptmp;
+
+ return stat;
+}
+
+int decodeDynOctetString (OOCTXT* pctxt, ASN1DynOctStr* pOctStr)
+{
+ ASN1OCTET* ptmp;
+ int nocts, stat;
+
+ /* If "fast copy" option is not set (ASN1FASTCOPY) or if constructed,
+ * copy the octet string value into a dynamic memory buffer;
+ * otherwise, store the pointer to the value in the decode
+ * buffer in the data pointer argument. */
+
+ if (pctxt->flags & ASN1FASTCOPY) {
+ /* check if it is possible to do optimized decoding */
+
+ ASN1OCTET bit;
+ ASN1UINT byteIndex = pctxt->buffer.byteIndex; /* save byte index */
+ ASN1USINT bitOffset = pctxt->buffer.bitOffset; /* save bit offset */
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = DECODEBIT (pctxt, &bit); /* read first bit of length determinant */
+ if (bit == 1 && stat == ASN_OK)
+ stat = DECODEBIT (pctxt, &bit); /* read second bit */
+
+ pctxt->buffer.byteIndex = byteIndex; /* restore byte index */
+ pctxt->buffer.bitOffset = bitOffset; /* restore bit offset */
+
+ /* if either first or second bit != 0 - not fragmented */
+
+ if (bit == 0 && stat == ASN_OK) {
+ ASN1UINT octcnt;
+
+ stat = decodeLength (pctxt, &octcnt);
+ if (stat != 0) return LOG_ASN1ERR (pctxt, stat);
+
+ pOctStr->numocts = octcnt;
+ if (octcnt > 0) {
+ pOctStr->data = ASN1BUFPTR (pctxt);
+
+ stat = moveBitCursor (pctxt, octcnt * 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else
+ pOctStr->data = 0;
+
+ return stat;
+ }
+ }
+
+ nocts = getComponentLength (pctxt, 8);
+
+ if (nocts < 0) return LOG_ASN1ERR (pctxt, nocts);
+ else if (nocts == 0) {
+ pOctStr->numocts = 0;
+ ptmp = 0;
+ }
+
+ /* Allocate memory for the target string */
+
+ else {
+ ptmp = (ASN1OCTET*) ASN1MALLOC (pctxt, nocts);
+ if (0 == ptmp) return LOG_ASN1ERR (pctxt, ASN_E_NOMEM);
+ }
+
+ /* Call static octet string decode function */
+
+ stat = decodeOctetString (pctxt, &pOctStr->numocts, ptmp, nocts);
+
+ pOctStr->data = ptmp;
+
+ return stat;
+}
+
+int decodeLength (OOCTXT* pctxt, ASN1UINT* pvalue)
+{
+ Asn1SizeCnst* pSize;
+ ASN1UINT lower, upper;
+ ASN1BOOL bitValue, extbit;
+ int stat;
+
+ /* If size constraint is present and extendable, decode extension */
+ /* bit.. */
+
+ if (isExtendableSize(pctxt->pSizeConstraint)) {
+ stat = DECODEBIT (pctxt, &extbit);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else extbit = 0;
+
+ /* Now use the value of the extension bit to select the proper */
+ /* size constraint range specification.. */
+
+ pSize = getSizeConstraint (pctxt, extbit);
+
+ lower = (pSize) ? pSize->lower : 0;
+ upper = (pSize) ? pSize->upper : ASN1UINT_MAX;
+
+ /* Reset the size constraint in the context block structure */
+
+ pctxt->pSizeConstraint = 0;
+
+ /* If upper limit is less than 64k, constrained case */
+
+ if (upper < 65536) {
+ if (lower == upper) {
+ *pvalue = 0;
+ stat = ASN_OK;
+ }
+ else
+ stat = decodeConsWholeNumber (pctxt, pvalue, (upper - lower + 1));
+
+ if (stat == ASN_OK) *pvalue += lower;
+ }
+ else {
+ /* unconstrained case OR constrained with upper bound >= 64K*/
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = DECODEBIT (pctxt, &bitValue);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (bitValue == 0) {
+ stat = decodeBits (pctxt, pvalue, 7); /* 10.9.3.6 */
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else {
+ stat = DECODEBIT (pctxt, &bitValue);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (bitValue == 0) {
+ stat = decodeBits (pctxt, pvalue, 14); /* 10.9.3.7 */
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else {
+ ASN1UINT multiplier;
+
+ stat = decodeBits (pctxt, &multiplier, 6);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ *pvalue = 16384 * multiplier;
+
+ stat = ASN_OK_FRAG;
+ }
+ }
+ }
+
+ return stat;
+}
+
+int decodeObjectIdentifier (OOCTXT* pctxt, ASN1OBJID* pvalue)
+{
+ ASN1UINT len;
+ int stat, j;
+ unsigned subid;
+ ASN1UINT b;
+
+ /* Decode unconstrained length */
+
+ if ((stat = decodeLength (pctxt, &len)) < 0) {
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Copy contents to a byte-aligned local buffer */
+
+ j = 0;
+ while (len > 0 && stat == ASN_OK) {
+ if (j < ASN_K_MAXSUBIDS) {
+
+ /* Parse a subidentifier out of the contents field */
+
+ pvalue->subid[j] = 0;
+ do {
+ if ((stat = decodeBits (pctxt, &b, 8)) == ASN_OK) {
+ pvalue->subid[j] = (pvalue->subid[j] * 128) + (b & 0x7F);
+ len--;
+ }
+ } while (b & 0x80 && stat == ASN_OK);
+
+ /* Handle the first subidentifier special case: the first two */
+ /* sub-id's are encoded into one using the formula (x * 40) + y */
+
+ if (j == 0) {
+ subid = pvalue->subid[0];
+ pvalue->subid[0] = ((subid / 40) >= 2) ? 2 : subid / 40;
+ pvalue->subid[1] = (pvalue->subid[0] == 2) ?
+ subid - 80 : subid % 40;
+ j = 2;
+ }
+ else j++;
+ }
+ else
+ stat = ASN_E_INVOBJID;
+ }
+
+ pvalue->numids = j;
+ if (stat == ASN_OK && len != 0) stat = ASN_E_INVLEN;
+
+ return (stat);
+}
+
+static int decodeOctets
+(OOCTXT* pctxt, ASN1OCTET* pbuffer, ASN1UINT bufsiz, ASN1UINT nbits)
+{
+ ASN1UINT nbytes = (nbits + 7) / 8 ;
+ ASN1UINT i = 0, j;
+ ASN1UINT rshift = pctxt->buffer.bitOffset;
+ ASN1UINT lshift = 8 - rshift;
+ ASN1UINT nbitsInLastOctet;
+ ASN1OCTET mask;
+ int stat;
+
+ /* Check to make sure buffer contains number of bits requested */
+
+ if ((pctxt->buffer.byteIndex + nbytes) > pctxt->buffer.size) {
+ return LOG_ASN1ERR (pctxt, ASN_E_ENDOFBUF);
+ }
+
+ /* Check to make sure buffer is big enough to hold requested */
+ /* number of bits.. */
+
+ if (nbytes > bufsiz) {
+ return LOG_ASN1ERR (pctxt, ASN_E_STROVFLW);
+ }
+
+ /* If on a byte boundary, can do a direct memcpy to target buffer */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ memcpy (pbuffer, &pctxt->buffer.data[pctxt->buffer.byteIndex], nbytes);
+ stat = moveBitCursor (pctxt, nbits);
+ if (stat != ASN_OK) return stat;
+ i = nbytes - 1; nbits %= 8;
+ }
+ else {
+ while (nbits >= 8) {
+
+ /* Transfer lower bits from stream octet to upper bits of */
+ /* target octet.. */
+
+ pbuffer[i] = pctxt->buffer.data[pctxt->buffer.byteIndex++]
+ << lshift;
+
+ /* Transfer upper bits from next stream octet to lower bits */
+ /* target octet.. */
+
+ pbuffer[i++] |= pctxt->buffer.data[pctxt->buffer.byteIndex]
+ >> rshift;
+
+ nbits -= 8;
+ }
+
+ /* Copy last partial byte */
+
+ if (nbits >= rshift) {
+ pbuffer[i] =
+ pctxt->buffer.data[pctxt->buffer.byteIndex++] << lshift;
+
+ nbitsInLastOctet = nbits - rshift;
+
+ if (nbitsInLastOctet > 0) {
+ pbuffer[i] |=
+ pctxt->buffer.data[pctxt->buffer.byteIndex] >> rshift;
+ }
+
+ pctxt->buffer.bitOffset = 8 - nbitsInLastOctet;
+ }
+ else if (nbits > 0) { /* nbits < rshift */
+ pbuffer[i] =
+ pctxt->buffer.data[pctxt->buffer.byteIndex] << lshift;
+ pctxt->buffer.bitOffset = rshift - nbits;
+ }
+ }
+
+ /* Mask unused bits off of last byte */
+
+ if (nbits > 0) {
+ mask = 0;
+ for (j = 0; j < nbits; j++) {
+ mask >>= 1;
+ mask |= 0x80;
+ }
+ pbuffer[i] &= mask;
+ }
+
+ return ASN_OK;
+}
+
+int decodeOctetString
+(OOCTXT* pctxt, ASN1UINT* numocts_p, ASN1OCTET* buffer, ASN1UINT bufsiz)
+{
+ ASN1UINT octcnt;
+ int lstat, octidx = 0, stat;
+ Asn1SizeCnst* pSizeList = pctxt->pSizeConstraint;
+
+ for (*numocts_p = 0;;) {
+ lstat = decodeLength (pctxt, &octcnt);
+ if (lstat < 0) return LOG_ASN1ERR (pctxt, lstat);
+
+ if (octcnt > 0) {
+ *numocts_p += octcnt;
+
+ if (TRUE) {
+ ASN1BOOL doAlign;
+
+ stat = bitAndOctetStringAlignmentTest
+ (pSizeList, octcnt, FALSE, &doAlign);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (doAlign) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+
+ stat = decodeOctets (pctxt, &buffer[octidx],
+ bufsiz - octidx, (octcnt * 8));
+
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ if (lstat == ASN_OK_FRAG) {
+ octidx += octcnt;
+ }
+ else break;
+ }
+
+ return ASN_OK;
+}
+
+int decodeOpenType
+(OOCTXT* pctxt, const ASN1OCTET** object_p2, ASN1UINT* numocts_p)
+{
+ ASN1DynOctStr octStr;
+ int stat;
+
+ stat = decodeDynOctetString (pctxt, &octStr);
+ if (stat == ASN_OK) {
+ *numocts_p = octStr.numocts;
+ *object_p2 = octStr.data;
+ }
+
+ return stat;
+}
+
+int decodeSemiConsInteger (OOCTXT* pctxt, ASN1INT* pvalue, ASN1INT lower)
+{
+ signed char b;
+ unsigned char ub;
+ ASN1UINT nbytes;
+ int stat;
+
+ stat = decodeLength (pctxt, &nbytes);
+ if (stat < 0) return LOG_ASN1ERR (pctxt, stat);
+
+ if (nbytes > 0) {
+
+ /* Align buffer */
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Decode first byte into a signed byte value and assign to integer. */
+ /* This should handle sign extension.. */
+
+ stat = decodeOctets (pctxt, (ASN1OCTET*)&b, 1, 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ *pvalue = b;
+ nbytes--;
+
+ /* Decode remaining bytes and add to result */
+
+ while (nbytes > 0) {
+ stat = decodeOctets (pctxt, (ASN1OCTET*)&ub, 1, 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ *pvalue = (*pvalue * 256) + ub;
+ nbytes--;
+ }
+ }
+ else { /* nbytes == 0 */
+ *pvalue = 0;
+ }
+ if (lower > ASN1INT_MIN)
+ *pvalue += lower;
+
+ return ASN_OK;
+}
+
+int decodeSemiConsUnsigned (OOCTXT* pctxt, ASN1UINT* pvalue, ASN1UINT lower)
+{
+ ASN1UINT nbytes;
+ int stat;
+
+ stat = decodeLength (pctxt, &nbytes);
+ if (stat < 0) return LOG_ASN1ERR (pctxt, stat);
+
+
+ if (nbytes > 0) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = decodeBits (pctxt, pvalue, nbytes * 8);
+ }
+ else
+ *pvalue = 0;
+ *pvalue += lower;
+
+ return stat;
+}
+
+int decodeSmallNonNegWholeNumber (OOCTXT* pctxt, ASN1UINT* pvalue)
+{
+ ASN1BOOL bitValue;
+ ASN1UINT len;
+ int ret;
+
+ if ((ret = DECODEBIT (pctxt, &bitValue)) != ASN_OK)
+ return ret;
+
+ if (bitValue == 0) {
+ return decodeBits (pctxt, pvalue, 6); /* 10.6.1 */
+ }
+ else {
+ if ((ret = decodeLength (pctxt, &len)) < 0)
+ return ret;
+
+ if ((ret = decodeByteAlign (pctxt)) != ASN_OK)
+ return ret;
+
+ return decodeBits (pctxt, pvalue, len*8);
+ }
+}
+
+int decodeVarWidthCharString (OOCTXT* pctxt, const char** pvalue)
+{
+ int stat;
+ ASN1OCTET* tmpstr;
+ ASN1UINT len;
+
+ /* note: need to save size constraint for use in alignCharStr */
+ /* because it will be cleared in decodeLength from the context.. */
+ Asn1SizeCnst* psize = pctxt->pSizeConstraint;
+
+ /* Decode length */
+
+ stat = decodeLength (pctxt, &len);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte-align */
+
+ if (alignCharStr (pctxt, len, 8, psize)) {
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Decode data */
+
+ tmpstr = (ASN1OCTET*) ASN1MALLOC (pctxt, len + 1);
+ if (0 != tmpstr) {
+ if ((stat = decodeOctets (pctxt, tmpstr, len, len * 8)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+
+ tmpstr[len] = '\0'; /* add null-terminator */
+ }
+ else
+ return LOG_ASN1ERR (pctxt, ASN_E_NOMEM);
+
+ *pvalue = (char*)tmpstr;
+
+ return ASN_OK;
+}
+
+static int decode16BitConstrainedString
+(OOCTXT* pctxt, Asn116BitCharString* pString, Asn116BitCharSet* pCharSet)
+{
+ ASN1UINT i, idx, nbits = pCharSet->alignedBits;
+ int stat;
+
+ /* Decode length */
+
+ stat = decodeLength (pctxt, &pString->nchars);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte-align */
+
+ stat = decodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Decode data */
+
+ pString->data = (ASN116BITCHAR*)
+ ASN1MALLOC (pctxt, pString->nchars*sizeof(ASN116BITCHAR));
+
+ if (pString->data) {
+ for (i = 0; i < pString->nchars; i++) {
+ stat = decodeBits (pctxt, &idx, nbits);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ pString->data[i] = (pCharSet->charSet.data == 0) ?
+ idx + pCharSet->firstChar : pCharSet->charSet.data[idx];
+ }
+ }
+ else
+ return LOG_ASN1ERR (pctxt, ASN_E_NOMEM);
+
+ return ASN_OK;
+}
+
+static int getComponentLength (OOCTXT* pctxt, ASN1UINT itemBits)
+{
+ OOCTXT lctxt;
+ ASN1UINT len, totalLen = 0;
+ int stat;
+
+ stat = initSubContext (&lctxt, pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = setPERBufferUsingCtxt (&lctxt, pctxt);
+ if (stat != ASN_OK) {
+ freeContext (&lctxt);
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ lctxt.pSizeConstraint = pctxt->pSizeConstraint;
+
+ for (;;) {
+ stat = decodeLength (&lctxt, &len);
+ if (stat < 0) {
+ freeContext (&lctxt);
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ totalLen += len;
+
+ if (stat == ASN_OK_FRAG) {
+ stat = moveBitCursor (&lctxt, len * itemBits);
+ if (stat != ASN_OK) {
+ freeContext (&lctxt);
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+ else break;
+ }
+
+ freeContext (&lctxt);
+
+ return totalLen;
+}
+
+int moveBitCursor (OOCTXT* pctxt, int bitOffset)
+{
+ int currBitOffset =
+ (pctxt->buffer.byteIndex * 8) + (8 - pctxt->buffer.bitOffset);
+
+ currBitOffset += bitOffset;
+
+ pctxt->buffer.byteIndex = (currBitOffset / 8);
+ pctxt->buffer.bitOffset = 8 - (currBitOffset % 8);
+
+ if (pctxt->buffer.byteIndex > pctxt->buffer.size) {
+ return (ASN_E_ENDOFBUF);
+ }
+
+ return ASN_OK;
+}
diff --git a/addons/ooh323c/src/dlist.c b/addons/ooh323c/src/dlist.c
new file mode 100644
index 000000000..52fc87d42
--- /dev/null
+++ b/addons/ooh323c/src/dlist.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+#include "ooasn1.h"
+
+void dListInit (DList* pList)
+{
+ if (pList) {
+ pList->count = 0;
+ pList->head = (DListNode*) 0;
+ pList->tail = (DListNode*) 0;
+ }
+}
+
+DListNode* dListAppend (OOCTXT* pctxt, DList* pList, void* pData)
+{
+ DListNode* pListNode = (DListNode*)
+ memAlloc (pctxt, sizeof(DListNode));
+
+ if (0 != pListNode) {
+ pListNode->data = pData;
+ pListNode->next = (DListNode*) 0;
+ if (0 != pList->tail) {
+ pList->tail->next = pListNode;
+ pListNode->prev = pList->tail;
+ }
+ if (0 == pList->head) {
+ pList->head = pListNode;
+ pListNode->prev = (DListNode*) 0;
+ }
+ pList->tail = pListNode;
+ pList->count++;
+ }
+
+ return pListNode;
+}
+
+DListNode* dListAppendNode (OOCTXT* pctxt, DList* pList, void* pData)
+{
+ DListNode* pListNode =
+ (DListNode*) (((char*)pData) - sizeof(DListNode));
+
+ if (0 != pListNode) {
+ pListNode->data = pData;
+ pListNode->next = (DListNode*) 0;
+ if (0 != pList->tail) {
+ pList->tail->next = pListNode;
+ pListNode->prev = pList->tail;
+ }
+ if (0 == pList->head) {
+ pList->head = pListNode;
+ pListNode->prev = (DListNode*) 0;
+ }
+ pList->tail = pListNode;
+ pList->count++;
+ }
+
+ return pListNode;
+}
+
+/* Delete the head node from the list and return the data item stored */
+/* in that node.. */
+
+void* dListDeleteHead (OOCTXT* pctxt, DList* pList)
+{
+ DListNode* pNode = (0 != pList) ? pList->head : 0;
+ if (0 != pNode) {
+ void* pdata = pNode->data;
+ dListRemove (pList, pNode);
+ memFreePtr (pctxt, pNode);
+ return pdata;
+ }
+ return 0;
+}
+
+/* Free all nodes, but not the data */
+void dListFreeNodes (OOCTXT* pctxt, DList* pList)
+{
+ DListNode* pNode, *pNextNode;
+
+ for (pNode = pList->head; pNode != 0; pNode = pNextNode) {
+ pNextNode = pNode->next;
+ memFreePtr (pctxt, pNode);
+ }
+ pList->count = 0;
+ pList->head = pList->tail = 0;
+}
+
+/* Free all nodes and their data */
+void dListFreeAll (OOCTXT* pctxt, DList* pList)
+{
+ DListNode* pNode, *pNextNode;
+
+ for (pNode = pList->head; pNode != 0; pNode = pNextNode) {
+ pNextNode = pNode->next;
+
+ memFreePtr (pctxt, pNode->data);
+ memFreePtr (pctxt, pNode);
+ }
+ pList->count = 0;
+ pList->head = pList->tail = 0;
+}
+
+/* Remove node from list. Node is not freed */
+void dListRemove (DList* pList, DListNode* node)
+{
+ if(node->next != 0) {
+ node->next->prev = node->prev;
+ }
+ else { /* tail */
+ pList->tail = node->prev;
+ }
+ if(node->prev != 0) {
+ node->prev->next = node->next;
+ }
+ else { /* head */
+ pList->head = node->next;
+ }
+ pList->count--;
+}
+
+void dListFindAndRemove(DList* pList, void *data)
+{
+ DListNode *pNode, *pNextNode;
+ for(pNode = pList->head; pNode !=0; pNode = pNextNode){
+ pNextNode = pNode->next;
+ if(pNode->data == data) /* pointer comparison*/
+ break;
+ }
+ if(pNode)
+ dListRemove(pList, pNode);
+}
+
+DListNode* dListFindByIndex (DList* pList, int index)
+{
+ DListNode* curNode;
+ int i;
+
+ if(index >= (int)pList->count) return 0;
+ for(i = 0, curNode = pList->head; i < index && curNode != 0; i++) {
+ curNode = curNode->next;
+ }
+ return curNode;
+}
+
+/* Insert item before given node */
+
+DListNode* dListInsertBefore
+(OOCTXT* pctxt, DList* pList, DListNode* node, const void* pData)
+{
+ DListNode* pListNode = (DListNode*) memAlloc (pctxt, sizeof(DListNode));
+
+ if (0 != pListNode) {
+ pListNode->data = (void*)pData;
+
+ if (node == 0) { /* insert before end (as last element) */
+ pListNode->next = (DListNode*) 0;
+ if (0 != pList->tail) {
+ pList->tail->next = pListNode;
+ pListNode->prev = pList->tail;
+ }
+ if (0 == pList->head) {
+ pList->head = pListNode;
+ pListNode->prev = (DListNode*) 0;
+ }
+ pList->tail = pListNode;
+ }
+ else if (node == pList->head) { /* insert as head (head case) */
+ pListNode->next = pList->head;
+ pListNode->prev = (DListNode*) 0;
+ if(pList->head != 0) {
+ pList->head->prev = pListNode;
+ }
+ if(pList->tail == 0) {
+ pList->tail = pListNode;
+ }
+ pList->head = pListNode;
+ }
+ else { /* other cases */
+ pListNode->next = node;
+ pListNode->prev = node->prev;
+ node->prev = pListNode;
+ /* here, pListNode->prev always should be non-zero,
+ * because if pListNode->prev is zero - it is head case (see above).
+ */
+ pListNode->prev->next = pListNode;
+ }
+
+ pList->count++;
+ }
+
+ return pListNode;
+}
+
+/* Insert item after given node */
+
+DListNode* dListInsertAfter
+(OOCTXT* pctxt, DList* pList, DListNode* node, const void* pData)
+{
+ DListNode* pListNode = (DListNode*) memAlloc (pctxt, sizeof(DListNode));
+
+ if (0 != pListNode) {
+ pListNode->data = (void*)pData;
+
+ if (node == 0) { /* insert as head (as first element) */
+ pListNode->next = pList->head;
+ pListNode->prev = (DListNode*) 0;
+ if (pList->head != 0) {
+ pList->head->prev = pListNode;
+ }
+ if (pList->tail == 0) {
+ pList->tail = pListNode;
+ }
+ pList->head = pListNode;
+ }
+ else if (node == pList->tail) { /* insert as tail (as last element) */
+ pListNode->next = (DListNode*) 0;
+ if (0 != pList->tail) {
+ pList->tail->next = pListNode;
+ pListNode->prev = pList->tail;
+ }
+ if (0 == pList->head) {
+ pList->head = pListNode;
+ pListNode->prev = (DListNode*) 0;
+ }
+ pList->tail = pListNode;
+ }
+ else { /* other cases */
+ pListNode->next = node->next;
+ pListNode->prev = node;
+ node->next = pListNode;
+ /* here, pListNode->next always should be non-zero,
+ * because if pListNode->next is zero - it is tail case (see above).
+ */
+ pListNode->next->prev = pListNode;
+ }
+
+ pList->count++;
+ }
+
+ return pListNode;
+}
+
diff --git a/addons/ooh323c/src/dlist.h b/addons/ooh323c/src/dlist.h
new file mode 100644
index 000000000..79663d999
--- /dev/null
+++ b/addons/ooh323c/src/dlist.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+/**
+ * @file dlist.h
+ * Doubly-linked list structures and utility functions.
+ */
+#ifndef _OODLIST_H_
+#define _OODLIST_H_
+
+struct OOCTXT;
+
+/**
+ * @defgroup llfuns Doubly-linked list structures and utility functions.
+ * @{
+ */
+typedef struct _DListNode {
+ void* data;
+ struct _DListNode* next;
+ struct _DListNode* prev;
+} DListNode;
+
+typedef struct _DList {
+ unsigned int count;
+ DListNode* head;
+ DListNode* tail;
+} DList;
+
+#define ALLOC_ASN1ELEMDNODE(pctxt,type) \
+(type*) (((char*)memHeapAllocZ (&(pctxt)->pTypeMemHeap, sizeof(type) + \
+sizeof(DListNode))) + sizeof(DListNode))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef EXTERN
+#if defined (MAKE_DLL)
+#define EXTERN __declspec(dllexport)
+#elif defined (USEASN1DLL)
+#define EXTERN __declspec(dllimport)
+#else
+#define EXTERN
+#endif /* MAKE_DLL */
+#endif /* EXTERN */
+
+/**
+ * This function appends an item to the linked list structure. The data item is
+ * passed into the function as a void pointer that can point to any object of
+ * any type. The memAlloc function is used to allocated the memory for the
+ * list node structure; therefore, all internal list memory will be released
+ * whenever memFree is called. The pointer to the data item itself is stored
+ * in the node structure - a copy is not made.
+ *
+ * @param pctxt A pointer to a context structure. This provides a
+ * storage area for the function to store all working
+ * variables that must be maintained between function
+ * calls.
+ * @param pList A pointer to a linked list structure onto which the data
+ * item is to be appended. A pointer to an updated linked
+ * list structure.
+ * @param pData A pointer to a data item to be appended to the list.
+ * @return A pointer to an allocated node structure used to link
+ * the given data value into the list.
+ */
+EXTERN DListNode* dListAppend
+(struct OOCTXT* pctxt, DList* pList, void* pData);
+
+EXTERN DListNode* dListAppendNode
+(struct OOCTXT* pctxt, DList* pList, void* pData);
+
+/**
+ * This function delete the head item from the list and returns a pointer
+ * the data item stored in that node. The memory for the node structure
+ * is released.
+ *
+ * @param pctxt A pointer to a context structure. This provides a
+ * storage area for the function to store all working
+ * variables that must be maintained between function
+ * calls.
+ * @param pList A pointer to the linked list structure from which
+ * the node will be deleted.
+ * @return A pointer to the data item stored in the deleted node.
+ */
+EXTERN void* dListDeleteHead (struct OOCTXT* pctxt, DList* pList);
+
+EXTERN DListNode* dListFindByIndex (DList* pList, int index);
+
+/**
+ * This function initializes a doubly linked list structure. It sets the number
+ * of elements to zero and sets all internal pointer values to NULL. A doubly
+ * linked-list structure is described by the DList type. Nodes of the list
+ * are of type DListNode.
+ *
+ * Memory for the structures is allocated using the memAlloc run-time
+ * function and is maintained within the context structure that is a required
+ * parameter to all dList functions. This memory is released when memFree
+ * is called or the Context is released. Unless otherwise noted, all data
+ * passed into the list functions is simply stored on the list by value (i.e. a
+ * deep-copy of the data is not done).
+ *
+ * @param pList A pointer to a linked list structure to be initialized.
+ */
+EXTERN void dListInit (DList* pList);
+
+/**
+ * This function removes all nodes from the linked list and releases the memory
+ * that was allocated for storing the node structures (DListNode). The data
+ * will not be released.
+ *
+ * @param pctxt A pointer to a context structure. This provides a
+ * storage area for the function to store all working
+ * variables that must be maintained between function
+ * calls.
+ * @param pList A pointer to a linked list structure onto which the data
+ * item is to be appended. A pointer to an updated linked
+ * list structure.
+ */
+EXTERN void dListFreeNodes (struct OOCTXT* pctxt, DList* pList);
+
+/**
+ * This function removes all nodes from the linked list structure and releases
+ * the memory that was allocated for storing the node structures
+ * (DListNode) and for data. The memory for data in each node must have
+ * been previously allocated with calls to memAlloc, memAllocZ, or
+ * memRealloc functions.
+ *
+ * @param pctxt Pointer to a context structure. This provides a
+ * storage area for the function to store all working
+ * variables that must be maintained between function
+ * calls.
+ * @param pList Pointer to a linked list structure.
+ */
+EXTERN void dListFreeAll (struct OOCTXT* pctxt, DList* pList);
+
+/**
+ * This function inserts an item into the linked list structure before the
+ * specified element.
+ *
+ * @param pctxt Pointer to a context structure.
+ * @param pList A pointer to a linked list structure into which the
+ * data item is to be inserted.
+ * @param node The position in the list where the item is to be
+ * inserted. The item will be inserted before this
+ * node or appended to the list if node is null.
+ * @param pData A pointer to the data item to be inserted to the list.
+ * @return A pointer to an allocated node structure used to
+ * link the given data value into the list.
+ */
+EXTERN DListNode* dListInsertBefore
+(struct OOCTXT* pctxt, DList* pList, DListNode* node, const void* pData);
+
+/**
+ * This function inserts an item into the linked list structure after the
+ * specified element.
+ *
+ * @param pctxt Pointer to a context structure.
+ * @param pList A pointer to a linked list structure into which the
+ * data item is to be inserted.
+ * @param node The position in the list where the item is to be
+ * inserted. The item will be inserted after this
+ * node or added as the head element if node is null.
+ * @param pData A pointer to the data item to be inserted to the list.
+ * @return A pointer to an allocated node structure used to
+ * link the given data value into the list.
+ */
+EXTERN DListNode* dListInsertAfter
+(struct OOCTXT* pctxt, DList* pList, DListNode* node, const void* pData);
+
+/**
+ * This function removes a node from the linked list structure. The memAlloc
+ * function was used to allocate the memory for the list node structure,
+ * therefore, all internal list memory will be released whenever memFree or
+ * memFreePtr is called.
+ *
+ * @param pList A pointer to a linked list structure onto which the data
+ * item is to be removed. A pointer to an updated linked
+ * list structure.
+ * @param node A pointer to the node that is to be removed. It should
+ * already be in the linked list structure.
+ */
+EXTERN void dListRemove (DList* pList, DListNode* node);
+void dListFindAndRemove(DList* pList, void* data);
+
+/**
+ * @}
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/addons/ooh323c/src/encode.c b/addons/ooh323c/src/encode.c
new file mode 100644
index 000000000..45e5916df
--- /dev/null
+++ b/addons/ooh323c/src/encode.c
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include "ooasn1.h"
+
+static int encode16BitConstrainedString
+(OOCTXT* pctxt, Asn116BitCharString value, Asn116BitCharSet* pCharSet);
+
+static int encode2sCompBinInt (OOCTXT* pctxt, ASN1INT value);
+static int encodeNonNegBinInt (OOCTXT* pctxt, ASN1UINT value);
+static int encodeUnconsLength (OOCTXT* pctxt, ASN1UINT value);
+static int getIdentByteCount (ASN1UINT ident);
+
+int encodeBit (OOCTXT* pctxt, ASN1BOOL value)
+{
+ int stat = ASN_OK;
+
+ /* If start of new byte, init to zero */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+
+ /* Adjust bit offset and determine if at end of current byte */
+
+ if (--pctxt->buffer.bitOffset < 0) {
+ if (++pctxt->buffer.byteIndex >= pctxt->buffer.size) {
+ if ((stat = encodeExpandBuffer (pctxt, 1)) != ASN_OK) {
+ return stat;
+ }
+ }
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ pctxt->buffer.bitOffset = 7;
+ }
+
+ /* Set single-bit value */
+
+ if (value) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] |=
+ ( 1 << pctxt->buffer.bitOffset );
+ }
+
+ /* If last bit in octet, set offsets to start new byte (ED, 9/7/01) */
+
+ if (pctxt->buffer.bitOffset == 0) {
+ pctxt->buffer.bitOffset = 8;
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+
+ return stat;
+}
+
+int encodeBits (OOCTXT* pctxt, ASN1UINT value, ASN1UINT nbits)
+{
+ int nbytes = (nbits + 7)/ 8, stat = ASN_OK;
+
+ if (nbits == 0) return stat;
+
+ /* If start of new byte, init to zero */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+
+ /* Mask off unused bits from the front of the value */
+
+ if (nbits < (sizeof(ASN1UINT) * 8))
+ value &= ((1 << nbits) - 1);
+
+ /* If bits will fit in current byte, set them and return */
+
+ if (nbits < (unsigned)pctxt->buffer.bitOffset) {
+ pctxt->buffer.bitOffset -= nbits;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] |=
+ ( value << pctxt->buffer.bitOffset );
+ return stat;
+ }
+
+ /* Check buffer space and allocate more memory if necessary */
+
+ stat = encodeCheckBuffer (pctxt, nbytes);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Set bits in remainder of the current byte and then loop */
+ /* to set bits in subsequent bytes.. */
+
+ nbits -= pctxt->buffer.bitOffset;
+ pctxt->buffer.data[pctxt->buffer.byteIndex++] |=
+ (ASN1OCTET)( value >> nbits );
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+
+ while (nbits >= 8) {
+ nbits -= 8;
+ pctxt->buffer.data[pctxt->buffer.byteIndex++] =
+ (ASN1OCTET)( value >> nbits );
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+
+ /* copy final partial byte */
+
+ pctxt->buffer.bitOffset = 8 - nbits;
+ if (nbits > 0) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] =
+ (ASN1OCTET)((value & ((1 << nbits)-1)) << pctxt->buffer.bitOffset);
+ }
+ else
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+
+ return stat;
+}
+
+int encodeBitsFromOctet (OOCTXT* pctxt, ASN1OCTET value, ASN1UINT nbits)
+{
+ int lshift = pctxt->buffer.bitOffset;
+ int rshift = 8 - pctxt->buffer.bitOffset;
+ int stat = ASN_OK;
+ ASN1OCTET mask;
+
+ if (nbits == 0) return ASN_OK;
+
+ /* Mask off unused bits from the end of the value */
+
+ if (nbits < 8) {
+ switch (nbits) {
+ case 1: mask = 0x80; break;
+ case 2: mask = 0xC0; break;
+ case 3: mask = 0xE0; break;
+ case 4: mask = 0xF0; break;
+ case 5: mask = 0xF8; break;
+ case 6: mask = 0xFC; break;
+ case 7: mask = 0xFE; break;
+ default:;
+ }
+ value &= mask;
+ }
+
+ /* If we are on a byte boundary, we can do a direct assignment */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = value;
+ if (nbits == 8) {
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+ else
+ pctxt->buffer.bitOffset -= nbits;
+ }
+
+ /* Otherwise, need to set some bits in the first octet and */
+ /* possibly some bits in the following octet.. */
+
+ else {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] |=
+ (ASN1OCTET)(value >> rshift);
+
+ pctxt->buffer.bitOffset -= nbits;
+
+ if (pctxt->buffer.bitOffset < 0) {
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] =
+ (ASN1OCTET)(value << lshift);
+ pctxt->buffer.bitOffset += 8;
+ }
+ }
+
+ return stat;
+}
+
+int encodeBitString (OOCTXT* pctxt, ASN1UINT numbits, const ASN1OCTET* data)
+{
+ int enclen, octidx = 0, stat;
+ Asn1SizeCnst* pSizeList = pctxt->pSizeConstraint;
+
+ for (;;) {
+ if ((enclen = encodeLength (pctxt, numbits)) < 0) {
+ return LOG_ASN1ERR (pctxt, enclen);
+ }
+
+ if (enclen > 0) {
+ ASN1BOOL doAlign;
+
+ stat = bitAndOctetStringAlignmentTest
+ (pSizeList, numbits, TRUE, &doAlign);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (doAlign) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ stat = encodeOctets (pctxt, &data[octidx], enclen);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ if (enclen < (int)numbits) {
+ numbits -= enclen;
+ octidx += (enclen/8);
+ }
+ else break;
+ }
+
+ return ASN_OK;
+}
+
+int encodeBMPString
+(OOCTXT* pctxt, ASN1BMPString value, Asn116BitCharSet* permCharSet)
+{
+ Asn116BitCharSet charSet;
+ int stat;
+
+ /* Set character set */
+
+ init16BitCharSet (&charSet, BMP_FIRST, BMP_LAST, BMP_ABITS, BMP_UBITS);
+
+ if (permCharSet) {
+ set16BitCharSet (pctxt, &charSet, permCharSet);
+ }
+
+ /* Encode constrained string */
+
+ stat = encode16BitConstrainedString (pctxt, value, &charSet);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return stat;
+}
+
+int encodeByteAlign (OOCTXT* pctxt)
+{
+ if (pctxt->buffer.bitOffset != 8) {
+ if ((pctxt->buffer.byteIndex + 1) >= pctxt->buffer.size) {
+ int stat = encodeExpandBuffer (pctxt, 1);
+ if (stat != ASN_OK) return (stat);
+ }
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.bitOffset = 8;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+
+ return ASN_OK;
+}
+
+int encodeCheckBuffer (OOCTXT* pctxt, ASN1UINT nbytes)
+{
+ int stat = ASN_OK;
+
+ /* Add one to required bytes because increment logic will always */
+ /* init the byte at the incremented index to zero.. */
+
+ if ( ( pctxt->buffer.byteIndex + nbytes + 1 ) >= pctxt->buffer.size ) {
+ if ((stat = encodeExpandBuffer (pctxt, nbytes+1)) != ASN_OK) {
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+
+ return (stat);
+}
+
+int encodeConsInteger
+(OOCTXT* pctxt, ASN1INT value, ASN1INT lower, ASN1INT upper)
+{
+ ASN1UINT range_value;
+ ASN1UINT adjusted_value;
+ int stat;
+
+ /* Check value against given range */
+
+ if (value < lower || value > upper) {
+ return ASN_E_CONSVIO;
+ }
+
+ /* Adjust range value based on lower/upper signed values and */
+ /* other possible conflicts.. */
+
+ if ((upper > 0 && lower >= 0) || (upper <= 0 && lower < 0)) {
+ range_value = upper - lower;
+ adjusted_value = value - lower;
+ }
+ else {
+ range_value = upper + abs(lower);
+ adjusted_value = value + abs(lower);
+ }
+
+ if (range_value != ASN1UINT_MAX) { range_value += 1; }
+
+ if (range_value == 0 || lower > upper)
+ stat = ASN_E_RANGERR;
+ else if (lower != upper) {
+ stat = encodeConsWholeNumber (pctxt, adjusted_value, range_value);
+ }
+ else
+ stat = ASN_OK;
+
+ return stat;
+}
+
+int encodeConsUnsigned
+(OOCTXT* pctxt, ASN1UINT value, ASN1UINT lower, ASN1UINT upper)
+{
+ ASN1UINT range_value;
+ ASN1UINT adjusted_value;
+ int stat;
+
+ /* Check for special case: if lower is 0 and upper is ASN1UINT_MAX, */
+ /* set range to ASN1UINT_MAX; otherwise to upper - lower + 1 */
+
+ range_value = (lower == 0 && upper == ASN1UINT_MAX) ?
+ ASN1UINT_MAX : upper - lower + 1;
+
+ adjusted_value = value - lower;
+
+ if (lower != upper) {
+ stat = encodeConsWholeNumber (pctxt, adjusted_value, range_value);
+ }
+ else
+ stat = ASN_OK;
+
+ return stat;
+}
+
+int encodeConsWholeNumber
+(OOCTXT* pctxt, ASN1UINT adjusted_value, ASN1UINT range_value)
+{
+ ASN1UINT nocts, range_bitcnt = getUIntBitCount (range_value - 1);
+ int stat;
+
+ if (adjusted_value >= range_value && range_value != ASN1UINT_MAX) {
+ return LOG_ASN1ERR (pctxt, ASN_E_RANGERR);
+ }
+
+ /* If range is <= 255, bit-field case (10.5.7a) */
+
+ if (range_value <= 255) {
+ return encodeBits (pctxt, adjusted_value, range_bitcnt);
+ }
+
+ /* If range is exactly 256, one-octet case (10.5.7b) */
+
+ else if (range_value == 256) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return encodeBits (pctxt, adjusted_value, 8);
+ }
+
+ /* If range > 256 and <= 64k (65536), two-octet case (10.5.7c) */
+
+ else if (range_value <= 65536) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return encodeBits (pctxt, adjusted_value, 16);
+ }
+
+ /* If range > 64k, indefinite-length case (10.5.7d) */
+
+ else {
+ /* Encode length determinant as a constrained whole number. */
+ /* Constraint is 1 to max number of bytes needed to hold */
+ /* the target integer value.. */
+
+ if (adjusted_value < 256) nocts = 1;
+ else if (adjusted_value < 65536) nocts = 2;
+ else if (adjusted_value < 0x1000000) nocts = 3;
+ else nocts = 4;
+
+ stat = encodeBits (pctxt, nocts - 1, 2);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return encodeNonNegBinInt (pctxt, adjusted_value);
+ }
+}
+
+int encodeConstrainedStringEx (OOCTXT* pctxt,
+ const char* string,
+ const char* charSet,
+ ASN1UINT abits, /* aligned char bits */
+ ASN1UINT ubits, /* unaligned char bits */
+ ASN1UINT canSetBits)
+{
+ ASN1UINT i, len = strlen(string);
+ int stat;
+ /* note: need to save size constraint for use in alignCharStr */
+ /* because it will be cleared in encodeLength from the context.. */
+ Asn1SizeCnst* psize = pctxt->pSizeConstraint;
+
+ /* Encode length */
+
+ stat = encodeLength (pctxt, len);
+ if (stat < 0) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte align */
+
+ if (alignCharStr (pctxt, len, abits, psize)) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Encode data */
+
+ if (abits >= canSetBits && canSetBits > 4) {
+ for (i = 0; i < len; i++) {
+ if ((stat = encodeBits (pctxt, string[i], abits)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+ else if (0 != charSet) {
+ ASN1UINT nchars = strlen(charSet), pos;
+ const char* ptr;
+ for (i = 0; i < len; i++) {
+ ptr = memchr (charSet, string[i], nchars);
+
+ if (0 == ptr)
+ return LOG_ASN1ERR (pctxt, ASN_E_CONSVIO);
+ else
+ pos = ptr - charSet;
+
+ if ((stat = encodeBits (pctxt, pos, abits)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+ else return LOG_ASN1ERR (pctxt, ASN_E_INVPARAM);
+
+ return stat;
+}
+
+int encodeExpandBuffer (OOCTXT* pctxt, ASN1UINT nbytes)
+{
+ if (pctxt->buffer.dynamic)
+ {
+ /* If dynamic encoding is enabled, expand the current buffer to */
+ /* allow encoding to continue. */
+
+ pctxt->buffer.size += ASN1MAX (ASN_K_ENCBUFSIZ, nbytes);
+
+ pctxt->buffer.data = (ASN1OCTET*) memHeapRealloc
+ (&pctxt->pMsgMemHeap, pctxt->buffer.data, pctxt->buffer.size);
+
+ if (!pctxt->buffer.data) return (ASN_E_NOMEM);
+
+ return (ASN_OK);
+ }
+
+ return (ASN_E_BUFOVFLW);
+}
+
+int encodeGetMsgBitCnt (OOCTXT* pctxt)
+{
+ int numBitsInLastByte = 8 - pctxt->buffer.bitOffset;
+ return ((pctxt->buffer.byteIndex * 8) + numBitsInLastByte);
+}
+
+ASN1OCTET* encodeGetMsgPtr (OOCTXT* pctxt, int* pLength)
+{
+ if (pLength) *pLength = getPERMsgLen (pctxt);
+ return pctxt->buffer.data;
+}
+
+int encodeIdent (OOCTXT* pctxt, ASN1UINT ident)
+{
+ ASN1UINT mask;
+ int nshifts = 0, stat;
+
+ if (ident !=0) {
+ ASN1UINT lv;
+ nshifts = getIdentByteCount (ident);
+ while (nshifts > 0) {
+ mask = ((ASN1UINT)0x7f) << (7 * (nshifts - 1));
+ nshifts--;
+ lv = (ASN1UINT)((ident & mask) >> (nshifts * 7));
+ if (nshifts != 0) { lv |= 0x80; }
+ if ((stat = encodeBits (pctxt, lv, 8)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+ else {
+ /* encode a single zero byte */
+ if ((stat = encodeBits (pctxt, 0, 8)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ return ASN_OK;
+}
+
+int encodeLength (OOCTXT* pctxt, ASN1UINT value)
+{
+ ASN1BOOL extendable;
+ Asn1SizeCnst* pSize =
+ checkSize (pctxt->pSizeConstraint, value, &extendable);
+ ASN1UINT lower = (pSize) ? pSize->lower : 0;
+ ASN1UINT upper = (pSize) ? pSize->upper : ASN1UINT_MAX;
+ int enclen, stat;
+
+ /* If size constraints exist and the given length did not fall */
+ /* within the range of any of them, signal constraint violation */
+ /* error.. */
+
+ if (pctxt->pSizeConstraint && !pSize)
+ return LOG_ASN1ERR (pctxt, ASN_E_CONSVIO);
+
+ /* Reset the size constraint in the context block structure */
+
+ pctxt->pSizeConstraint = 0;
+
+ /* If size constraint is present and extendable, encode extension */
+ /* bit.. */
+
+ if (extendable) {
+ stat = (pSize) ?
+ encodeBit (pctxt, pSize->extended) : encodeBit (pctxt, 1);
+
+ if (stat != ASN_OK) return (stat);
+ }
+
+ /* If upper limit is less than 64k, constrained case */
+
+ if (upper < 65536) {
+ stat = (lower == upper) ? ASN_OK :
+ encodeConsWholeNumber (pctxt, value - lower, upper - lower + 1);
+ enclen = (stat == ASN_OK) ? value : stat;
+ }
+ else {
+ /* unconstrained case or Constrained with upper bound >= 64K*/
+ enclen = encodeUnconsLength (pctxt, value);
+ }
+
+ return enclen;
+
+}
+
+int encodeObjectIdentifier (OOCTXT* pctxt, ASN1OBJID* pvalue)
+{
+ int len, stat;
+ ASN1UINT temp;
+ register int numids, i;
+
+ /* Calculate length in bytes and encode */
+
+ len = 1; /* 1st 2 arcs require 1 byte */
+ numids = pvalue->numids;
+ for (i = 2; i < numids; i++) {
+ len += getIdentByteCount (pvalue->subid[i]);
+ }
+
+ /* PER encode length */
+
+ if ((stat = encodeLength (pctxt, (ASN1UINT)len)) < 0) {
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Validate given object ID by applying ASN.1 rules */
+
+ if (0 == pvalue) return LOG_ASN1ERR (pctxt, ASN_E_INVOBJID);
+ if (numids < 2) return LOG_ASN1ERR (pctxt, ASN_E_INVOBJID);
+ if (pvalue->subid[0] > 2) return LOG_ASN1ERR (pctxt, ASN_E_INVOBJID);
+ if (pvalue->subid[0] != 2 && pvalue->subid[1] > 39)
+ return LOG_ASN1ERR (pctxt, ASN_E_INVOBJID);
+
+ /* Passed checks, encode object identifier */
+
+ /* Munge first two sub ID's and encode */
+
+ temp = ((pvalue->subid[0] * 40) + pvalue->subid[1]);
+ if ((stat = encodeIdent (pctxt, temp)) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+
+ /* Encode the remainder of the OID value */
+
+ for (i = 2; i < numids; i++) {
+ if ((stat = encodeIdent (pctxt, pvalue->subid[i])) != ASN_OK)
+ return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ return ASN_OK;
+}
+
+int encodebitsFromOctet (OOCTXT* pctxt, ASN1OCTET value, ASN1UINT nbits)
+{
+ int lshift = pctxt->buffer.bitOffset;
+ int rshift = 8 - pctxt->buffer.bitOffset;
+ int stat = ASN_OK;
+ ASN1OCTET mask;
+
+ if (nbits == 0) return ASN_OK;
+
+ /* Mask off unused bits from the end of the value */
+
+ if (nbits < 8) {
+ switch (nbits) {
+ case 1: mask = 0x80; break;
+ case 2: mask = 0xC0; break;
+ case 3: mask = 0xE0; break;
+ case 4: mask = 0xF0; break;
+ case 5: mask = 0xF8; break;
+ case 6: mask = 0xFC; break;
+ case 7: mask = 0xFE; break;
+ default:;
+ }
+ value &= mask;
+ }
+
+ /* If we are on a byte boundary, we can do a direct assignment */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = value;
+ if (nbits == 8) {
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ }
+ else
+ pctxt->buffer.bitOffset -= nbits;
+ }
+
+ /* Otherwise, need to set some bits in the first octet and */
+ /* possibly some bits in the following octet.. */
+
+ else {
+ pctxt->buffer.data[pctxt->buffer.byteIndex] |=
+ (ASN1OCTET)(value >> rshift);
+
+ pctxt->buffer.bitOffset -= nbits;
+
+ if (pctxt->buffer.bitOffset < 0) {
+ pctxt->buffer.byteIndex++;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] =
+ (ASN1OCTET)(value << lshift);
+ pctxt->buffer.bitOffset += 8;
+ }
+ }
+
+ return stat;
+}
+
+int encodeOctets (OOCTXT* pctxt, const ASN1OCTET* pvalue, ASN1UINT nbits)
+{
+ int i = 0, stat;
+ int numFullOcts = nbits / 8;
+
+ if (nbits == 0) return 0;
+
+ /* Check buffer space and allocate more memory if necessary */
+
+ stat = encodeCheckBuffer (pctxt, numFullOcts + 1);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (numFullOcts > 0) {
+
+ /* If the current bit offset is 8 (i.e. we don't have a */
+ /* byte started), can copy the string directly to the */
+ /* encode buffer.. */
+
+ if (pctxt->buffer.bitOffset == 8) {
+ memcpy (&pctxt->buffer.data[pctxt->buffer.byteIndex], pvalue,
+ numFullOcts);
+ pctxt->buffer.byteIndex += numFullOcts;
+ pctxt->buffer.data[pctxt->buffer.byteIndex] = 0;
+ i = numFullOcts;
+ }
+
+ /* Else, copy bits */
+
+ else {
+ for (i = 0; i < numFullOcts; i++) {
+ stat = encodeBitsFromOctet (pctxt, pvalue[i], 8);
+ if (stat != ASN_OK) return stat;
+ }
+ }
+ }
+
+ /* Move remaining bits from the last octet to the output buffer */
+
+ if (nbits % 8 != 0) {
+ stat = encodeBitsFromOctet (pctxt, pvalue[i], nbits % 8);
+ }
+
+ return stat;
+}
+
+int encodeOctetString (OOCTXT* pctxt, ASN1UINT numocts, const ASN1OCTET* data)
+{
+ int enclen, octidx = 0, stat;
+ Asn1SizeCnst* pSizeList = pctxt->pSizeConstraint;
+
+ for (;;) {
+ if ((enclen = encodeLength (pctxt, numocts)) < 0) {
+ return LOG_ASN1ERR (pctxt, enclen);
+ }
+
+ if (enclen > 0) {
+ ASN1BOOL doAlign;
+
+ stat = bitAndOctetStringAlignmentTest
+ (pSizeList, numocts, FALSE, &doAlign);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (doAlign) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ stat = encodeOctets (pctxt, &data[octidx], enclen * 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ if (enclen < (int)numocts) {
+ numocts -= enclen;
+ octidx += enclen;
+ }
+ else break;
+ }
+
+ return ASN_OK;
+}
+
+int encodeOpenType (OOCTXT* pctxt, ASN1UINT numocts, const ASN1OCTET* data)
+{
+ int enclen, octidx = 0, stat;
+ ASN1OCTET zeroByte = 0x00;
+ ASN1OpenType openType;
+
+ /* If open type contains length zero, add a single zero byte (10.1) */
+
+ if (numocts == 0) {
+ openType.numocts = 1;
+ openType.data = &zeroByte;
+ }
+ else {
+ openType.numocts = numocts;
+ openType.data = data;
+ }
+
+ /* Encode the open type */
+
+ for (;;) {
+ if ((enclen = encodeLength (pctxt, openType.numocts)) < 0) {
+ return LOG_ASN1ERR (pctxt, enclen);
+ }
+
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = encodeOctets (pctxt, &openType.data[octidx], enclen * 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ if (enclen < (int)openType.numocts) {
+ openType.numocts -= enclen;
+ octidx += enclen;
+ }
+ else break;
+ }
+
+ return ASN_OK;
+}
+
+int encodeOpenTypeExt (OOCTXT* pctxt, DList* pElemList)
+{
+ DListNode* pnode;
+ ASN1OpenType* pOpenType;
+ int stat;
+
+ if (0 != pElemList) {
+ pnode = pElemList->head;
+ while (0 != pnode) {
+ if (0 != pnode->data) {
+ pOpenType = (ASN1OpenType*)pnode->data;
+
+ if (pOpenType->numocts > 0) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ stat = encodeOpenType
+ (pctxt, pOpenType->numocts, pOpenType->data);
+
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ }
+ pnode = pnode->next;
+ }
+ }
+
+ return ASN_OK;
+}
+
+int encodeOpenTypeExtBits (OOCTXT* pctxt, DList* pElemList)
+{
+ DListNode* pnode;
+ int stat;
+
+ if (0 != pElemList) {
+ pnode = pElemList->head;
+
+ while (0 != pnode) {
+ stat = encodeBit (pctxt, (ASN1BOOL)(0 != pnode->data));
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ pnode = pnode->next;
+ }
+ }
+
+ return ASN_OK;
+}
+
+int encodeSemiConsInteger (OOCTXT* pctxt, ASN1INT value, ASN1INT lower)
+{
+ int nbytes, stat;
+ int shift = ((sizeof(value) - 1) * 8) - 1;
+ ASN1UINT tempValue;
+
+ if (lower > ASN1INT_MIN)
+ value -= lower;
+
+ /* Calculate signed number value length */
+
+ for ( ; shift > 0; shift -= 8) {
+ tempValue = (value >> shift) & 0x1ff;
+ if (tempValue == 0 || tempValue == 0x1ff) continue;
+ else break;
+ }
+
+ nbytes = (shift + 9) / 8;
+
+ /* Encode length */
+
+ if ((stat = encodeLength (pctxt, nbytes)) < 0) {
+ return stat;
+ }
+
+ if ((stat = encodeByteAlign (pctxt)) != ASN_OK)
+ return stat;
+
+ /* Encode signed value */
+
+ stat = encode2sCompBinInt (pctxt, value);
+
+ return stat;
+}
+
+int encodeSemiConsUnsigned (OOCTXT* pctxt, ASN1UINT value, ASN1UINT lower)
+{
+ int nbytes, stat;
+ int shift = ((sizeof(value) - 1) * 8) - 1;
+ ASN1UINT mask = 1UL << ((sizeof(value) * 8) - 1);
+ ASN1UINT tempValue;
+
+ value -= lower;
+
+ /* Calculate unsigned number value length */
+
+ for ( ; shift > 0; shift -= 8) {
+ tempValue = (value >> shift) & 0x1ff;
+
+ if (tempValue == 0) continue;
+ else break;
+ }
+
+ nbytes = (shift + 9) / 8;
+
+ /* If MS bit in unsigned number is set, add an extra zero byte */
+
+ if ((value & mask) != 0) nbytes++;
+
+ /* Encode length */
+
+ if ((stat = encodeLength (pctxt, nbytes)) < 0) {
+ return stat;
+ }
+
+ if ((stat = encodeByteAlign (pctxt)) != ASN_OK)
+ return stat;
+
+ /* Encode additional zero byte if necessary */
+
+ if (nbytes > sizeof(value)) {
+ stat = encodebitsFromOctet (pctxt, 0, 8);
+ if (stat != ASN_OK) return (stat);
+ }
+
+ /* Encode unsigned value */
+
+ stat = encodeNonNegBinInt (pctxt, value);
+
+ return stat;
+}
+
+int encodeSmallNonNegWholeNumber (OOCTXT* pctxt, ASN1UINT value)
+{
+ int stat;
+
+ if (value < 64) {
+ stat = encodeBits (pctxt, value, 7);
+ }
+ else {
+ ASN1UINT len;
+
+ /* Encode a one-byte length determinant value */
+ if (value < 256) len = 1;
+ else if (value < 65536) len = 2;
+ else if (value < 0x1000000) len = 3;
+ else len = 4;
+
+ stat = encodeBits (pctxt, len, 8);
+
+ /* Byte-align and encode the value */
+ if (stat == ASN_OK) {
+ if ((stat = encodeByteAlign (pctxt)) == ASN_OK) {
+ stat = encodeBits (pctxt, value, len*8);
+ }
+ }
+ }
+
+ return stat;
+}
+
+int encodeVarWidthCharString (OOCTXT* pctxt, const char* value)
+{
+ int stat;
+ ASN1UINT len = strlen (value);
+ /* note: need to save size constraint for use in alignCharStr */
+ /* because it will be cleared in encodeLength from the context.. */
+ Asn1SizeCnst* psize = pctxt->pSizeConstraint;
+
+ /* Encode length */
+
+ stat = encodeLength (pctxt, len);
+ if (stat < 0) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte align */
+
+ if (alignCharStr (pctxt, len, 8, psize)) {
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+
+ /* Encode data */
+
+ stat = encodeOctets (pctxt, (const ASN1OCTET*)value, len * 8);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ return ASN_OK;
+}
+
+static int encode16BitConstrainedString
+(OOCTXT* pctxt, Asn116BitCharString value, Asn116BitCharSet* pCharSet)
+{
+ ASN1UINT i, pos;
+ ASN1UINT nbits = pCharSet->alignedBits;
+ int stat;
+
+ /* Encode length */
+
+ stat = encodeLength (pctxt, value.nchars);
+ if (stat < 0) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Byte align */
+
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* Encode data */
+
+ for (i = 0; i < value.nchars; i++) {
+ if (pCharSet->charSet.data == 0) {
+ stat = encodeBits
+ (pctxt, value.data[i] - pCharSet->firstChar, nbits);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ }
+ else {
+ for (pos = 0; pos < pCharSet->charSet.nchars; pos++) {
+ if (value.data[i] == pCharSet->charSet.data[pos]) {
+ stat = encodeBits (pctxt, pos, nbits);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+ break;
+ }
+ }
+ }
+ }
+
+ return stat;
+}
+
+int encode2sCompBinInt (OOCTXT* pctxt, ASN1INT value)
+{
+ /* 10.4.6 A minimum octet 2's-complement-binary-integer encoding */
+ /* of the whole number has a field width that is a multiple of 8 */
+ /* bits and also satisifies the condition that the leading 9 bits */
+ /* field shall not be all zeros and shall not be all ones. */
+
+ /* first encode integer value into a local buffer */
+
+ ASN1OCTET lbuf[8], lb;
+ ASN1INT i = sizeof(lbuf), temp = value;
+
+ memset (lbuf, 0, sizeof(lbuf));
+ do {
+ lb = temp % 256;
+ temp /= 256;
+ if (temp < 0 && lb != 0) temp--; /* two's complement adjustment */
+ lbuf[--i] = lb;
+ } while (temp != 0 && temp != -1);
+
+ /* If the value is positive and bit 8 of the leading byte is set, */
+ /* copy a zero byte to the contents to signal a positive number.. */
+
+ if (value > 0 && (lb & 0x80) != 0) {
+ i--;
+ }
+
+ /* If the value is negative and bit 8 of the leading byte is clear, */
+ /* copy a -1 byte (0xFF) to the contents to signal a negative */
+ /* number.. */
+
+ else if (value < 0 && ((lb & 0x80) == 0)) {
+ lbuf[--i] = 0xff;
+ }
+
+ /* Add the data to the encode buffer */
+
+ return encodeOctets (pctxt, &lbuf[i], (sizeof(lbuf) - i) * 8);
+}
+
+static int encodeNonNegBinInt (OOCTXT* pctxt, ASN1UINT value)
+{
+ /* 10.3.6 A minimum octet non-negative binary integer encoding of */
+ /* the whole number (which does not predetermine the number of */
+ /* octets to be used for the encoding) has a field which is a */
+ /* multiple of 8 bits and also satisifies the condition that the */
+ /* leading eight bits of the field shall not be zero unless the */
+ /* field is precisely 8 bits long. */
+
+ ASN1UINT bitcnt = (value == 0) ? 1 : getUIntBitCount (value);
+
+ /* round-up to nearest 8-bit boundary */
+
+ bitcnt = (bitcnt + 7) & (~7);
+
+ /* encode bits */
+
+ return encodeBits (pctxt, value, bitcnt);
+}
+
+static int encodeUnconsLength (OOCTXT* pctxt, ASN1UINT value)
+{
+ int enclen, stat;
+
+ stat = encodeByteAlign (pctxt);
+ if (stat != ASN_OK) return LOG_ASN1ERR (pctxt, stat);
+
+ /* 1 octet case */
+ if (value < 128) {
+ stat = encodeBits (pctxt, value, 8);
+ enclen = (stat == ASN_OK) ? value : stat;
+ }
+ /* 2 octet case */
+ else if (value < 16384) {
+ if ((stat = encodeBit (pctxt, 1)) == ASN_OK)
+ stat = encodeBits (pctxt, value, 15);
+ enclen = (stat == ASN_OK) ? value : stat;
+ }
+ /* fragmentation case */
+ else {
+ int multiplier = ASN1MIN (value/16384, 4);
+ encodeBit (pctxt, 1); /* set bit 8 of first octet */
+ encodeBit (pctxt, 1); /* set bit 7 of first octet */
+ stat = encodeBits (pctxt, multiplier, 6);
+ enclen = (stat == ASN_OK) ? 16384 * multiplier : stat;
+ }
+
+ return enclen;
+}
+
+static int getIdentByteCount (ASN1UINT ident)
+{
+ if (ident < (1u << 7)) { /* 7 */
+ return 1;
+ }
+ else if (ident < (1u << 14)) { /* 14 */
+ return 2;
+ }
+ else if (ident < (1u << 21)) { /* 21 */
+ return 3;
+ }
+ else if (ident < (1u << 28)) { /* 28 */
+ return 4;
+ }
+ return 5;
+}
diff --git a/addons/ooh323c/src/errmgmt.c b/addons/ooh323c/src/errmgmt.c
new file mode 100644
index 000000000..d41bad102
--- /dev/null
+++ b/addons/ooh323c/src/errmgmt.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 1997-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+/* Error management functions */
+
+#include <stdlib.h>
+#include "ooasn1.h"
+
+/* Error status text */
+static const char* g_status_text[] = {
+ "Encode buffer overflow",
+ "Unexpected end of buffer on decode",
+ "Unexpected tag encountered: expected = %s, parsed = %s",
+ "Invalid object identifier",
+ "Invalid field length detected",
+ "Enumerated value %s not in defined set",
+ "Duplicate element in SET",
+ "Missing required element in SET",
+ "Element with tag %s not part of SET",
+ "Max elements defined for SEQUENCE field exceeded",
+ "Element with tag %s is an invalid option in choice",
+ "No dynamic memory available",
+ "Invalid string type",
+ "Invalid hex string",
+ "Invalid binary string",
+ "Invalid real value",
+ "Max items in sized BIT or OCTET STRING field exceeded",
+ "Invalid value specification",
+ "No definition found for referenced defined value",
+ "No definition found for referenced defined type",
+ "Invalid tag value",
+ "Nesting level too deep",
+ "Value constraint violation: field %s, value %s",
+ "Value range error: lower bound is greater than upper",
+ "Unexpected end of file detected",
+ "Invalid UTF-8 character at index %d",
+ "List error: concurrent modification attempt while iterating",
+ "List error: illegal state for attempted operation",
+ "Array index out of bounds",
+ "Invalid parameter passed to function or method",
+ "Invalid time string format",
+ "Context is not initialized",
+ "ASN.1 value will not fit in target variable",
+ "Character is not within the defined character set",
+ "Invalid XML state for attempted operation",
+ "Error condition returned from XML parser:\n%s",
+ "SEQUENCE elements not in correct order",
+ "Invalid index for table constraint identifier",
+ "Invalid value for relational table constraint fixed type field",
+ "File not found",
+ "File read error",
+ "File write error",
+ "Invalid Base64 string",
+ "Socket error",
+ "XML interface library not found",
+ "Invalid XML interface library"
+} ;
+
+#define ASN1_K_MAX_STAT (sizeof(g_status_text)/sizeof(char *))
+
+/* Add an integer parameter to an error message */
+
+int errAddIntParm (ASN1ErrInfo* pErrInfo, int errParm)
+{
+ char lbuf[16];
+ sprintf (lbuf, "%d", errParm);
+ return errAddStrParm (pErrInfo, lbuf);
+}
+
+/* Add a character string parameter to an error message */
+
+int errAddStrParm (ASN1ErrInfo* pErrInfo, const char* errprm_p)
+{
+#if defined(_NO_THREADS) || !defined(_NO_MALLOC)
+ if (pErrInfo->parmcnt < ASN_K_MAXERRP) {
+ char* tmpstr = (char*) ASN1CRTMALLOC0 (strlen(errprm_p)+1);
+ strcpy (tmpstr, errprm_p);
+ pErrInfo->parms[pErrInfo->parmcnt] = tmpstr;
+ pErrInfo->parmcnt++;
+ return TRUE;
+ }
+ else
+#endif
+ return FALSE;
+}
+
+/* Add an unsigned integer parameter to an error message */
+
+int errAddUIntParm (ASN1ErrInfo* pErrInfo, unsigned int errParm)
+{
+ char lbuf[16];
+ sprintf (lbuf, "%u", errParm);
+ return errAddStrParm (pErrInfo, lbuf);
+}
+
+/* Free error parameter memory */
+
+void errFreeParms (ASN1ErrInfo* pErrInfo)
+{
+#if defined(_NO_THREADS) || !defined(_NO_MALLOC)
+ int i;
+
+ for (i = 0; i < pErrInfo->parmcnt; i++)
+ ASN1CRTFREE0 ((char*)pErrInfo->parms[i]);
+#endif
+
+ pErrInfo->parmcnt = 0;
+ pErrInfo->status = 0;
+}
+
+/* Reset error */
+
+int errReset (ASN1ErrInfo* pErrInfo)
+{
+ errFreeParms (pErrInfo);
+ pErrInfo->stkx = 0;
+ return ASN_OK;
+}
+
+/* Format error message */
+
+char* errFmtMsg (ASN1ErrInfo* pErrInfo, char* bufp)
+{
+ const char* tp;
+ int i, j, pcnt;
+
+ if (pErrInfo->status < 0)
+ {
+ i = abs (pErrInfo->status + 1);
+
+ if (i >= 0 && i < ASN1_K_MAX_STAT)
+ {
+ /* Substitute error parameters into error message */
+
+ j = pcnt = 0;
+ tp = g_status_text[i];
+
+ while (*tp)
+ {
+ if (*tp == '%' && *(tp+1) == 's')
+ {
+ /* Plug in error parameter */
+
+ if (pcnt < pErrInfo->parmcnt && pErrInfo->parms[pcnt])
+ {
+ strcpy (&bufp[j], pErrInfo->parms[pcnt]);
+ j += strlen (pErrInfo->parms[pcnt++]);
+ }
+ else
+ bufp[j++] = '?';
+
+ tp += 2;
+ }
+ else
+ bufp[j++] = *tp++;
+ }
+
+ bufp[j] = '\0'; /* null terminate string */
+ }
+ else
+ strcpy (bufp, "unrecognized completion status");
+ }
+ else strcpy (bufp, "normal completion status");
+
+ return (bufp);
+}
+
+/* Get error text in a dynamic memory buffer. This allocates memory */
+/* using the 'memAlloc' function. This memory is automatically freed */
+/* at the time the 'memFree' function is called. */
+
+char* errGetText (OOCTXT* pctxt)
+{
+ char lbuf[500];
+ char* pBuf = (char*) ASN1MALLOC (pctxt,
+ (sizeof(lbuf) + 100 * (2 + pctxt->errInfo.stkx)) * sizeof(char));
+
+ sprintf (pBuf, "ASN.1 ERROR: Status %d\n", pctxt->errInfo.status);
+ sprintf (lbuf, "%s\nStack trace:", errFmtMsg (&pctxt->errInfo, lbuf));
+ strcat(pBuf, lbuf);
+
+ while (pctxt->errInfo.stkx > 0) {
+ pctxt->errInfo.stkx--;
+ sprintf (lbuf, " Module: %s, Line %d\n",
+ pctxt->errInfo.stack[pctxt->errInfo.stkx].module,
+ pctxt->errInfo.stack[pctxt->errInfo.stkx].lineno);
+ strcat(pBuf, lbuf);
+ }
+
+ errFreeParms (&pctxt->errInfo);
+
+ return pBuf;
+}
+
+/* Print error information to the standard output */
+
+void errPrint (ASN1ErrInfo* pErrInfo)
+{
+ char lbuf[200];
+ printf ("ASN.1 ERROR: Status %d\n", pErrInfo->status);
+ printf ("%s\n", errFmtMsg (pErrInfo, lbuf));
+ printf ("Stack trace:");
+ while (pErrInfo->stkx > 0) {
+ pErrInfo->stkx--;
+ printf (" Module: %s, Line %d\n",
+ pErrInfo->stack[pErrInfo->stkx].module,
+ pErrInfo->stack[pErrInfo->stkx].lineno);
+ }
+ errFreeParms (pErrInfo);
+}
+
+/* Copy error data from one error structure to another */
+
+int errCopyData (ASN1ErrInfo* pSrcErrInfo, ASN1ErrInfo* pDestErrInfo)
+{
+ int i;
+ pDestErrInfo->status = pSrcErrInfo->status;
+
+ /* copy error parameters */
+
+ for (i = 0; i < pSrcErrInfo->parmcnt; i++) {
+ errAddStrParm (pDestErrInfo, pSrcErrInfo->parms[i]);
+ }
+
+ /* copy stack info */
+
+ for (i = 0; i < pSrcErrInfo->stkx; i++) {
+ if (pDestErrInfo->stkx < ASN_K_MAXERRSTK) {
+ pDestErrInfo->stack[pDestErrInfo->stkx].module =
+ pSrcErrInfo->stack[i].module;
+ pDestErrInfo->stack[pDestErrInfo->stkx++].lineno =
+ pSrcErrInfo->stack[i].lineno;
+ }
+ }
+
+ return (pSrcErrInfo->status);
+}
+
+
+int errSetData (ASN1ErrInfo* pErrInfo, int status,
+ const char* module, int lno)
+{
+ if (pErrInfo->status == 0) {
+ pErrInfo->status = status;
+ }
+ ooLogAsn1Error(status, module, lno);
+ return status;
+}
diff --git a/addons/ooh323c/src/eventHandler.c b/addons/ooh323c/src/eventHandler.c
new file mode 100644
index 000000000..7b15759a6
--- /dev/null
+++ b/addons/ooh323c/src/eventHandler.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+
+#include "eventHandler.h"
+
+void setEventHandler (OOCTXT* pctxt, EventHandler* pHandler)
+{
+ pctxt->pEventHandler = pHandler;
+}
+
+void removeEventHandler (OOCTXT* pctxt)
+{
+ pctxt->pEventHandler = 0;
+}
+
+void invokeStartElement (OOCTXT* pctxt, const char* name, int index)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->startElement (name, index);
+ }
+}
+
+void invokeEndElement (OOCTXT* pctxt, const char* name, int index)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->endElement (name, index);
+ }
+}
+
+void invokeBoolValue (OOCTXT* pctxt, ASN1BOOL value)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->boolValue (value);
+ }
+}
+
+void invokeIntValue (OOCTXT* pctxt, ASN1INT value)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->intValue (value);
+ }
+}
+
+void invokeUIntValue (OOCTXT* pctxt, ASN1UINT value)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->uIntValue (value);
+ }
+}
+
+void invokeBitStrValue (OOCTXT* pctxt, ASN1UINT numbits,
+ const ASN1OCTET* data)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->bitStrValue (numbits, data);
+ }
+}
+
+void invokeOctStrValue (OOCTXT* pctxt, ASN1UINT numocts,
+ const ASN1OCTET* data)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->octStrValue (numocts, data);
+ }
+}
+
+void invokeCharStrValue (OOCTXT* pctxt, const char* value)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->charStrValue (value);
+ }
+}
+
+void invokeCharStr16BitValue (OOCTXT* pctxt, ASN1UINT nchars,
+ ASN116BITCHAR* data)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->charStr16BitValue (nchars, data);
+ }
+}
+
+void invokeNullValue (OOCTXT* pctxt)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->nullValue ();
+ }
+}
+
+void invokeOidValue (OOCTXT* pctxt, ASN1UINT numSubIds, ASN1UINT* pSubIds)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->oidValue (numSubIds, pSubIds);
+ }
+}
+
+void invokeEnumValue (OOCTXT* pctxt, ASN1UINT value)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->enumValue (value);
+ }
+}
+
+void invokeOpenTypeValue (OOCTXT* pctxt, ASN1UINT numocts,
+ const ASN1OCTET* data)
+{
+ if (0 != pctxt->pEventHandler) {
+ pctxt->pEventHandler->openTypeValue (numocts, data);
+ }
+}
diff --git a/addons/ooh323c/src/eventHandler.h b/addons/ooh323c/src/eventHandler.h
new file mode 100644
index 000000000..320a55b99
--- /dev/null
+++ b/addons/ooh323c/src/eventHandler.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+/**
+ * @file eventHandler.h
+ * C event handler structure. This structure holds event handler function
+ * callbacks for use by the generated code.
+ */
+/**
+ * @defgroup EventHandler event handler
+ * Event handler structures and callback function definitions.
+ * @{
+ */
+#ifndef _EVENTHANDLER_H_
+#define _EVENTHANDLER_H_
+
+#include <stdio.h>
+#include "ooasn1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef EXTERN
+#if define (MAKE_DLL)
+#define EXTERN __declspec(dllexport)
+#else
+#define EXTERN
+#endif /* MAKE_DLL */
+#endif /* EXTERN */
+
+
+/**
+ * This is a function pointer for a callback function which is invoked
+ * from within a decode function when an element of a SEQUENCE, SET,
+ * SEQUENCE OF, SET OF, or CHOICE construct is parsed.
+ *
+ * @param name For SEQUENCE, SET, or CHOICE, this is the name of the
+ * element as defined in the ASN.1 defination. For
+ * SEQUENCE OF or SET OF, this is set to the name
+ * "element".
+ * @param index For SEQUENCE, SET, or CHOICE, this is not used and is
+ * set to the value
+ * -1. For SEQUENCE OF or SET OF, this contains the
+ * zero-based index of the element in the conceptual
+ * array associated with the construct.
+ * @return - none
+ */
+typedef void (*StartElement) (const char* name, int index) ;
+
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when parsing is complete on an element of a
+ * SEQUENCE, SET, SEQUENCE OF, SET OF, or CHOICE construct.
+ *
+ * @param name For SEQUENCE, SET, or CHOICE, this is the name of the
+ * element as defined in the ASN.1 defination. For
+ * SEQUENCE OF or SET OF, this is set to the name
+ * "element".
+ * @param index For SEQUENCE, SET, or CHOICE, this is not used and is
+ * set to the value
+ * -1. For SEQUENCE OF or SET OF, this contains the
+ * zero-based index of the element in the conceptual
+ * array associated with the construct.
+ * @return - none
+ */
+typedef void (*EndElement) (const char* name, int index) ;
+
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of the BOOLEAN ASN.1 type is parsed.
+ *
+ * @param value Parsed value.
+ * @return - none
+ */
+typedef void (*BoolValue) (ASN1BOOL value);
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of the INTERGER ASN.1 type is parsed.
+ *
+ * @param value Parsed value.
+ * @return - none
+ */
+typedef void (*IntValue) (ASN1INT value);
+
+/**
+ * This is a function pointer for a callback function which is invoked
+ * from within a decode function when a value of the INTEGER ASN.1 type
+ * is parsed. In this case, constraints on the integer value forced the
+ * use of unsigned integer C type to represent the value.
+ *
+ * @param value Parsed value.
+ * @return - none
+ */
+typedef void (*UIntValue) (ASN1UINT value);
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of the BIT STRING ASN.1 type is
+ * parsed.
+ *
+ * @param numbits - Number of bits in the parsed value.
+ * @param data - Pointer to a byte array that contains the bit
+ * string data.
+ * @return - none
+ */
+typedef void (*BitStrValue) (ASN1UINT numbits, const ASN1OCTET* data);
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of one of the OCTET STRING ASN.1 type
+ * is parsed.
+ *
+ * @param numocts Number of octets in the parsed value.
+ * @param data Pointer to byte array containing the octet string
+ * data.
+ * @return - none
+ */
+typedef void (*OctStrValue) (ASN1UINT numocts, const ASN1OCTET* data) ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of one of the 8-bit ASN.1 character
+ * string types is parsed.
+ *
+ * @param value Null terminated character string value.
+ * @return - none
+ */
+typedef void (*CharStrValue) (const char* value) ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of one of the 16-bit ASN.1 character
+ * string types is parsed.
+ *
+ * This is used for the ASN.1 BmpString type.
+ *
+ * @param nchars Number of characters in the parsed value.
+ * @param data Pointer to an array containing 16-bit values.
+ * These are represented using unsigned short integer
+ * values.
+ * @return - none
+ */
+typedef void (*CharStrValue16Bit) (ASN1UINT nchars, ASN116BITCHAR* data) ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of the NULL ASN.1 type is parsed.
+ *
+ * @param - none
+ * @return - none
+ */
+typedef void (*NullValue) () ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function whn a value the OBJECT IDENTIFIER ASN.1 type is
+ * parsed.
+ *
+ * @param numSubIds Number of subidentifiers in the object identifier.
+ * @param pSubIds Pointer to array containing the subidentifier values.
+ * @return -none
+ */
+typedef void (*OidValue) (ASN1UINT numSubIds, ASN1UINT* pSubIds) ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when a value of the ENUMERATED ASN.1 type is
+ * parsed.
+ *
+ * @param value - Parsed enumerated value
+ * @return - none
+ */
+typedef void (*EnumValue) (ASN1UINT value) ;
+
+/**
+ * This is a function pointer for a callback function which is invoked from
+ * within a decode function when an ASN.1 open type is parsed.
+ *
+ * @param numocts Number of octets in the parsed value.
+ * @param data Pointer to byet array contain in tencoded ASN.1
+ * value.
+ * @return - none
+ */
+typedef void (*OpenTypeValue) (ASN1UINT numocts, const ASN1OCTET* data) ;
+
+
+/**
+ * This is a basic C based event handler structure, which can be used
+ * to define user-defined event handlers.
+ */
+typedef struct EventHandler {
+ StartElement startElement;
+ EndElement endElement;
+ BoolValue boolValue;
+ IntValue intValue;
+ UIntValue uIntValue;
+ BitStrValue bitStrValue;
+ OctStrValue octStrValue;
+ CharStrValue charStrValue;
+ CharStrValue16Bit charStr16BitValue;
+ NullValue nullValue;
+ OidValue oidValue;
+ EnumValue enumValue;
+ OpenTypeValue openTypeValue;
+} EventHandler;
+
+
+/**
+ * This function sets the event handler object within the context. It
+ * will overwrite the definition of any handler that was set previously.
+ *
+ * @param pctxt Context to which event handler has to be added.
+ * @param pHandler Pointer to the event handler structure.
+ * @return none
+ */
+EXTERN void setEventHandler (OOCTXT* pctxt, EventHandler* pHandler);
+
+/**
+ * This function is called to remove the event handler current defined
+ * in the context. This is done by setting the event handler object
+ * pointer to NULL.
+ *
+ * @param pctxt Context from which event handler has to be removed.
+ * @return none
+ */
+EXTERN void removeEventHandler (OOCTXT* pctxt);
+
+/**
+ * The following functions are invoked from within the generated
+ * code to call the various user-defined event handler methods ..
+ */
+EXTERN void invokeStartElement (OOCTXT* pctxt, const char* name, int index);
+EXTERN void invokeEndElement (OOCTXT* pctxt, const char* name, int index);
+EXTERN void invokeBoolValue (OOCTXT* pctxt, ASN1BOOL value);
+EXTERN void invokeIntValue (OOCTXT* pctxt, ASN1INT value);
+EXTERN void invokeUIntValue (OOCTXT* pctxt, ASN1UINT value);
+
+EXTERN void invokeBitStrValue
+(OOCTXT* pctxt, ASN1UINT numbits, const ASN1OCTET* data);
+
+EXTERN void invokeOctStrValue
+(OOCTXT* pctxt, ASN1UINT numocts, const ASN1OCTET* data);
+
+EXTERN void invokeCharStrValue (OOCTXT* pctxt, const char* value);
+
+EXTERN void invokeCharStr16BitValue
+(OOCTXT* pctxt, ASN1UINT nchars, ASN116BITCHAR* data);
+
+EXTERN void invokeNullValue (OOCTXT* pctxt);
+
+EXTERN void invokeOidValue
+(OOCTXT* pctxt, ASN1UINT numSubIds, ASN1UINT* pSubIds);
+
+EXTERN void invokeEnumValue (OOCTXT* pctxt, ASN1UINT value);
+
+EXTERN void invokeOpenTypeValue
+(OOCTXT* pctxt, ASN1UINT numocts, const ASN1OCTET* data);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/addons/ooh323c/src/h323/H235-SECURITY-MESSAGES.h b/addons/ooh323c/src/h323/H235-SECURITY-MESSAGES.h
new file mode 100644
index 000000000..2f401b39a
--- /dev/null
+++ b/addons/ooh323c/src/h323/H235-SECURITY-MESSAGES.h
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+/**
+ * This file was generated by the Objective Systems ASN1C Compiler
+ * (http://www.obj-sys.com). Version: 5.72, Date: 22-Mar-2005.
+ */
+#ifndef H235_SECURITY_MESSAGES_H
+#define H235_SECURITY_MESSAGES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ooasn1.h"
+
+/**************************************************************/
+/* */
+/* ChallengeString */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ChallengeString {
+ ASN1UINT numocts;
+ ASN1OCTET data[128];
+} H235ChallengeString;
+
+EXTERN int asn1PE_H235ChallengeString (OOCTXT* pctxt, H235ChallengeString* pvalue);
+
+EXTERN int asn1PD_H235ChallengeString (OOCTXT* pctxt, H235ChallengeString* pvalue);
+
+/**************************************************************/
+/* */
+/* TimeStamp */
+/* */
+/**************************************************************/
+
+typedef ASN1UINT H235TimeStamp;
+
+EXTERN int asn1PE_H235TimeStamp (OOCTXT* pctxt, H235TimeStamp value);
+
+EXTERN int asn1PD_H235TimeStamp (OOCTXT* pctxt, H235TimeStamp* pvalue);
+
+/**************************************************************/
+/* */
+/* RandomVal */
+/* */
+/**************************************************************/
+
+typedef ASN1INT H235RandomVal;
+
+EXTERN int asn1PE_H235RandomVal (OOCTXT* pctxt, H235RandomVal value);
+
+EXTERN int asn1PD_H235RandomVal (OOCTXT* pctxt, H235RandomVal* pvalue);
+
+/**************************************************************/
+/* */
+/* Password */
+/* */
+/**************************************************************/
+
+typedef ASN1BMPString H235Password;
+
+EXTERN int asn1PE_H235Password (OOCTXT* pctxt, H235Password value);
+
+EXTERN int asn1PD_H235Password (OOCTXT* pctxt, H235Password* pvalue);
+
+/**************************************************************/
+/* */
+/* EncodedPwdCertToken */
+/* */
+/**************************************************************/
+
+typedef ASN1OpenType H235EncodedPwdCertToken;
+
+EXTERN int asn1PE_H235EncodedPwdCertToken (OOCTXT* pctxt, H235EncodedPwdCertToken value);
+
+EXTERN int asn1PD_H235EncodedPwdCertToken (OOCTXT* pctxt, H235EncodedPwdCertToken* pvalue);
+
+/**************************************************************/
+/* */
+/* NonStandardParameter */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235NonStandardParameter {
+ ASN1OBJID nonStandardIdentifier;
+ ASN1DynOctStr data;
+} H235NonStandardParameter;
+
+EXTERN int asn1PE_H235NonStandardParameter (OOCTXT* pctxt, H235NonStandardParameter* pvalue);
+
+EXTERN int asn1PD_H235NonStandardParameter (OOCTXT* pctxt, H235NonStandardParameter* pvalue);
+
+/**************************************************************/
+/* */
+/* AuthenticationBES */
+/* */
+/**************************************************************/
+
+/* Choice tag constants */
+
+#define T_H235AuthenticationBES_default_ 1
+#define T_H235AuthenticationBES_radius 2
+#define T_H235AuthenticationBES_extElem1 3
+
+typedef struct EXTERN H235AuthenticationBES {
+ int t;
+} H235AuthenticationBES;
+
+EXTERN int asn1PE_H235AuthenticationBES (OOCTXT* pctxt, H235AuthenticationBES* pvalue);
+
+EXTERN int asn1PD_H235AuthenticationBES (OOCTXT* pctxt, H235AuthenticationBES* pvalue);
+
+/**************************************************************/
+/* */
+/* AuthenticationMechanism */
+/* */
+/**************************************************************/
+
+/* Choice tag constants */
+
+#define T_H235AuthenticationMechanism_dhExch 1
+#define T_H235AuthenticationMechanism_pwdSymEnc 2
+#define T_H235AuthenticationMechanism_pwdHash 3
+#define T_H235AuthenticationMechanism_certSign 4
+#define T_H235AuthenticationMechanism_ipsec 5
+#define T_H235AuthenticationMechanism_tls 6
+#define T_H235AuthenticationMechanism_nonStandard 7
+#define T_H235AuthenticationMechanism_authenticationBES 8
+#define T_H235AuthenticationMechanism_extElem1 9
+
+typedef struct EXTERN H235AuthenticationMechanism {
+ int t;
+ union {
+ /* t = 1 */
+ /* t = 2 */
+ /* t = 3 */
+ /* t = 4 */
+ /* t = 5 */
+ /* t = 6 */
+ /* t = 7 */
+ H235NonStandardParameter *nonStandard;
+ /* t = 8 */
+ H235AuthenticationBES *authenticationBES;
+ /* t = 9 */
+ } u;
+} H235AuthenticationMechanism;
+
+EXTERN int asn1PE_H235AuthenticationMechanism (OOCTXT* pctxt, H235AuthenticationMechanism* pvalue);
+
+EXTERN int asn1PD_H235AuthenticationMechanism (OOCTXT* pctxt, H235AuthenticationMechanism* pvalue);
+
+/**************************************************************/
+/* */
+/* DHset_halfkey */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235DHset_halfkey {
+ ASN1UINT numbits;
+ ASN1OCTET data[256];
+} H235DHset_halfkey;
+
+EXTERN int asn1PE_H235DHset_halfkey (OOCTXT* pctxt, H235DHset_halfkey* pvalue);
+
+EXTERN int asn1PD_H235DHset_halfkey (OOCTXT* pctxt, H235DHset_halfkey* pvalue);
+
+/**************************************************************/
+/* */
+/* DHset_modSize */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235DHset_modSize {
+ ASN1UINT numbits;
+ ASN1OCTET data[256];
+} H235DHset_modSize;
+
+EXTERN int asn1PE_H235DHset_modSize (OOCTXT* pctxt, H235DHset_modSize* pvalue);
+
+EXTERN int asn1PD_H235DHset_modSize (OOCTXT* pctxt, H235DHset_modSize* pvalue);
+
+/**************************************************************/
+/* */
+/* DHset_generator */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235DHset_generator {
+ ASN1UINT numbits;
+ ASN1OCTET data[256];
+} H235DHset_generator;
+
+EXTERN int asn1PE_H235DHset_generator (OOCTXT* pctxt, H235DHset_generator* pvalue);
+
+EXTERN int asn1PD_H235DHset_generator (OOCTXT* pctxt, H235DHset_generator* pvalue);
+
+/**************************************************************/
+/* */
+/* DHset */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235DHset {
+ H235DHset_halfkey halfkey;
+ H235DHset_modSize modSize;
+ H235DHset_generator generator;
+} H235DHset;
+
+EXTERN int asn1PE_H235DHset (OOCTXT* pctxt, H235DHset* pvalue);
+
+EXTERN int asn1PD_H235DHset (OOCTXT* pctxt, H235DHset* pvalue);
+
+/**************************************************************/
+/* */
+/* TypedCertificate */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235TypedCertificate {
+ ASN1OBJID type;
+ ASN1DynOctStr certificate;
+} H235TypedCertificate;
+
+EXTERN int asn1PE_H235TypedCertificate (OOCTXT* pctxt, H235TypedCertificate* pvalue);
+
+EXTERN int asn1PD_H235TypedCertificate (OOCTXT* pctxt, H235TypedCertificate* pvalue);
+
+/**************************************************************/
+/* */
+/* Identifier */
+/* */
+/**************************************************************/
+
+typedef ASN1BMPString H235Identifier;
+
+EXTERN int asn1PE_H235Identifier (OOCTXT* pctxt, H235Identifier value);
+
+EXTERN int asn1PD_H235Identifier (OOCTXT* pctxt, H235Identifier* pvalue);
+
+/**************************************************************/
+/* */
+/* ECpoint_x */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECpoint_x {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECpoint_x;
+
+EXTERN int asn1PE_H235ECpoint_x (OOCTXT* pctxt, H235ECpoint_x* pvalue);
+
+EXTERN int asn1PD_H235ECpoint_x (OOCTXT* pctxt, H235ECpoint_x* pvalue);
+
+/**************************************************************/
+/* */
+/* ECpoint_y */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECpoint_y {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECpoint_y;
+
+EXTERN int asn1PE_H235ECpoint_y (OOCTXT* pctxt, H235ECpoint_y* pvalue);
+
+EXTERN int asn1PD_H235ECpoint_y (OOCTXT* pctxt, H235ECpoint_y* pvalue);
+
+/**************************************************************/
+/* */
+/* ECpoint */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECpoint {
+ struct {
+ unsigned xPresent : 1;
+ unsigned yPresent : 1;
+ } m;
+ H235ECpoint_x x;
+ H235ECpoint_y y;
+} H235ECpoint;
+
+EXTERN int asn1PE_H235ECpoint (OOCTXT* pctxt, H235ECpoint* pvalue);
+
+EXTERN int asn1PD_H235ECpoint (OOCTXT* pctxt, H235ECpoint* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdhp_modulus */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdhp_modulus {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdhp_modulus;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdhp_modulus (OOCTXT* pctxt, H235ECKASDH_eckasdhp_modulus* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdhp_modulus (OOCTXT* pctxt, H235ECKASDH_eckasdhp_modulus* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdhp_weierstrassA */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdhp_weierstrassA {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdhp_weierstrassA;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdhp_weierstrassA (OOCTXT* pctxt, H235ECKASDH_eckasdhp_weierstrassA* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdhp_weierstrassA (OOCTXT* pctxt, H235ECKASDH_eckasdhp_weierstrassA* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdhp_weierstrassB */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdhp_weierstrassB {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdhp_weierstrassB;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdhp_weierstrassB (OOCTXT* pctxt, H235ECKASDH_eckasdhp_weierstrassB* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdhp_weierstrassB (OOCTXT* pctxt, H235ECKASDH_eckasdhp_weierstrassB* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdhp */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdhp {
+ H235ECpoint public_key;
+ H235ECKASDH_eckasdhp_modulus modulus;
+ H235ECpoint base;
+ H235ECKASDH_eckasdhp_weierstrassA weierstrassA;
+ H235ECKASDH_eckasdhp_weierstrassB weierstrassB;
+} H235ECKASDH_eckasdhp;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdhp (OOCTXT* pctxt, H235ECKASDH_eckasdhp* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdhp (OOCTXT* pctxt, H235ECKASDH_eckasdhp* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdh2_fieldSize */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdh2_fieldSize {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdh2_fieldSize;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdh2_fieldSize (OOCTXT* pctxt, H235ECKASDH_eckasdh2_fieldSize* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdh2_fieldSize (OOCTXT* pctxt, H235ECKASDH_eckasdh2_fieldSize* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdh2_weierstrassA */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdh2_weierstrassA {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdh2_weierstrassA;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdh2_weierstrassA (OOCTXT* pctxt, H235ECKASDH_eckasdh2_weierstrassA* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdh2_weierstrassA (OOCTXT* pctxt, H235ECKASDH_eckasdh2_weierstrassA* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdh2_weierstrassB */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdh2_weierstrassB {
+ ASN1UINT numbits;
+ ASN1OCTET data[64];
+} H235ECKASDH_eckasdh2_weierstrassB;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdh2_weierstrassB (OOCTXT* pctxt, H235ECKASDH_eckasdh2_weierstrassB* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdh2_weierstrassB (OOCTXT* pctxt, H235ECKASDH_eckasdh2_weierstrassB* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH_eckasdh2 */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ECKASDH_eckasdh2 {
+ H235ECpoint public_key;
+ H235ECKASDH_eckasdh2_fieldSize fieldSize;
+ H235ECpoint base;
+ H235ECKASDH_eckasdh2_weierstrassA weierstrassA;
+ H235ECKASDH_eckasdh2_weierstrassB weierstrassB;
+} H235ECKASDH_eckasdh2;
+
+EXTERN int asn1PE_H235ECKASDH_eckasdh2 (OOCTXT* pctxt, H235ECKASDH_eckasdh2* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH_eckasdh2 (OOCTXT* pctxt, H235ECKASDH_eckasdh2* pvalue);
+
+/**************************************************************/
+/* */
+/* ECKASDH */
+/* */
+/**************************************************************/
+
+/* Choice tag constants */
+
+#define T_H235ECKASDH_eckasdhp 1
+#define T_H235ECKASDH_eckasdh2 2
+#define T_H235ECKASDH_extElem1 3
+
+typedef struct EXTERN H235ECKASDH {
+ int t;
+ union {
+ /* t = 1 */
+ H235ECKASDH_eckasdhp *eckasdhp;
+ /* t = 2 */
+ H235ECKASDH_eckasdh2 *eckasdh2;
+ /* t = 3 */
+ } u;
+} H235ECKASDH;
+
+EXTERN int asn1PE_H235ECKASDH (OOCTXT* pctxt, H235ECKASDH* pvalue);
+
+EXTERN int asn1PD_H235ECKASDH (OOCTXT* pctxt, H235ECKASDH* pvalue);
+
+/**************************************************************/
+/* */
+/* ClearToken */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ClearToken {
+ struct {
+ unsigned timeStampPresent : 1;
+ unsigned passwordPresent : 1;
+ unsigned dhkeyPresent : 1;
+ unsigned challengePresent : 1;
+ unsigned randomPresent : 1;
+ unsigned certificatePresent : 1;
+ unsigned generalIDPresent : 1;
+ unsigned nonStandardPresent : 1;
+ unsigned eckasdhkeyPresent : 1;
+ unsigned sendersIDPresent : 1;
+ } m;
+ ASN1OBJID tokenOID;
+ H235TimeStamp timeStamp;
+ H235Password password;
+ H235DHset dhkey;
+ H235ChallengeString challenge;
+ H235RandomVal random;
+ H235TypedCertificate certificate;
+ H235Identifier generalID;
+ H235NonStandardParameter nonStandard;
+ H235ECKASDH eckasdhkey;
+ H235Identifier sendersID;
+} H235ClearToken;
+
+EXTERN int asn1PE_H235ClearToken (OOCTXT* pctxt, H235ClearToken* pvalue);
+
+EXTERN int asn1PD_H235ClearToken (OOCTXT* pctxt, H235ClearToken* pvalue);
+
+/**************************************************************/
+/* */
+/* IV8 */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235IV8 {
+ ASN1UINT numocts;
+ ASN1OCTET data[8];
+} H235IV8;
+
+EXTERN int asn1PE_H235IV8 (OOCTXT* pctxt, H235IV8* pvalue);
+
+EXTERN int asn1PD_H235IV8 (OOCTXT* pctxt, H235IV8* pvalue);
+
+/**************************************************************/
+/* */
+/* IV16 */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235IV16 {
+ ASN1UINT numocts;
+ ASN1OCTET data[16];
+} H235IV16;
+
+EXTERN int asn1PE_H235IV16 (OOCTXT* pctxt, H235IV16* pvalue);
+
+EXTERN int asn1PD_H235IV16 (OOCTXT* pctxt, H235IV16* pvalue);
+
+/**************************************************************/
+/* */
+/* Params */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235Params {
+ struct {
+ unsigned ranIntPresent : 1;
+ unsigned iv8Present : 1;
+ unsigned iv16Present : 1;
+ } m;
+ ASN1INT ranInt;
+ H235IV8 iv8;
+ H235IV16 iv16;
+} H235Params;
+
+EXTERN int asn1PE_H235Params (OOCTXT* pctxt, H235Params* pvalue);
+
+EXTERN int asn1PD_H235Params (OOCTXT* pctxt, H235Params* pvalue);
+
+/**************************************************************/
+/* */
+/* ENCRYPTED */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235ENCRYPTED {
+ ASN1OBJID algorithmOID;
+ H235Params paramS;
+ ASN1DynOctStr encryptedData;
+} H235ENCRYPTED;
+
+EXTERN int asn1PE_H235ENCRYPTED (OOCTXT* pctxt, H235ENCRYPTED* pvalue);
+
+EXTERN int asn1PD_H235ENCRYPTED (OOCTXT* pctxt, H235ENCRYPTED* pvalue);
+
+/**************************************************************/
+/* */
+/* CryptoToken_cryptoEncryptedToken */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235CryptoToken_cryptoEncryptedToken {
+ ASN1OBJID tokenOID;
+ H235ENCRYPTED token;
+} H235CryptoToken_cryptoEncryptedToken;
+
+EXTERN int asn1PE_H235CryptoToken_cryptoEncryptedToken (OOCTXT* pctxt, H235CryptoToken_cryptoEncryptedToken* pvalue);
+
+EXTERN int asn1PD_H235CryptoToken_cryptoEncryptedToken (OOCTXT* pctxt, H235CryptoToken_cryptoEncryptedToken* pvalue);
+
+/**************************************************************/
+/* */
+/* EncodedGeneralToken */
+/* */
+/**************************************************************/
+
+typedef ASN1OpenType H235EncodedGeneralToken;
+
+EXTERN int asn1PE_H235EncodedGeneralToken (OOCTXT* pctxt, H235EncodedGeneralToken value);
+
+EXTERN int asn1PD_H235EncodedGeneralToken (OOCTXT* pctxt, H235EncodedGeneralToken* pvalue);
+
+/**************************************************************/
+/* */
+/* CryptoToken_cryptoSignedToken_token */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235CryptoToken_cryptoSignedToken_token {
+ H235EncodedGeneralToken toBeSigned;
+ ASN1OBJID algorithmOID;
+ H235Params paramS;
+ ASN1DynBitStr signature;
+} H235CryptoToken_cryptoSignedToken_token;
+
+EXTERN int asn1PE_H235CryptoToken_cryptoSignedToken_token (OOCTXT* pctxt, H235CryptoToken_cryptoSignedToken_token* pvalue);
+
+EXTERN int asn1PD_H235CryptoToken_cryptoSignedToken_token (OOCTXT* pctxt, H235CryptoToken_cryptoSignedToken_token* pvalue);
+
+/**************************************************************/
+/* */
+/* CryptoToken_cryptoSignedToken */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235CryptoToken_cryptoSignedToken {
+ ASN1OBJID tokenOID;
+ H235CryptoToken_cryptoSignedToken_token token;
+} H235CryptoToken_cryptoSignedToken;
+
+EXTERN int asn1PE_H235CryptoToken_cryptoSignedToken (OOCTXT* pctxt, H235CryptoToken_cryptoSignedToken* pvalue);
+
+EXTERN int asn1PD_H235CryptoToken_cryptoSignedToken (OOCTXT* pctxt, H235CryptoToken_cryptoSignedToken* pvalue);
+
+/**************************************************************/
+/* */
+/* HASHED */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235HASHED {
+ ASN1OBJID algorithmOID;
+ H235Params paramS;
+ ASN1DynBitStr hash;
+} H235HASHED;
+
+EXTERN int asn1PE_H235HASHED (OOCTXT* pctxt, H235HASHED* pvalue);
+
+EXTERN int asn1PD_H235HASHED (OOCTXT* pctxt, H235HASHED* pvalue);
+
+/**************************************************************/
+/* */
+/* CryptoToken_cryptoHashedToken */
+/* */
+/**************************************************************/
+
+typedef struct EXTERN H235CryptoToken_cryptoHashedToken {
+ ASN1OBJID tokenOID;
+ H235ClearToken hashedVals;
+ H235HASHED token;
+} H235CryptoToken_cryptoHashedToken;
+
+EXTERN int asn1PE_H235CryptoToken_cryptoHashedToken (OOCTXT* pctxt, H235CryptoToken_cryptoHashedToken* pvalue);
+
+EXTERN int asn1PD_H235CryptoToken_cryptoHashedToken (OOCTXT* pctxt, H235CryptoToken_cryptoHashedToken* pvalue);
+
+/**************************************************************/
+/* */
+/* CryptoToken */
+/* */
+/**************************************************************/
+
+/* Choice tag constants */
+
+#define T_H235CryptoToken_cryptoEncryptedToken 1
+#define T_H235CryptoToken_cryptoSignedToken 2
+#define T_H235CryptoToken_cryptoHashedToken 3
+#define T_H235CryptoToken_cryptoPwdEncr 4
+#define T_H235CryptoToken_extElem1 5
+
+typedef struct EXTERN H235CryptoToken {
+ int t;
+ union {
+ /* t = 1 */
+ H235CryptoToken_cryptoEncryptedToken *cryptoEncryptedToken;
+ /* t = 2 */
+ H235CryptoToken_cryptoSignedToken *cryptoSignedToken;
+ /* t = 3 */
+ H235CryptoToken_cryptoHashedToken *cryptoHashedToken;
+ /* t = 4 */
+ H235ENCRYPTED *cryptoPwdEncr;
+ /* t = 5 */
+ } u;
+} H235CryptoToken;
+
+EXTERN int asn1PE_H235CryptoToken (OOCTXT* pctxt, H235CryptoToken* pvalue);
+
+EXTERN int asn1PD_H235CryptoToken (OOCTXT* pctxt, H235CryptoToken* pvalue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/addons/ooh323c/src/h323/H235-SECURITY-MESSAGESDec.c b/addons/ooh323c/src/h323/H235-SECURITY-MESSAGESDec.c
new file mode 100644
index 000000000..897bf0eec
--- /dev/null
+++ b/addons/ooh323c/src/h323/H235-SECURITY-MESSAGESDec.c
@@ -0,0 +1,1782 @@
+/*
+ * Copyright (C) 2004-2005 by Objective Systems, Inc.
+ *
+ * This software is furnished under an open source license and may be
+ * used and copied only in accordance with the terms of this license.
+ * The text of the license may generally be found in the root
+ * directory of this installation in the COPYING file. It
+ * can also be viewed online at the following URL:
+ *
+ * http://www.obj-sys.com/open/license.html
+ *
+ * Any redistributions of this file including modified versions must
+ * maintain this copyright notice.
+ *
+ *****************************************************************************/
+/**
+ * This file was generated by the Objective Systems ASN1C Compiler
+ * (http://www.obj-sys.com). Version: 5.72, Date: 22-Mar-2005.
+ */
+#include "ooasn1.h"
+#include "H235-SECURITY-MESSAGES.h"
+#include "eventHandler.h"
+
+/**************************************************************/
+/* */
+/* ChallengeString */
+/* */
+/**************************************************************/
+
+EXTERN int asn1PD_H235ChallengeString (OOCTXT* pctxt, H235ChallengeString* pvalue)
+{
+ static Asn1SizeCnst lsize1 = { 0, 8, 128, 0 };
+ int stat = ASN_OK;
+
+ addSizeConstraint (pctxt, &lsize1);
+
+ stat = decodeOctetString (pctxt,
+ &pvalue->numocts,
+ pvalue->data,
+ sizeof(pvalue->data));
+ if (stat != ASN_OK) return stat;
+ invokeOctStrValue (pctxt, pvalue->numocts, pvalue->data);
+
+ return (stat);
+}
+
+/**************************************************************/
+/* */
+/* TimeStamp */
+/* */
+/**************************************************************/
+
+EXTERN int asn1PD_H235TimeStamp (OOCTXT* pctxt, H235TimeStamp* pvalue)
+{
+ int stat = ASN_OK;
+
+ stat = decodeConsUnsigned (pctxt, pvalue, 1U, ASN1UINT_MAX);
+ if (stat != ASN_OK) return stat;
+ invokeUIntValue (pctxt, *pvalue);
+
+ return (stat);
+}
+
+/**************************************************************/
+/* */
+/* RandomVal */
+/* */
+/**************************************************************/
+
+EXTERN int asn1PD_H235RandomVal (OOCTXT* pctxt, H235RandomVal* pvalue)
+{
+ int stat = ASN_OK;
+
+ stat = decodeUnconsInteger (pctxt, pvalue);
+ if (stat != ASN_OK) return stat;
+ invokeIntValue (pctxt, *pvalue);
+
+ return (stat);
+}
+
+/**************************************************************/
+/* */
+/* Password */
+/* */
+/**************************************************************/
+
+EXTERN int asn1PD_H235Password (OOCTXT* pctxt, H235Password* pvalue)
+{
+ static Asn1SizeCnst lsize1 = { 0, 1, 128, 0 };
+ int stat = ASN_OK;
+
+ addSizeConstraint (pctxt, &lsize1);
+
+ stat = decodeBMPString (pctxt, pvalue, 0);
+ if (stat != ASN_OK) return stat;
+ invokeCharStr16BitValue (pctxt, pvalue->nchars, pvalue->data);
+
+ return (stat);
+}
+
+/**************************************************************/
+/* */
+/* EncodedPwdCertToken */
+/* */
+/**************************************************************/
+
+EXTERN int asn1PD_H235EncodedPwdCertToken (OOCTXT* pctxt, H235EncodedPwdCertToken* pvalue)
+{
+ int stat = ASN_OK;
+
+ stat = decodeOpenType (pctxt, &pvalue->data, &pvalue->numocts);
<