Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 212306 | Differences between
and this patch

Collapse All | Expand All

(-)asterisk.original/asterisk-1.2.26.2.ebuild (+479 lines)
Line 0 Link Here
1
# Copyright 1999-2008 Gentoo Foundation
2
# Distributed under the terms of the GNU General Public License v2
3
# $Header: /var/cvsroot/gentoo-x86/net-misc/asterisk/asterisk-1.2.21.1-r1.ebuild,v 1.5 2008/02/21 18:14:20 welp Exp $
4
5
inherit eutils multilib toolchain-funcs
6
7
IUSE="alsa bri curl debug doc gtk h323 hardened lowmem mmx \
8
	nosamples odbc osp postgres pri speex sqlite ssl zaptel \
9
	elibc_uclibc backports"
10
11
BRI_VERSION="0.3.0-PRE-1y-p"
12
AST_PATCHES="1.2.26.2-patches-1.0"
13
JB_PATCHES="1.2.11-jb"
14
15
MY_P="${P/_p/.}"
16
17
DESCRIPTION="Asterisk: A Modular Open Source PBX System"
18
HOMEPAGE="http://www.asterisk.org/"
19
SRC_URI="http://ftp.digium.com/pub/asterisk/releases/${MY_P}.tar.gz
20
	 http://www.netdomination.org/pub/asterisk/${PN}-${AST_PATCHES}.tar.bz2
21
	 mirror://gentoo/${PN}-${AST_PATCHES}.tar.bz2
22
	 bri? ( mirror://gentoo/${MY_P}-bristuff-${BRI_VERSION}.diff.bz2
23
		http://www.junghanns.net/downloads/bristuff-${BRI_VERSION}.tar.gz )"
24
25
S="${WORKDIR}/${MY_P}"
26
S_BRI="${WORKDIR}/bristuff-${BRI_VERSION}"
27
28
SLOT="0"
29
LICENSE="GPL-2"
30
KEYWORDS="~alpha amd64 ~hppa ~ppc sparc x86"
31
32
RDEPEND="dev-libs/newt
33
	media-sound/sox
34
	ssl? ( dev-libs/openssl )
35
	gtk? ( =x11-libs/gtk+-1.2* )
36
	pri? ( >=net-libs/libpri-1.2.5 )
37
	h323? ( >=dev-libs/pwlib-1.8.3
38
		>=net-libs/openh323-1.15.0 )
39
	alsa? ( media-libs/alsa-lib )
40
	curl? ( net-misc/curl )
41
	odbc? ( dev-db/unixODBC )
42
	backports? ( dev-db/unixODBC )
43
	speex? ( media-libs/speex )
44
	sqlite? ( <dev-db/sqlite-3.0.0 )
45
	zaptel? ( >=net-misc/zaptel-1.2.16 )
46
	postgres? ( dev-db/postgresql )
47
	osp? ( >=net-libs/osptoolkit-3.3.4 )
48
	bri? (  >=net-libs/libpri-1.2.5
49
		>=net-misc/zaptel-1.2.18 )"
50
51
DEPEND="${RDEPEND}
52
	sys-devel/flex
53
	sys-devel/bison
54
	doc? ( app-doc/doxygen )
55
	virtual/logger"
56
57
#asterisk uses special mpg123 functions and does not work with mpeg321, bug #42703
58
PDEPEND="|| ( media-sound/mpg123 net-misc/asterisk-addons )"
59
60
QA_TEXTRELS_x86="usr/lib/asterisk/modules/codec_gsm.so"
61
QA_EXECSTACK_x86="usr/lib/asterisk/modules/codec_gsm.so"
62
63
#
64
# List of modules to ignore during scan (because they have been removed in 1.2.x)
65
#
66
SCAN_IGNORE_MODS="
67
	app_qcall
68
	chan_modem
69
	chan_modem_i4l
70
	chan_modem_bestdata
71
	chan_modme_aopen"
72
73
#
74
# shortcuts
75
#
76
is_ast10update() {
77
	return $(has_version "=net-misc/asterisk-1.0*")
78
}
79
80
is_astupdate() {
81
	if ! is_ast10update; then
82
		return $(has_version "<net-misc/asterisk-${PV}")
83
	fi
84
	return 0
85
}
86
87
#
88
# Display a nice countdown...
89
#
90
countdown() {
91
	local n
92
93
	ebeep
94
95
	n=${1:-10}
96
	while [[ $n -gt 0 ]]; do
97
		echo -en "  Waiting $n second(s)...\r"
98
		sleep 1
99
		(( n-- ))
100
	done
101
}
102
103
#
104
# Scan for asterisk-1.0.x modules that will have to be updated
105
#
106
scan_modules() {
107
	local modules_list=""
108
	local n
109
110
	for x in $(ls -1 "${ROOT}"usr/$(get_libdir)/asterisk/modules/*.so); do
111
		echo -en "Scanning.... $(basename ${x})   \r"
112
113
		# skip blacklisted modules
114
		hasq $(basename ${x//.so}) ${SCAN_IGNORE_MODS} && continue
115
116
		if $(readelf -s "${x}" | grep -q "\(ast_load\|ast_destroy\)$"); then
117
			modules_list="${modules_list} $(basename ${x//.so})"
118
		fi
119
	done
120
121
	if [[ -n "${modules_list}" ]]; then
122
		echo  "   ========================================================"
123
		ewarn "Please update or unmerge the following modules:"
124
		echo
125
126
		n=0
127
		for x in ${modules_list}; do
128
			ewarn " -   ${x}"
129
			(( n++ ))
130
		done
131
132
		echo
133
		ewarn "Warning: $n outdated module(s) found!"
134
		ewarn "Warning: asterisk may not work if you don't update them!"
135
		echo  "   ========================================================"
136
		echo
137
		einfo "You can use the \"asterisk-updater\" script to update the modules"
138
		echo
139
		countdown
140
		echo
141
		return 1
142
	else
143
		einfo "No asterisk-1.0.x modules found!"
144
		return 0
145
	fi
146
}
147
148
pkg_setup() {
149
	local checkfailed=0 waitaftermsg=0
150
151
	if is_ast10update; then
152
		ewarn "                      Asterisk UPGRADE Warning"
153
		ewarn ""
154
		ewarn "- Please read ${ROOT}usr/share/doc/${PF}/UPGRADE.txt.gz after the installation!"
155
		ewarn ""
156
		ewarn "                      Asterisk UPGRADE Warning"
157
		echo
158
		waitaftermsg=1
159
	fi
160
161
	if use bri; then
162
		if ! built_with_use net-libs/libpri bri; then
163
			die "net-libs/libpri must be rebuilt ith USE=bri."
164
		fi
165
	elif use pri; then
166
		if built_with_use net-libs/libpri bri; then
167
			die "net-libs/libpri must be rebuilt without USE=bri."
168
		fi
169
	fi
170
171
	#
172
	# Regular checks
173
	#
174
	einfo "Running some pre-flight checks..."
175
	echo
176
177
}
178
179
src_unpack() {
180
	unpack ${A}
181
	cd "${S}"
182
183
	#
184
	# gentoo patchset
185
	#
186
	for x in $(grep -v "^#\| \+" "${WORKDIR}"/patches/patches.list); do
187
		epatch "${WORKDIR}"/patches/${x}
188
	done
189
190
	if use mmx; then
191
		if ! use hardened; then
192
			einfo "Enabling mmx optimization"
193
			sed -i -e "s:^#\(K6OPT[\t ]\+= -DK6OPT\):\1:" \
194
				Makefile
195
		else
196
			ewarn "Hardened use-flag is set, not enabling mmx optimization for codec_gsm!"
197
		fi
198
	fi
199
200
	if ! use debug; then
201
		einfo "Disabling debug support"
202
		sed -i -e "s:^\(DEBUG=\):#\1:" \
203
			Makefile
204
	fi
205
206
	if ! use ssl; then
207
		einfo "Disabling crypto support"
208
		sed -i -e 's:^#\(NOCRYPTO=yes\):\1:' \
209
			-e '/^LIBS+=-lssl/d' Makefile || die
210
	fi
211
212
	epatch "${FILESDIR}"/1.2.0/asterisk-1.2.21.1-h323-dumb-makefile.diff
213
214
	#
215
	# uclibc patch
216
	#
217
	if use elibc_uclibc; then
218
		einfo "Patching asterisk for uclibc..."
219
		epatch "${FILESDIR}"/1.0.0/${PN}-1.0.5-uclibc-dns.diff
220
		epatch "${FILESDIR}"/1.2.0/${PN}-1.2.1-uclibc-getloadavg.diff
221
	fi
222
223
	#
224
	# BRI patches
225
	#
226
	if use bri; then
227
		einfo "Patching asterisk w/ BRI stuff"
228
229
		epatch "${WORKDIR}"/${MY_P}-bristuff-${BRI_VERSION}.diff
230
		#epatch "${S_BRI}"/patches/asterisk.patch
231
	fi
232
233
	#
234
	# Disable AEL, security bug #171884
235
	# Re-enable at your own risk (no USE since it can be critical)
236
	#
237
	sed -i -e 's/pbx_ael.so//' pbx/Makefile || die
238
239
	#
240
	# Some useful backports
241
	#
242
	if use backports; then
243
		einfo "Adding func_odbc backport"
244
		# http://svncommunity.digium.com/view/func_odbc/1.2/
245
		cp "${FILESDIR}"/1.2.0/backports/func_odbc/*.c "${WORKDIR}"/${P}/funcs/
246
		cp "${FILESDIR}"/1.2.0/backports/func_odbc/*.h "${WORKDIR}"/${P}/include/
247
		cp "${FILESDIR}"/1.2.0/backports/func_odbc/*.sample "${WORKDIR}"/${P}/configs/
248
249
		einfo "Adding app_stack2 backport"
250
		# http://svncommunity.digium.com/view/app_stack/1.2/
251
		cp "${FILESDIR}"/1.2.0/backports/app_stack/*.c "${WORKDIR}"/${P}/apps/
252
		epatch "${FILESDIR}"/1.2.0/backports/app_stack/asterisk-app_stack2-makefile.diff
253
	fi
254
255
}
256
257
src_compile() {
258
	local myopts
259
260
	use lowmem && \
261
		myopts="-DLOW_MEMORY"
262
263
	if use h323; then
264
		einfo "Building H.323 wrapper lib..."
265
		make -C channels/h323 \
266
			NOTRACE=1 \
267
			PWLIBDIR=/usr/share/pwlib \
268
			OPENH323DIR=/usr/share/openh323 \
269
			libchanh323.a Makefile.ast || die "Make h323 failed"
270
	fi
271
272
	einfo "Building Asterisk..."
273
	make \
274
		CC=$(tc-getCC) \
275
		NOTRACE=1 \
276
		OPTIMIZE="${CFLAGS}" \
277
		PWLIBDIR=/usr/share/pwlib \
278
		OPENH323DIR=/usr/share/openh323 \
279
		OPTIONS="${myopts}" || die "Make failed"
280
281
	# create api docs
282
	use doc && \
283
		make progdocs
284
285
	# build bristuff's ISDNguard
286
	use bri && \
287
		make -C "${S_BRI}"/ISDNguard
288
}
289
290
src_install() {
291
292
	# install asterisk
293
	make DESTDIR="${D}" ASTLIBDIR="\$(INSTALL_PREFIX)/usr/$(get_libdir)/asterisk" install || die "Make install failed"
294
	make DESTDIR="${D}" ASTLIBDIR="\$(INSTALL_PREFIX)/usr/$(get_libdir)/asterisk" samples || die "Failed to create sample files"
295
296
	# remove bristuff capi
297
	use bri && \
298
		rm -f "${D}"usr/$(get_libdir)/asterisk/modules/{app,chan}_capi*.so 2>/dev/null
299
300
	# remove installed sample files if nosamples flag is set
301
	if use nosamples; then
302
		einfo "Skipping installation of sample files..."
303
		rm -rf "${D}"var/spool/asterisk/voicemail/default
304
		rm -f  "${D}"var/lib/asterisk/mohmp3/*
305
		rm -f  "${D}"var/lib/asterisk/sounds/demo-*
306
		rm -f  "${D}"var/lib/asterisk/agi-bin/*
307
	else
308
		einfo "Sample files have been installed"
309
		keepdir /var/spool/asterisk/voicemail/default/1234/INBOX
310
	fi
311
312
	# move sample configuration files to doc directory
313
	if is_ast10update; then
314
		elog "Updating from old (pre-1.2) asterisk version, new configuration files have been installed"
315
		elog "into ${ROOT}etc/asterisk, use etc-update or dispatch-conf to update them"
316
	elif has_version "net-misc/asterisk"; then
317
		elog "Configuration samples have been moved to: $ROOT/usr/share/doc/${PF}/conf"
318
		insinto /usr/share/doc/${PF}/conf
319
		doins "${D}"etc/asterisk/*.conf*
320
		rm -f "${D}"etc/asterisk/*.conf* 2>/dev/null
321
	fi
322
323
	# don't delete these directories, even if they are empty
324
	for x in voicemail meetme system dictate monitor tmp; do
325
		keepdir /var/spool/asterisk/${x}
326
	done
327
	keepdir /var/lib/asterisk/sounds/priv-callerintros
328
	keepdir /var/lib/asterisk/mohmp3
329
	keepdir /var/lib/asterisk/agi-bin
330
	keepdir /var/log/asterisk/cdr-csv
331
	keepdir /var/log/asterisk/cdr-custom
332
	keepdir /var/run/asterisk
333
334
	# install astxs
335
	dobin  contrib/scripts/astxs
336
337
	newinitd "${FILESDIR}"/1.2.0/asterisk.rc6 asterisk
338
	newconfd "${FILESDIR}"/1.2.0/asterisk.confd asterisk
339
340
	# install standard docs...
341
	dodoc BUGS CREDITS LICENSE ChangeLog HARDWARE README README.fpm
342
	dodoc SECURITY doc/CODING-GUIDELINES doc/linkedlists.README UPGRADE.txt
343
	dodoc doc/README.*
344
	dodoc doc/*.txt
345
346
	docinto scripts
347
	dodoc contrib/scripts/*
348
349
	docinto utils
350
	dodoc contrib/utils/*
351
352
	docinto configs
353
	dodoc configs/*
354
355
	# install api docs
356
	if use doc; then
357
		insinto /usr/share/doc/${PF}/api/html
358
		doins doc/api/html/*
359
	fi
360
361
	# install ISDNguard
362
	if use bri; then
363
		cd "${S_BRI}"/ISDNguard
364
		dosbin ISDNguard
365
366
		docinto ISDNguard
367
		dodoc INSTALL.ISDNguard
368
369
		cd "${S}"
370
	fi
371
372
	insinto /usr/share/doc/${PF}/cgi
373
	doins contrib/scripts/vmail.cgi
374
	doins images/*.gif
375
376
	# install asterisk-updater
377
	dosbin "${FILESDIR}"/1.2.0/asterisk-updater
378
379
	# install asterisk.h, a lot of external modules need this
380
	insinto /usr/include/asterisk
381
	doins   include/asterisk.h
382
383
	# make sure misdn/capi stuff is not installed, provided by asterisk-chan_..
384
	rm -f "${D}"/etc/asterisk/misdn.conf "${D}"/usr/lib/asterisk/modules/chan_misdn.so \
385
		"${D}"/usr/share/doc/${PF}/{conf/misdn.conf,configs/misdn.conf.sample.gz,README.misdn.gz}
386
	rm -f "${D}"/usr/include/asterisk/chan_capi{,_app}.h \
387
		"${D}"/usr/share/doc/${PF}/{conf/capi.conf,configs/capi.conf.sample.gz}
388
}
389
390
pkg_preinst() {
391
	enewgroup asterisk
392
	enewuser asterisk -1 -1 /var/lib/asterisk asterisk
393
}
394
395
pkg_postinst() {
396
	einfo "Fixing permissions"
397
	chown -R asterisk:asterisk "${ROOT}"var/log/asterisk
398
	chmod -R u=rwX,g=rX,o=     "${ROOT}"var/log/asterisk
399
400
	for x in lib run spool; do
401
		chown -R asterisk:asterisk "${ROOT}"var/${x}/asterisk
402
		chmod -R u=rwX,g=rwX,o=    "${ROOT}"var/${x}/asterisk
403
	done
404
405
	chown asterisk:asterisk "${ROOT}"etc/asterisk/
406
	chown asterisk:asterisk "${ROOT}"etc/asterisk/*.adsi
407
	chown asterisk:asterisk "${ROOT}"etc/asterisk/extensions.ael
408
	chmod u=rwX,g=rwX,o= "${ROOT}"etc/asterisk/
409
	chmod u=rwX,g=rwX,o= "${ROOT}"etc/asterisk/*.adsi
410
	chmod u=rwX,g=rwX,o= "${ROOT}"etc/asterisk/extensions.ael
411
	echo
412
413
	#
414
	# Announcements, warnings, reminders...
415
	#
416
	einfo "Asterisk has been installed"
417
	einfo ""
418
	elog "If you want to know more about asterisk, visit these sites:"
419
	elog "http://www.asteriskdocs.org/"
420
	elog "http://www.voip-info.org/wiki-Asterisk"
421
	elog
422
	elog "http://www.automated.it/guidetoasterisk.htm"
423
	elog
424
	elog "Gentoo VoIP IRC Channel:"
425
	elog "#gentoo-voip @ irc.freenode.net"
426
	elog
427
	elog "Please note that AEL is no longer built because of security bugs"
428
	elog "See http://bugs.gentoo.org/show_bug.cgi?id=171884"
429
	elog
430
	echo
431
	echo
432
433
	#
434
	# Warning about 1.0 -> 1.2 changes...
435
	#
436
	if is_ast10update; then
437
		ewarn ""
438
		ewarn "- Please read ${ROOT}usr/share/doc/${PF}/UPGRADE.txt.gz before continuing"
439
		ewarn ""
440
	fi
441
442
	if is_astupdate; then
443
		ewarn ""
444
		ewarn " - The initgroups patch has been dropped, please update your"
445
		ewarn "   \"conf.d/asterisk\" and \"init.d/asterisk\" file!"
446
		ewarn ""
447
	fi
448
449
	# scan for old modules
450
	if is_ast10update; then
451
		einfo "Asterisk has been updated from pre-1.2.x, scanning for old modules"
452
		scan_modules
453
	fi
454
}
455
456
pkg_config() {
457
	einfo "Do you want to reset file permissions and ownerships (y/N)?"
458
459
	read tmp
460
	tmp="$(echo $tmp | tr [:upper:] [:lower:])"
461
462
	if [[ "$tmp" = "y" ]] ||\
463
	   [[ "$tmp" = "yes" ]]
464
	then
465
		einfo "Resetting permissions to defaults..."
466
467
		for x in spool run lib log; do
468
			chown -R asterisk:asterisk "${ROOT}"var/${x}/asterisk
469
			chmod -R u=rwX,g=rX,o=     "${ROOT}"var/${x}/asterisk
470
		done
471
472
		chown -R root:asterisk "${ROOT}"etc/asterisk
473
		chmod -R u=rwX,g=rX,o= "${ROOT}"etc/asterisk
474
475
		einfo "done"
476
	else
477
		einfo "skipping"
478
	fi
479
}
(-)asterisk.original/files/1.2.0/backports/app_stack/app_stack2.c (+267 lines)
Line 0 Link Here
1
/*
2
 * Asterisk -- An open source telephony toolkit.
3
 *
4
 * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
5
 *
6
 * This code is released by the author with no restrictions on usage.
7
 *
8
 * See http://www.asterisk.org for more information about
9
 * the Asterisk project. Please do not directly contact
10
 * any of the maintainers of this project for assistance;
11
 * the project provides a web site, mailing lists and IRC
12
 * channels for your use.
13
 *
14
 * This program is free software, distributed under the terms of
15
 * the GNU General Public License Version 2. See the LICENSE file
16
 * at the top of the source tree.
17
 */
18
19
/*! \file
20
 *
21
 * \brief Stack applications Gosub, Return, etc.
22
 * 
23
 * \ingroup applications
24
 */
25
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <unistd.h>
29
#include <string.h>
30
31
#include "asterisk/options.h"
32
#include "asterisk/logger.h"
33
#include "asterisk/channel.h"
34
#include "asterisk/chanvars.h"
35
#include "asterisk/pbx.h"
36
#include "asterisk/module.h"
37
#include "asterisk/config.h"
38
#include "asterisk/app.h"
39
40
#define STACKVAR	"~GOSUB2~STACK~"
41
42
static const char *tdesc = "Stack Routines";
43
44
static const char *app_gosub = "Gosub2";
45
static const char *app_gosubif = "GosubIf2";
46
static const char *app_return = "Return2";
47
static const char *app_pop = "StackPop2";
48
49
static const char *gosub_synopsis = "Jump to label, saving return address";
50
static const char *gosubif_synopsis = "Jump to label, saving return address";
51
static const char *return_synopsis = "Return from gosub routine";
52
static const char *pop_synopsis = "Remove one address from gosub stack";
53
54
static const char *gosub_descrip =
55
"Gosub2([[context|]exten|]priority[(arg1[|...][|argN])])\n"
56
"  Jumps to the label specified, saving the return address.\n";
57
static const char *gosubif_descrip =
58
"GosubIf2(condition?labeliftrue[(arg1[|...])][:labeliffalse[(arg1[|...])]])\n"
59
"  If the condition is true, then jump to labeliftrue.  If false, jumps to\n"
60
"labeliffalse, if specified.  In either case, a jump saves the return point\n"
61
"in the dialplan, to be returned to with a Return.\n";
62
static const char *return_descrip =
63
"Return2([return-value])\n"
64
"  Jumps to the last label on the stack, removing it.  The return value, if\n"
65
"any, is saved in the channel variable GOSUB_RETVAL.\n";
66
static const char *pop_descrip =
67
"StackPop2()\n"
68
"  Removes last label on the stack, discarding it.\n";
69
70
STANDARD_LOCAL_USER;
71
72
LOCAL_USER_DECL;
73
74
static int pop_exec(struct ast_channel *chan, void *data)
75
{
76
	const char *frame = pbx_builtin_getvar_helper(chan, STACKVAR);
77
	int numargs = 0, i;
78
	char argname[15];
79
80
	/* Pop any arguments for this stack frame off the variable stack */
81
	if (frame) {
82
		numargs = atoi(frame);
83
		for (i = 1; i <= numargs; i++) {
84
			snprintf(argname, sizeof(argname), "ARG%d", i);
85
			pbx_builtin_setvar_helper(chan, argname, NULL);
86
		}
87
	}
88
89
	/* Remove the last frame from the Gosub stack */
90
	pbx_builtin_setvar_helper(chan, STACKVAR, NULL);
91
92
	return 0;
93
}
94
95
static int return_exec(struct ast_channel *chan, void *data)
96
{
97
	char *label = pbx_builtin_getvar_helper(chan, STACKVAR);
98
	char argname[15], *retval = data;
99
	int numargs, i;
100
101
	if (ast_strlen_zero(label)) {
102
		ast_log(LOG_ERROR, "Return2 without Gosub2: stack is empty\n");
103
		return -1;
104
	}
105
106
	/* Pop any arguments for this stack frame off the variable stack */
107
	numargs = atoi(label);
108
	for (i = 1; i <= numargs; i++) {
109
		snprintf(argname, sizeof(argname), "ARG%d", i);
110
		pbx_builtin_setvar_helper(chan, argname, NULL);
111
	}
112
113
	/* If the label exists, it will always have a ':' */
114
	label = strchr(label, ':') + 1;
115
116
	if (ast_parseable_goto(chan, label)) {
117
		ast_log(LOG_WARNING, "No next statement after Gosub2?\n");
118
		return -1;
119
	}
120
121
	/* Remove the current frame from the Gosub stack */
122
	pbx_builtin_setvar_helper(chan, STACKVAR, NULL);
123
124
	/* Set a return value, if any */
125
	pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", !ast_strlen_zero(retval) ? retval : "");
126
	return 0;
127
}
128
129
static int gosub_exec(struct ast_channel *chan, void *data)
130
{
131
	char newlabel[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 11 + 11 + 4];
132
	char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
133
	int i;
134
	struct localuser *u;
135
	AST_DECLARE_APP_ARGS(args2,
136
		AST_APP_ARG(argval)[100];
137
	);
138
139
	if (ast_strlen_zero(data)) {
140
		ast_log(LOG_ERROR, "Gosub2 requires an argument: Gosub2([[context|]exten|]priority[(arg1[|...][|argN])])\n");
141
		return -1;
142
	}
143
144
	LOCAL_USER_ADD(u);
145
146
	/* Separate the arguments from the label */
147
	/* NOTE:  you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
148
	label = strsep(&tmp, "(");
149
	if (tmp) {
150
		endparen = strrchr(tmp, ')');
151
		if (endparen)
152
			*endparen = '\0';
153
		else
154
			ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", (char *)data);
155
		AST_STANDARD_APP_ARGS(args2, tmp);
156
	} else
157
		args2.argc = 0;
158
159
	/* Create the return address, but don't save it until we know that the Gosub destination exists */
160
	snprintf(newlabel, sizeof(newlabel), "%d:%s|%s|%d", args2.argc == 2 ? args2.argc : 0, chan->context, chan->exten, chan->priority + 1);
161
162
	if (ast_parseable_goto(chan, data)) {
163
		ast_log(LOG_WARNING, "Destination '%s' does not exist in the dialplan\n", (char *)data);
164
		LOCAL_USER_REMOVE(u);
165
		return -1;
166
	}
167
168
	/* Now that we know for certain that we're going to a new location, set our arguments */
169
	for (i = 0; i < args2.argc; i++) {
170
		snprintf(argname, sizeof(argname), "ARG%d", i + 1);
171
		pbx_builtin_pushvar_helper(chan, argname, args2.argval[i]);
172
		ast_log(LOG_DEBUG, "Setting %s to '%s'\n", argname, args2.argval[i]);
173
	}
174
175
	/* And finally, save our return address */
176
	pbx_builtin_pushvar_helper(chan, STACKVAR, newlabel);
177
	ast_log(LOG_DEBUG, "Setting gosub return address to %s\n", newlabel);
178
179
	LOCAL_USER_REMOVE(u);
180
181
	return 0;
182
}
183
184
static int gosubif_exec(struct ast_channel *chan, void *data)
185
{
186
	struct localuser *u;
187
	char *args;
188
	int res=0;
189
	AST_DECLARE_APP_ARGS(cond,
190
		AST_APP_ARG(ition);
191
		AST_APP_ARG(labels);
192
	);
193
	AST_DECLARE_APP_ARGS(label,
194
		AST_APP_ARG(iftrue);
195
		AST_APP_ARG(iffalse);
196
	);
197
198
	if (ast_strlen_zero(data)) {
199
		ast_log(LOG_WARNING, "GosubIf2 requires an argument: GosubIf2(cond?label1(args):label2(args)\n");
200
		return 0;
201
	}
202
203
	LOCAL_USER_ADD(u);
204
205
	args = ast_strdupa((char *)data);
206
	/* AST_NONSTANDARD_APP_ARGS(cond, args, '?'); */
207
	cond.argc = ast_app_separate_args(args, '?', cond.argv, (sizeof(cond) - sizeof(cond.argc)) / sizeof(cond.argv[0]));
208
	if (cond.argc != 2) {
209
		ast_log(LOG_WARNING, "GosubIf2 requires an argument: GosubIf2(cond?label1(args):label2(args)\n");
210
		LOCAL_USER_REMOVE(u);
211
		return 0;
212
	}
213
214
	/* AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':'); */
215
	label.argc = ast_app_separate_args(cond.labels, ':', label.argv, (sizeof(label) - sizeof(label.argc)) / sizeof(label.argv[0]));
216
217
	if (pbx_checkcondition(cond.ition)) {
218
		if (!ast_strlen_zero(label.iftrue))
219
			res = gosub_exec(chan, label.iftrue);
220
	} else if (!ast_strlen_zero(label.iffalse)) {
221
		res = gosub_exec(chan, label.iffalse);
222
	}
223
224
	LOCAL_USER_REMOVE(u);
225
	return res;
226
}
227
228
int unload_module(void)
229
{
230
	ast_unregister_application(app_return);
231
	ast_unregister_application(app_pop);
232
	ast_unregister_application(app_gosubif);
233
	ast_unregister_application(app_gosub);
234
235
	STANDARD_HANGUP_LOCALUSERS;
236
237
	return 0;
238
}
239
240
int load_module(void)
241
{
242
	ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
243
	ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
244
	ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
245
	ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
246
247
	return 0;
248
}
249
250
char *description(void)
251
{
252
	return (char *) tdesc;
253
}
254
255
int usecount(void)
256
{
257
	int res;
258
259
	STANDARD_USECOUNT(res);
260
261
	return res;
262
}
263
264
char *key()
265
{
266
	return ASTERISK_GPL_KEY;
267
}
(-)asterisk.original/files/1.2.0/backports/func_odbc/func_array.c (+333 lines)
Line 0 Link Here
1
--- asterisk-1.2.26.2-original/apps/Makefile	2006-04-30 15:38:22.000000000 +0200
Line 0 Link Here
1
/*
2
 * Asterisk -- An open source telephony toolkit.
3
 *
4
 * Copyright (C) 2005, Tilghman Lesher.
5
 *
6
 * See http://www.asterisk.org for more information about
7
 * the Asterisk project. Please do not directly contact
8
 * any of the maintainers of this project for assistance;
9
 * the project provides a web site, mailing lists and IRC
10
 * channels for your use.
11
 *
12
 * This program is free software, distributed under the terms of
13
 * the GNU General Public License Version 2. See the LICENSE file
14
 * at the top of the source tree.
15
 */
16
17
/*! \file
18
 *
19
 * \brief ARRAY dialplan function
20
 *
21
 * \author Tilghman Lesher
22
 */
23
24
#include <stdlib.h>
25
#include <stdio.h>
26
#include <string.h>
27
#include <sys/types.h>
28
#include <sched.h>
29
30
#include "asterisk.h"
31
32
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 20003 $")
33
34
#include "asterisk/module.h"
35
#include "asterisk/channel.h"
36
#include "asterisk/pbx.h"
37
#include "asterisk/logger.h"
38
#include "asterisk/utils.h"
39
#include "asterisk/app.h"
40
#include "asterisk/localtime.h"
41
#include "asterisk/linkedlists.h"
42
43
#include "separate.h"
44
45
#define HASH_PREFIX	"~HASH~%s~"
46
#define HASH_FORMAT	HASH_PREFIX "%s~"
47
48
AST_MUTEX_DEFINE_STATIC(local_lock);
49
static int use_count = 0;
50
51
static char *app_clearhash = "ClearHash";
52
static char *syn_clearhash = "Clear the keys from a specified hashname";
53
static char *desc_clearhash =
54
"ClearHash(<hashname>)\n"
55
"  Clears all keys out of the specified hashname\n";
56
57
/* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
58
static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
59
{
60
	struct ast_var_t *newvar;
61
	int len = strlen(prefix);
62
	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, newvar, entries) {
63
		if (strncasecmp(prefix, ast_var_name(newvar), len) == 0) {
64
			AST_LIST_REMOVE_CURRENT(&chan->varshead, entries);
65
			free(newvar);
66
		}
67
	}
68
	AST_LIST_TRAVERSE_SAFE_END
69
}
70
71
static int exec_clearhash(struct ast_channel *chan, void *data)
72
{
73
	char prefix[80];
74
	snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
75
	clearvar_prefix(chan, prefix);
76
	return 0;
77
}
78
79
static void array(struct ast_channel *chan, char *cmd, char *var, const char *value)
80
{
81
	AST_DECLARE_APP_ARGS(arg1,
82
		AST_APP_ARG(var)[100];
83
	);
84
	AST_DECLARE_APP_ARGS(arg2,
85
		AST_APP_ARG(val)[100];
86
	);
87
	char *origvar = "", *value2, varname[256];
88
	int i, ishash = 0;
89
90
	value2 = ast_strdupa(value);
91
	if (!var || !value2)
92
		return;
93
94
	ast_mutex_lock(&local_lock);
95
	use_count++;
96
	ast_mutex_unlock(&local_lock);
97
98
	if (!strcmp(cmd, "HASH")) {
99
		const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
100
		origvar = var;
101
		if (var2)
102
			var = ast_strdupa(var2);
103
		else
104
			return;
105
		ishash = 1;
106
	}
107
108
	/* The functions this will generally be used with are SORT and ODBC_*, which
109
	 * both return comma-delimited lists.  However, if somebody uses literal lists,
110
	 * their commas will be translated to vertical bars by the load, and I don't
111
	 * want them to be surprised by the result.  Hence, we prefer commas as the
112
	 * delimiter, but we'll fall back to vertical bars if commas aren't found.
113
	 */
114
	ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
115
	if (strchr(var, ','))
116
		TRUNK_NONSTANDARD_APP_ARGS(arg1, var, ',');
117
	else
118
		TRUNK_STANDARD_APP_ARGS(arg1, var);
119
120
	if (strchr(value2, ','))
121
		TRUNK_NONSTANDARD_APP_ARGS(arg2, value2, ',');
122
	else
123
		TRUNK_STANDARD_APP_ARGS(arg2, value2);
124
125
	for (i = 0; i < arg1.argc; i++) {
126
		ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i],
127
			arg2.val[i]);
128
		if (i < arg2.argc) {
129
			if (ishash) {
130
				snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
131
				pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
132
			} else {
133
				pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
134
			}
135
		} else {
136
			/* We could unset the variable, by passing a NULL, but due to
137
			 * pushvar semantics, that could create some undesired behavior. */
138
			if (ishash) {
139
				snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
140
				pbx_builtin_setvar_helper(chan, varname, "");
141
			} else {
142
				pbx_builtin_setvar_helper(chan, arg1.var[i], "");
143
			}
144
		}
145
	}
146
147
	ast_mutex_lock(&local_lock);
148
	use_count--;
149
	ast_mutex_unlock(&local_lock);
150
151
	return;
152
}
153
154
static char *hashkeys_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
155
{
156
	struct ast_var_t *newvar;
157
	int plen;
158
	char prefix[80];
159
	snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
160
	plen = strlen(prefix);
161
162
	memset(buf, 0, len);
163
	AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
164
		if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
165
			/* Copy everything after the prefix */
166
			strncat(buf, ast_var_name(newvar) + plen, len);
167
			/* Trim the trailing ~ */
168
			buf[strlen(buf) - 1] = ',';
169
		}
170
	}
171
	/* Trim the trailing comma */
172
	buf[strlen(buf) - 1] = '\0';
173
	return buf;
174
}
175
176
static void hash_write(struct ast_channel *chan, char *cmd, char *var, const char *value)
177
{
178
	char varname[256];
179
	AST_DECLARE_APP_ARGS(arg,
180
		AST_APP_ARG(hashname);
181
		AST_APP_ARG(hashkey);
182
	);
183
184
	ast_mutex_lock(&local_lock);
185
	use_count++;
186
	ast_mutex_unlock(&local_lock);
187
188
	if (!strchr(var, '|')) {
189
		/* Single argument version */
190
		array(chan, "HASH", var, value);
191
		return;
192
	}
193
194
	TRUNK_STANDARD_APP_ARGS(arg, var);
195
	snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
196
	pbx_builtin_setvar_helper(chan, varname, value);
197
198
	ast_mutex_lock(&local_lock);
199
	use_count--;
200
	ast_mutex_unlock(&local_lock);
201
}
202
203
static char *hash_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
204
{
205
	char varname[256];
206
	const char *varvalue;
207
	AST_DECLARE_APP_ARGS(arg,
208
		AST_APP_ARG(hashname);
209
		AST_APP_ARG(hashkey);
210
	);
211
212
	ast_mutex_lock(&local_lock);
213
	use_count++;
214
	ast_mutex_unlock(&local_lock);
215
216
	TRUNK_STANDARD_APP_ARGS(arg, data);
217
	if (arg.argc == 2) {
218
		snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
219
		varvalue = pbx_builtin_getvar_helper(chan, varname);
220
		if (varvalue)
221
			ast_copy_string(buf, varvalue, len);
222
		else
223
			*buf = '\0';
224
	} else if (arg.argc == 1) {
225
		char colnames[4096];
226
		int i;
227
		AST_DECLARE_APP_ARGS(arg2,
228
			AST_APP_ARG(col)[100];
229
		);
230
231
		/* Get column names, in no particular order */
232
		hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
233
		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
234
235
		TRUNK_NONSTANDARD_APP_ARGS(arg2, colnames, ',');
236
		*buf = '\0';
237
238
		/* Now get the corresponding column values, in exactly the same order */
239
		for (i = 0; i < arg2.argc; i++) {
240
			snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
241
			varvalue = pbx_builtin_getvar_helper(chan, varname);
242
			strncat(buf, varvalue, len);
243
			strncat(buf, ",", len);
244
		}
245
246
		/* Strip trailing comma */
247
		buf[strlen(buf) - 1] = '\0';
248
	}
249
250
	return buf;
251
}
252
253
static struct ast_custom_function array_function = {
254
	.name = "ARRAY",
255
	.synopsis = "Allows setting multiple variables at once",
256
	.syntax = "ARRAY(var1[|var2[...][|varN]])",
257
	.write = array,
258
	.desc =
259
		"The comma-separated list passed as a value to which the function is set will\n"
260
		"be interpreted as a set of values to which the comma-separated list of\n"
261
		"variable names in the argument should be set.\n"
262
		"Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2\n"
263
		"Note: remember to either backslash your commas in extensions.conf or quote the\n"
264
		"entire argument, since Set can take multiple arguments itself.\n",
265
};
266
267
static struct ast_custom_function hash_function = {
268
	.name = "HASH",
269
	.synopsis = "Implementation of a dialplan associative array",
270
	.syntax = "HASH(hashname[|hashkey])",
271
	.write = hash_write,
272
	.read = hash_read,
273
	.desc =
274
		"In two argument mode, gets and sets values to corresponding keys within a named\n"
275
		"associative array.  The single-argument mode will only work when assigned to from\n"
276
		"a function defined by func_odbc.so.\n",
277
};
278
279
static struct ast_custom_function hashkeys_function = {
280
	.name = "HASHKEYS",
281
	.synopsis = "Retrieve the keys of a HASH()",
282
	.syntax = "HASHKEYS(<hashname>)",
283
	.read = hashkeys_read,
284
	.desc =
285
		"Returns a comma-delimited list of the current keys of an associative array\n"
286
	   	"defined by the HASH() function.  Note that if you iterate over the keys of\n"
287
		"the result, adding keys during iteration will cause the result of the HASHKEYS\n"
288
		"function to change.\n",
289
};
290
291
static char *tdesc = "String handling dialplan functions";
292
293
int unload_module(void)
294
{
295
	int res = 0;
296
297
	res |= ast_custom_function_unregister(&array_function);
298
	res |= ast_custom_function_unregister(&hash_function);
299
	res |= ast_custom_function_unregister(&hashkeys_function);
300
	res |= ast_unregister_application(app_clearhash);
301
	sched_yield(); /* Any remaining process gets time to clear out.  Increases safety if a force unload is attempted. */
302
303
	return res;
304
}
305
306
int load_module(void)
307
{
308
	int res = 0;
309
310
	res |= ast_custom_function_register(&array_function);
311
	res |= ast_custom_function_register(&hash_function);
312
	res |= ast_custom_function_register(&hashkeys_function);
313
	res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
314
315
	return res;
316
}
317
318
char *description(void)
319
{
320
	return tdesc;
321
}
322
323
int usecount(void)
324
{
325
	return use_count;
326
}
327
328
char *key(void)
329
{
330
	return ASTERISK_GPL_KEY;
331
}
332
(-)asterisk.original/files/1.2.0/backports/func_odbc/func_odbc.c (+800 lines)
Line 0 Link Here
1
/*
2
 * Asterisk -- An open source telephony toolkit.
3
 *
4
 * Copyright (c) 2005 Tilghman Lesher
5
 *
6
 * Tilghman Lesher <func_odbc__200604@the-tilghman.com>
7
 *
8
 * See http://www.asterisk.org for more information about
9
 * the Asterisk project. Please do not directly contact
10
 * any of the maintainers of this project for assistance;
11
 * the project provides a web site, mailing lists and IRC
12
 * channels for your use.
13
 *
14
 * This program is free software, distributed under the terms of
15
 * the GNU General Public License Version 2. See the LICENSE file
16
 * at the top of the source tree.
17
 */
18
19
/*!
20
 * \file
21
 *
22
 * \brief ODBC lookups
23
 *
24
 * \author Tilghman Lesher <func_odbc__200604@the-tilghman.com>
25
 */
26
27
#include <sys/types.h>
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <unistd.h>
31
#include <string.h>
32
#include <errno.h>
33
34
#include "asterisk.h"
35
36
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38
#include "asterisk/module.h"
39
#include "asterisk/file.h"
40
#include "asterisk/logger.h"
41
#include "asterisk/options.h"
42
#include "asterisk/channel.h"
43
#include "asterisk/pbx.h"
44
#include "asterisk/module.h"
45
#include "asterisk/config.h"
46
#include "asterisk/res_odbc.h"
47
#include "asterisk/app.h"
48
49
#include "separate.h"
50
51
static char *tdesc = "ODBC lookups";
52
53
static char *config = "func_odbc.conf";
54
55
enum {
56
	OPT_ESCAPECOMMAS =	(1 << 0),
57
} odbc_option_flags;
58
59
struct acf_odbc_query {
60
	AST_LIST_ENTRY(acf_odbc_query) list;
61
	char readhandle[5][30];
62
	char writehandle[5][30];
63
	char sql_read[2048];
64
	char sql_write[2048];
65
	unsigned int flags;
66
	struct ast_custom_function *acf;
67
};
68
69
AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
70
71
#ifdef NEEDTRACE
72
static void acf_odbc_error(SQLHSTMT stmt, int res)
73
{
74
	char state[10] = "", diagnostic[256] = "";
75
	SQLINTEGER nativeerror = 0;
76
	SQLSMALLINT diagbytes = 0;
77
	SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
78
	ast_log(LOG_WARNING, "SQL return value %d: error %s: %s (len %d)\n", res, state, diagnostic, diagbytes);
79
}
80
#endif
81
82
/*
83
 * Master control routine
84
 */
85
#define INCREMENT_DSN	if (++dsn < 5 && !ast_strlen_zero(query->writehandle[dsn])) goto retry_write
86
static void acf_odbc_write(struct ast_channel *chan, char *cmd, char *data, const char *value)
87
{
88
	odbc_obj *obj;
89
	struct acf_odbc_query *query;
90
	char *s, *t, buf[2048]="", varname[15];
91
	int res, i, dsn = 0, alloc, prepare, execute;
92
	SQLHSTMT stmt;
93
	SQLINTEGER nativeerror=0, numfields=0, rows=0;
94
	SQLSMALLINT diagbytes=0;
95
	unsigned char state[10], diagnostic[256];
96
	AST_DECLARE_APP_ARGS(args,
97
				AST_APP_ARG(arg)[100];
98
	);
99
	AST_DECLARE_APP_ARGS(values,
100
				AST_APP_ARG(val)[100];
101
	);
102
#ifdef NEEDTRACE
103
	SQLINTEGER enable = 1;
104
	char *tracefile = "/tmp/odbc.trace";
105
#endif
106
107
	AST_LIST_LOCK(&queries);
108
	AST_LIST_TRAVERSE(&queries, query, list) {
109
		if (!strcmp(query->acf->name, cmd)) {
110
			break;
111
		}
112
	}
113
114
	if (!query) {
115
		ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
116
		AST_LIST_UNLOCK(&queries);
117
		return;
118
	}
119
120
	/* Parse our arguments */
121
	s = ast_strdupa(data);
122
	if (value) {
123
		t = ast_strdupa(value);
124
	} else {
125
		t = "";
126
	}
127
128
	if (!s || !t) {
129
		ast_log(LOG_ERROR, "Out of memory\n");
130
		AST_LIST_UNLOCK(&queries);
131
		return;
132
	}
133
134
	TRUNK_STANDARD_APP_ARGS(args, s);
135
	for (i = 0; i < args.argc; i++) {
136
		snprintf(varname, sizeof(varname), "ARG%d", i + 1);
137
		pbx_builtin_pushvar_helper(chan, varname, args.arg[i]);
138
	}
139
140
	TRUNK_NONSTANDARD_APP_ARGS(values, t, ',');
141
	for (i = 0; i < values.argc; i++) {
142
		snprintf(varname, sizeof(varname), "VAL%d", i + 1);
143
		pbx_builtin_pushvar_helper(chan, varname, values.val[i]);
144
	}
145
146
	/* Additionally set the value as a whole */
147
	/* Note that pbx_builtin_setvar_helper will quite happily take a NULL for the 3rd argument */
148
	pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
149
150
	pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
151
152
	/* Restore prior values */
153
	for (i = 0; i < args.argc; i++) {
154
		snprintf(varname, sizeof(varname), "ARG%d", i + 1);
155
		pbx_builtin_setvar_helper(chan, varname, NULL);
156
	}
157
158
	for (i = 0; i < values.argc; i++) {
159
		snprintf(varname, sizeof(varname), "VAL%d", i + 1);
160
		pbx_builtin_setvar_helper(chan, varname, NULL);
161
	}
162
	pbx_builtin_setvar_helper(chan, "VALUE", NULL);
163
164
	AST_LIST_UNLOCK(&queries);
165
166
retry_write:
167
	obj = fetch_odbc_obj(query->writehandle[dsn], 0);
168
169
	if (!obj) {
170
		INCREMENT_DSN;
171
		ast_log(LOG_ERROR, "Unable to load ODBC write class (check res_odbc.conf)\n");
172
		return;
173
	}
174
175
#ifdef NEEDTRACE
176
	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
177
	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
178
#endif
179
180
	for (execute = 0; execute < 2; execute++) {
181
		for (prepare = 0; prepare < 2; prepare++) {
182
			for (alloc = 0; alloc < 2; alloc++) {
183
				res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
184
				if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO))
185
					break;
186
				else if (alloc == 0)
187
					odbc_sanity_check(obj);
188
				else {
189
					INCREMENT_DSN;
190
					ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
191
					pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
192
					return;
193
				}
194
			}
195
196
			res = SQLPrepare(stmt, (unsigned char *)buf, SQL_NTS);
197
			if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO))
198
				break;
199
			else if (prepare == 0)
200
				odbc_sanity_check(obj);
201
			else {
202
				INCREMENT_DSN;
203
				ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", buf);
204
				SQLCloseCursor(stmt);
205
				SQLFreeHandle (SQL_HANDLE_STMT, stmt);
206
				pbx_builtin_setvar_helper(chan, "ODBCROWS", "-1");
207
				return;
208
			}
209
		}
210
211
		res = SQLExecute(stmt);
212
		if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
213
			/* Rows affected */
214
			SQLRowCount(stmt, &rows);
215
			break;
216
		} else if (execute == 0)
217
			odbc_sanity_check(obj);
218
		else {
219
			if (res == SQL_ERROR) {
220
				SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
221
				for (i = 0; i <= numfields; i++) {
222
					SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
223
					ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
224
					if (i > 10) {
225
						ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
226
						break;
227
					}
228
				}
229
			}
230
			INCREMENT_DSN;
231
232
			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
233
				ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", buf);
234
			}
235
			rows = -1;
236
		}
237
	}
238
239
	/* Output the affected rows, for all cases.  In the event of failure, we
240
	 * flag this as -1 rows.  Note that this is different from 0 affected rows
241
	 * which would be the case if we succeeded in our query, but the values did
242
	 * not change. */
243
	snprintf(varname, sizeof(varname), "%d", (int)rows);
244
	pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
245
246
	SQLCloseCursor(stmt);
247
	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
248
}
249
#undef INCREMENT_DSN
250
251
#define INCREMENT_DSN	if (++dsn < 5 && !ast_strlen_zero(query->readhandle[dsn])) goto restartread;
252
static char *acf_odbc_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
253
{
254
	odbc_obj *obj;
255
	struct acf_odbc_query *query;
256
	char *s, sql[2048] = "", varname[15], colnames[2048] = "";
257
	int res, x, buflen = 0, dsn = 0, escapecommas, alloc, prepare, execute;
258
	AST_DECLARE_APP_ARGS(args,
259
				AST_APP_ARG(arg)[100];
260
	);
261
	SQLHSTMT stmt;
262
	SQLSMALLINT colcount=0;
263
	SQLINTEGER indicator;
264
	SQLSMALLINT collength;
265
#ifdef NEEDTRACE
266
	SQLINTEGER enable = 1;
267
	char *tracefile = "/tmp/odbc.trace";
268
#endif
269
270
	AST_LIST_LOCK(&queries);
271
	AST_LIST_TRAVERSE(&queries, query, list) {
272
		if (!strcmp(query->acf->name, cmd)) {
273
			break;
274
		}
275
	}
276
277
	if (!query) {
278
		ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
279
		AST_LIST_UNLOCK(&queries);
280
		return "";
281
	}
282
283
	/* Parse our arguments */
284
	if (!(s = ast_strdupa(data))) {
285
		AST_LIST_UNLOCK(&queries);
286
		return "";
287
	}
288
289
	TRUNK_STANDARD_APP_ARGS(args, s);
290
	for (x = 0; x < args.argc; x++) {
291
		snprintf(varname, sizeof(varname), "ARG%d", x + 1);
292
		pbx_builtin_pushvar_helper(chan, varname, args.arg[x]);
293
	}
294
295
	pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
296
297
	/* Restore prior values */
298
	for (x = 0; x < args.argc; x++) {
299
		snprintf(varname, sizeof(varname), "ARG%d", x + 1);
300
		pbx_builtin_setvar_helper(chan, varname, NULL);
301
	}
302
303
	/* Save this flag, so we can release the lock */
304
	escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
305
306
	AST_LIST_UNLOCK(&queries);
307
308
restartread:
309
	obj = fetch_odbc_obj(query->readhandle[dsn], 0);
310
311
	if (!obj) {
312
		INCREMENT_DSN;
313
		ast_log(LOG_ERROR, "Unable to load ODBC read class (check res_odbc.conf)\n");
314
		return "";
315
	}
316
317
#ifdef NEEDTRACE
318
	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
319
	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
320
#endif
321
322
	for (execute = 0; execute < 2; execute++) {
323
		for (prepare = 0; prepare < 2; prepare++) {
324
			for (alloc = 0; alloc < 2; alloc++) {
325
				res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
326
				if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO))
327
					break;
328
				else if (alloc == 0)
329
					odbc_sanity_check(obj);
330
				else {
331
					INCREMENT_DSN;
332
					ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
333
					return "";
334
				}
335
			}
336
337
			res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
338
			if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO))
339
				break;
340
			else if (prepare == 0)
341
				odbc_sanity_check(obj);
342
			else {
343
				INCREMENT_DSN;
344
				SQLCloseCursor(stmt);
345
				SQLFreeHandle(SQL_HANDLE_STMT, stmt);
346
				ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
347
				return "";
348
			}
349
		}
350
351
		res = odbc_smart_execute(obj, stmt);
352
		if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO))
353
			break;
354
		else if (execute == 0)
355
			odbc_sanity_check(obj);
356
		else {
357
			INCREMENT_DSN;
358
			SQLCloseCursor(stmt);
359
			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
360
			ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
361
			return "";
362
		}
363
	}
364
365
	res = SQLNumResultCols(stmt, &colcount);
366
	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
367
		ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
368
		SQLCloseCursor(stmt);
369
		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
370
		return "";
371
	}
372
373
	memset(buf, 0, len);
374
375
	res = SQLFetch(stmt);
376
	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
377
		if (res == SQL_NO_DATA) {
378
			if (option_verbose > 3) {
379
				ast_verbose(VERBOSE_PREFIX_4 "Found no rows [%s]\n", sql);
380
			}
381
		} else if (option_verbose > 3) {
382
			ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
383
		}
384
		goto acf_out;
385
	}
386
387
	for (x = 0; x < colcount; x++) {
388
		int i, namelen;
389
		char coldata[256], colname[256];
390
391
		res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
392
		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
393
			snprintf(colname, sizeof(colname), "field%d", x);
394
		}
395
396
		if (!ast_strlen_zero(colnames))
397
			strncat(colnames, ",", sizeof(colnames) - 1);
398
		namelen = strlen(colnames);
399
400
		/* Copy data, encoding '\' and ',' for the argument parser */
401
		for (i = 0; i < sizeof(colname); i++) {
402
			if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
403
				colnames[namelen++] = '\\';
404
			}
405
			colnames[namelen++] = colname[i];
406
407
			if (namelen >= sizeof(colnames) - 2) {
408
				colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0';
409
				break;
410
			}
411
412
			if (colname[i] == '\0')
413
				break;
414
		}
415
416
		buflen = strlen(buf);
417
		res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
418
		if (indicator == SQL_NULL_DATA) {
419
			coldata[0] = '\0';
420
			res = SQL_SUCCESS;
421
		}
422
423
		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
424
			ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
425
			SQLCloseCursor(stmt);
426
			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
427
			return "";
428
		}
429
430
		/* Copy data, encoding '\' and ',' for the argument parser */
431
		for (i = 0; i < sizeof(coldata); i++) {
432
			if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
433
				buf[buflen++] = '\\';
434
			}
435
			buf[buflen++] = coldata[i];
436
437
			if (buflen >= len - 2) {
438
				buf[buflen >= len ? len - 1 : buflen] = '\0';
439
				break;
440
			}
441
442
			if (coldata[i] == '\0')
443
				break;
444
		}
445
446
		buf[buflen - 1] = ',';
447
	}
448
	/* Trim trailing comma */
449
	buf[buflen - 1] = '\0';
450
451
	pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
452
453
acf_out:
454
	SQLCloseCursor(stmt);
455
	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
456
	return buf;
457
}
458
#undef INCREMENT_DSN
459
460
static char *acf_escape(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
461
{
462
	char *in, *out = buf;
463
	for (in = data; *in && out - buf < len; in++) {
464
		if (*in == '\'') {
465
			*out = '\'';
466
			out++;
467
		}
468
		*out = *in;
469
		out++;
470
	}
471
	*out = '\0';
472
	return buf;
473
}
474
475
static struct ast_custom_function escape_function = {
476
	.name = "SQL_ESC",
477
	.synopsis = "Escapes single ticks for use in SQL statements",
478
	.syntax = "SQL_ESC(<string>)",
479
	.desc =
480
"Used in SQL templates to escape data which may contain single ticks (') which\n"
481
"are otherwise used to delimit data.  For example:\n"
482
"SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
483
	.read = acf_escape,
484
	.write = NULL,
485
};
486
487
488
static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
489
{
490
	const char *tmp;
491
	int i;
492
493
	if (!cfg || !catg) {
494
		return -1;
495
	}
496
497
	*query = calloc(1, sizeof(struct acf_odbc_query));
498
	if (! (*query))
499
		return ENOMEM;
500
501
	if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
502
		char *tmp2 = ast_strdupa(tmp);
503
		AST_DECLARE_APP_ARGS(write,
504
			AST_APP_ARG(dsn)[5];
505
		);
506
		TRUNK_NONSTANDARD_APP_ARGS(write, tmp2, ',');
507
		for (i = 0; i < 5; i++) {
508
			if (!ast_strlen_zero(write.dsn[i]))
509
				ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i]));
510
		}
511
	}
512
513
	if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
514
		char *tmp2 = ast_strdupa(tmp);
515
		AST_DECLARE_APP_ARGS(read,
516
			AST_APP_ARG(dsn)[5];
517
		);
518
		TRUNK_NONSTANDARD_APP_ARGS(read, tmp2, ',');
519
		for (i = 0; i < 5; i++) {
520
			if (!ast_strlen_zero(read.dsn[i]))
521
				ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i]));
522
		}
523
	} else {
524
		/* If no separate readhandle, then use the writehandle for reading */
525
		for (i = 0; i < 5; i++) {
526
			if (!ast_strlen_zero((*query)->writehandle[i]))
527
				ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
528
		}
529
	}
530
531
	if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
532
		ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
533
		ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
534
	} else if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
535
		ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
536
537
	if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
538
		free(*query);
539
		*query = NULL;
540
		ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
541
		return EINVAL;
542
	}
543
544
	if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
545
		ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
546
		ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
547
	} else if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
548
		ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
549
550
	if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
551
		free(*query);
552
		*query = NULL;
553
		ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
554
		return EINVAL;
555
	}
556
557
	/* Allow escaping of embedded commas in fields to be turned off */
558
	ast_set_flag((*query), OPT_ESCAPECOMMAS);
559
	if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
560
		if (ast_false(tmp))
561
			ast_clear_flag((*query), OPT_ESCAPECOMMAS);
562
	}
563
564
	(*query)->acf = calloc(1, sizeof(struct ast_custom_function));
565
	if (! (*query)->acf) {
566
		free(*query);
567
		*query = NULL;
568
		return ENOMEM;
569
	}
570
571
	if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
572
		asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
573
	} else {
574
		asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
575
	}
576
577
	if (!((*query)->acf->name)) {
578
		free((*query)->acf);
579
		free(*query);
580
		*query = NULL;
581
		return ENOMEM;
582
	}
583
584
	asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
585
586
	if (!((*query)->acf->syntax)) {
587
		free((char *)(*query)->acf->name);
588
		free((*query)->acf);
589
		free(*query);
590
		*query = NULL;
591
		return ENOMEM;
592
	}
593
594
	(*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
595
	if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
596
		asprintf((char **)&((*query)->acf->desc),
597
					"Runs the following query, as defined in func_odbc.conf, performing\n"
598
				   	"substitution of the arguments into the query as specified by ${ARG1},\n"
599
					"${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
600
					"either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
601
					"\nRead:\n%s\n\nWrite:\n%s\n",
602
					(*query)->sql_read,
603
					(*query)->sql_write);
604
	} else if (!ast_strlen_zero((*query)->sql_read)) {
605
		asprintf((char **)&((*query)->acf->desc),
606
					"Runs the following query, as defined in func_odbc.conf, performing\n"
607
				   	"substitution of the arguments into the query as specified by ${ARG1},\n"
608
					"${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
609
					(*query)->sql_read);
610
	} else if (!ast_strlen_zero((*query)->sql_write)) {
611
		asprintf((char **)&((*query)->acf->desc),
612
					"Runs the following query, as defined in func_odbc.conf, performing\n"
613
				   	"substitution of the arguments into the query as specified by ${ARG1},\n"
614
					"${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
615
					"${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
616
					"This function may only be set.\nSQL:\n%s\n",
617
					(*query)->sql_write);
618
	} else {
619
		free((char *)(*query)->acf->syntax);
620
		free((char *)(*query)->acf->name);
621
		free((*query)->acf);
622
		free(*query);
623
		*query = NULL;
624
		ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg);
625
		return EINVAL;
626
	}
627
628
	/* Could be out of memory, or could be we have neither sql_read nor sql_write */
629
	if (! ((*query)->acf->desc)) {
630
		free((char *)(*query)->acf->syntax);
631
		free((char *)(*query)->acf->name);
632
		free((*query)->acf);
633
		free(*query);
634
		*query = NULL;
635
		return ENOMEM;
636
	}
637
638
	if (ast_strlen_zero((*query)->sql_read)) {
639
		(*query)->acf->read = NULL;
640
	} else {
641
		(*query)->acf->read = acf_odbc_read;
642
	}
643
644
	if (ast_strlen_zero((*query)->sql_write)) {
645
		(*query)->acf->write = NULL;
646
	} else {
647
		(*query)->acf->write = acf_odbc_write;
648
	}
649
650
	return 0;
651
}
652
653
static int free_acf_query(struct acf_odbc_query *query)
654
{
655
	if (query) {
656
		if (query->acf) {
657
			if (query->acf->name)
658
				free(query->acf->name);
659
			if (query->acf->syntax)
660
				free(query->acf->syntax);
661
			if (query->acf->desc)
662
				free(query->acf->desc);
663
			free(query->acf);
664
		}
665
		free(query);
666
	}
667
	return 0;
668
}
669
670
static int odbc_load_module(void)
671
{
672
	int res = 0;
673
	struct ast_config *cfg;
674
	char *catg;
675
676
	AST_LIST_LOCK(&queries);
677
678
	cfg = ast_config_load(config);
679
	if (!cfg) {
680
		ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
681
		AST_LIST_UNLOCK(&queries);
682
		return -1;
683
	}
684
685
	for (catg = ast_category_browse(cfg, NULL);
686
	     catg;
687
	     catg = ast_category_browse(cfg, catg)) {
688
		struct acf_odbc_query *query = NULL;
689
		int err;
690
691
		if ((err = init_acf_query(cfg, catg, &query))) {
692
			if (err == ENOMEM)
693
				ast_log(LOG_ERROR, "Out of memory\n");
694
			else if (err == EINVAL)
695
				ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
696
			else
697
				ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
698
		} else {
699
			AST_LIST_INSERT_HEAD(&queries, query, list);
700
			ast_custom_function_register(query->acf);
701
		}
702
	}
703
704
	ast_config_destroy(cfg);
705
	ast_custom_function_register(&escape_function);
706
707
	AST_LIST_UNLOCK(&queries);
708
	return res;
709
}
710
711
static int odbc_unload_module(void)
712
{
713
	struct acf_odbc_query *query;
714
715
	AST_LIST_LOCK(&queries);
716
	while (!AST_LIST_EMPTY(&queries)) {
717
		query = AST_LIST_REMOVE_HEAD(&queries, list);
718
		ast_custom_function_unregister(query->acf);
719
		free_acf_query(query);
720
	}
721
722
	ast_custom_function_unregister(&escape_function);
723
724
	/* Allow any threads waiting for this lock to pass (avoids a race) */
725
	AST_LIST_UNLOCK(&queries);
726
	AST_LIST_LOCK(&queries);
727
728
	AST_LIST_UNLOCK(&queries);
729
	return 0;
730
}
731
732
int reload(void)
733
{
734
	int res = 0;
735
	struct ast_config *cfg;
736
	struct acf_odbc_query *oldquery;
737
	char *catg;
738
739
	AST_LIST_LOCK(&queries);
740
741
	while (!AST_LIST_EMPTY(&queries)) {
742
		oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
743
		ast_custom_function_unregister(oldquery->acf);
744
		free_acf_query(oldquery);
745
	}
746
747
	cfg = ast_config_load(config);
748
	if (!cfg) {
749
		ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
750
		goto reload_out;
751
	}
752
753
	for (catg = ast_category_browse(cfg, NULL);
754
	     catg;
755
	     catg = ast_category_browse(cfg, catg)) {
756
		struct acf_odbc_query *query = NULL;
757
758
		if (init_acf_query(cfg, catg, &query)) {
759
			ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
760
		} else {
761
			AST_LIST_INSERT_HEAD(&queries, query, list);
762
			ast_custom_function_register(query->acf);
763
		}
764
	}
765
766
	ast_config_destroy(cfg);
767
reload_out:
768
	AST_LIST_UNLOCK(&queries);
769
	return res;
770
}
771
772
int unload_module(void)
773
{
774
	return odbc_unload_module();
775
}
776
777
int load_module(void)
778
{
779
	return odbc_load_module();
780
}
781
782
char *description(void)
783
{
784
	return tdesc;
785
}
786
787
int usecount(void)
788
{
789
	if (! ast_mutex_trylock(&(&queries)->lock)) {
790
		ast_mutex_unlock(&(&queries)->lock);
791
		return 0;
792
	} else {
793
		return 1;
794
	}
795
}
796
797
char *key()
798
{
799
	return ASTERISK_GPL_KEY;
800
}
(-)asterisk.original/files/1.2.0/backports/func_odbc/func_odbc.conf.sample (+43 lines)
Line 0 Link Here
1
;
2
; func_odbc.conf
3
;
4
; Each context is a separately defined function.  By convention, all
5
; functions are entirely uppercase, so the defined contexts should also
6
; be all-uppercase, but there is nothing that enforces this.  All functions
7
; are case-sensitive, however.
8
;
9
; For substitution, you have ${ARG1}, ${ARG2} ... ${ARGn}
10
; for the arguments to each SQL statement.
11
;
12
; In addition, for write statements, you have ${VAL1}, ${VAL2} ... ${VALn}
13
; parsed, just like arguments, for the values.  In addition, if you want the
14
; whole value, never mind the parsing, you can get that with ${VALUE}.
15
;
16
;
17
; If you have data which may potentially contain single ticks, you may wish
18
; to use the dialplan function SQL_ESC() to escape the data prior to its
19
; inclusion in the SQL statement.
20
21
22
; ODBC_SQL - Allow an SQL statement to be built entirely in the dialplan
23
[SQL]
24
readhandle=mysql1
25
readsql=${ARG1}
26
27
; ODBC_ANTIGF - A blacklist.
28
[ANTIGF]
29
readhandle=mysql1
30
readsql=SELECT COUNT(*) FROM exgirlfriends WHERE callerid='${SQL_ESC(${ARG1})}'
31
32
; ODBC_PRESENCE - Retrieve and update presence
33
[PRESENCE]
34
readhandle=mysql1
35
writehandle=mysql1
36
readsql=SELECT location FROM presence WHERE id='${SQL_ESC(${ARG1})}'
37
writesql=UPDATE presence SET location='${SQL_ESC(${VAL1})}' WHERE id='${SQL_ESC(${ARG1})}'
38
;
39
;prefix=OFFICE		; Changes this function from ODBC_PRESENCE to OFFICE_PRESENCE
40
;escapecommas=no	; Normally, commas within a field are escaped such that each
41
			; field may be separated into individual variables with ARRAY or HASH.
42
			; This option turns that behavior off [default=yes].
43
(-)asterisk.original/files/1.2.0/backports/func_odbc/separate.h (+47 lines)
Line 0 Link Here
1
#define TRUNK_STANDARD_APP_ARGS(args, parse) \
2
	args.argc = trunk_app_separate_args(parse, '|', args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
3
#define TRUNK_NONSTANDARD_APP_ARGS(args, parse, sep) \
4
	args.argc = trunk_app_separate_args(parse, sep, args.argv, (sizeof(args) - sizeof(args.argc)) / sizeof(args.argv[0]))
5
6
static unsigned int trunk_app_separate_args(char *buf, char delim, char **array, int arraylen)
7
{
8
	int argc;
9
	char *scan;
10
	int paren = 0, quote = 0;
11
12
	if (!buf || !array || !arraylen)
13
		return 0;
14
15
	memset(array, 0, arraylen * sizeof(*array));
16
17
	scan = buf;
18
19
	for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
20
		array[argc] = scan;
21
		for (; *scan; scan++) {
22
			if (*scan == '(')
23
				paren++;
24
			else if (*scan == ')') {
25
				if (paren)
26
					paren--;
27
			} else if (*scan == '"') {
28
				quote = quote ? 0 : 1;
29
				/* Remove quote character from argument */
30
				memmove(scan, scan + 1, strlen(scan));
31
				scan--;
32
			} else if (*scan == '\\') {
33
				/* Literal character, don't parse */
34
				memmove(scan, scan + 1, strlen(scan));
35
			} else if ((*scan == delim) && !paren && !quote) {
36
				*scan++ = '\0';
37
				break;
38
			}
39
		}
40
	}
41
42
	if (*scan)
43
		array[argc++] = scan;
44
45
	return argc;
46
}
47

Return to bug 212306