Asterisk - The Open Source Telephony Project GIT-master-a358458
func_odbc.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (c) 2005, 2006 Tilghman Lesher
5 * Copyright (c) 2008, 2009 Digium, Inc.
6 *
7 * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
8 *
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
14 *
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
18 */
19
20/*!
21 * \file
22 *
23 * \brief ODBC lookups
24 *
25 * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
26 *
27 * \ingroup functions
28 */
29
30/*** MODULEINFO
31 <depend>res_odbc</depend>
32 <depend>generic_odbc</depend>
33 <support_level>core</support_level>
34 ***/
35
36#include "asterisk.h"
37
38#include "asterisk/module.h"
39#include "asterisk/file.h"
40#include "asterisk/channel.h"
41#include "asterisk/pbx.h"
42#include "asterisk/config.h"
43#include "asterisk/res_odbc.h"
45#include "asterisk/app.h"
46#include "asterisk/cli.h"
47#include "asterisk/strings.h"
48
49/*** DOCUMENTATION
50 <function name="ODBC_FETCH" language="en_US">
51 <synopsis>
52 Fetch a row from a multirow query.
53 </synopsis>
54 <syntax>
55 <parameter name="result-id" required="true" />
56 </syntax>
57 <description>
58 <para>For queries which are marked as mode=multirow, the original
59 query returns a <replaceable>result-id</replaceable> from which results
60 may be fetched. This function implements the actual fetch of the results.</para>
61 <para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
62 <variablelist>
63 <variable name="ODBC_FETCH_STATUS">
64 <value name="SUCESS">
65 If rows are available.
66 </value>
67 <value name="FAILURE">
68 If no rows are available.
69 </value>
70 </variable>
71 </variablelist>
72 </description>
73 </function>
74 <application name="ODBCFinish" language="en_US">
75 <synopsis>
76 Clear the resultset of a sucessful multirow query.
77 </synopsis>
78 <syntax>
79 <parameter name="result-id" required="true" />
80 </syntax>
81 <description>
82 <para>For queries which are marked as mode=multirow, this will clear
83 any remaining rows of the specified resultset.</para>
84 </description>
85 </application>
86 <function name="SQL_ESC" language="en_US">
87 <synopsis>
88 Escapes single ticks for use in SQL statements.
89 </synopsis>
90 <syntax>
91 <parameter name="string" required="true" />
92 </syntax>
93 <description>
94 <para>Used in SQL templates to escape data which may contain single ticks
95 <literal>'</literal> which are otherwise used to delimit data.</para>
96 <example title="Escape example">
97 SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'
98 </example>
99 </description>
100 </function>
101 <function name="SQL_ESC_BACKSLASHES" language="en_US">
102 <synopsis>
103 Escapes backslashes for use in SQL statements.
104 </synopsis>
105 <syntax>
106 <parameter name="string" required="true" />
107 </syntax>
108 <description>
109 <para>Used in SQL templates to escape data which may contain backslashes
110 <literal></literal> which are otherwise used to escape data.</para>
111 <example title="Escape with backslashes example">
112 SELECT foo FROM bar WHERE baz='${SQL_ESC(${SQL_ESC_BACKSLASHES(${ARG1})})}'
113 </example>
114 </description>
115 </function>
116 ***/
117
118static char *config = "func_odbc.conf";
119
120#define DEFAULT_SINGLE_DB_CONNECTION 0
121
123
125
128 OPT_MULTIROW = (1 << 1),
129};
130
133 char readhandle[5][30];
134 char writehandle[5][30];
135 char *sql_read;
136 char *sql_write;
137 char *sql_insert;
138 unsigned int flags;
139 int rowlimit;
140 int minargs;
141 struct ast_custom_function *acf;
142};
143
144static void odbc_datastore_free(void *data);
145
146static const struct ast_datastore_info odbc_info = {
147 .type = "FUNC_ODBC",
148 .destroy = odbc_datastore_free,
149};
150
151/* For storing each result row */
154 char data[0];
155};
156
157/* For storing each result set */
160 char names[0];
161};
162
163/*! \brief Data source name
164 *
165 * This holds data that pertains to a DSN
166 */
167struct dsn {
168 /*! A connection to the database */
170 /*! The name of the DSN as defined in res_odbc.conf */
171 char name[0];
172};
173
174#define DSN_BUCKETS 37
175
177
178static int dsn_hash(const void *obj, const int flags)
179{
180 const struct dsn *object;
181 const char *key;
182
183 switch (flags & OBJ_SEARCH_MASK) {
184 case OBJ_SEARCH_KEY:
185 key = obj;
186 break;
188 object = obj;
189 key = object->name;
190 break;
191 default:
192 ast_assert(0);
193 return 0;
194 }
195 return ast_str_hash(key);
196}
197
198static int dsn_cmp(void *obj, void *arg, int flags)
199{
200 const struct dsn *object_left = obj;
201 const struct dsn *object_right = arg;
202 const char *right_key = arg;
203 int cmp;
204
205 switch (flags & OBJ_SEARCH_MASK) {
207 right_key = object_right->name;
208 /* Fall through */
209 case OBJ_SEARCH_KEY:
210 cmp = strcmp(object_left->name, right_key);
211 break;
213 cmp = strncmp(object_left->name, right_key, strlen(right_key));
214 break;
215 default:
216 cmp = 0;
217 break;
218 }
219
220 if (cmp) {
221 return 0;
222 }
223
224 return CMP_MATCH;
225}
226
227static void dsn_destructor(void *obj)
228{
229 struct dsn *dsn = obj;
230
231 if (dsn->connection) {
233 }
234}
235
236/*!
237 * \brief Create a DSN and connect to the database
238 *
239 * \param name The name of the DSN as found in res_odbc.conf
240 * \retval NULL Fail
241 * \retval non-NULL The newly-created structure
242 */
243static struct dsn *create_dsn(const char *name)
244{
245 struct dsn *dsn;
246
247 if (!dsns) {
248 return NULL;
249 }
250
251 dsn = ao2_alloc(sizeof(*dsn) + strlen(name) + 1, dsn_destructor);
252 if (!dsn) {
253 return NULL;
254 }
255
256 /* Safe */
257 strcpy(dsn->name, name);
258
260 if (!dsn->connection) {
261 ao2_ref(dsn, -1);
262 return NULL;
263 }
264
266 ao2_ref(dsn, -1);
267 return NULL;
268 }
269
270 return dsn;
271}
272
273static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data);
274
275/*!
276 * \brief Determine if the connection has died.
277 *
278 * \param connection The connection to check
279 * \retval 1 Yep, it's dead
280 * \retval 0 It's alive and well
281 */
283{
284 SQLINTEGER dead;
285 SQLRETURN res;
286 SQLHSTMT stmt;
287
288 if (!connection) {
289 return 1;
290 }
291
292 res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
293 if (SQL_SUCCEEDED(res)) {
294 return dead == SQL_CD_TRUE ? 1 : 0;
295 }
296
297 /* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a direct
298 * execute of a probing statement and see if that succeeds instead
299 */
301 if (!stmt) {
302 return 1;
303 }
304
305 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
306 return 0;
307}
308
309/*!
310 * \brief Retrieve a DSN, or create it if it does not exist.
311 *
312 * The created DSN is returned locked. This should be inconsequential
313 * to callers in most cases.
314 *
315 * When finished with the returned structure, the caller must call
316 * \ref release_obj_or_dsn
317 *
318 * \param name Name of the DSN as found in res_odbc.conf
319 * \retval NULL Unable to retrieve or create the DSN
320 * \retval non-NULL The retrieved/created locked DSN
321 */
322static struct dsn *get_dsn(const char *name)
323{
324 struct dsn *dsn;
325
326 if (!dsns) {
327 return NULL;
328 }
329
330 ao2_lock(dsns);
332 if (!dsn) {
334 }
336
337 if (!dsn) {
338 return NULL;
339 }
340
341 ao2_lock(dsn);
342 if (!dsn->connection) {
344 if (!dsn->connection) {
346 ao2_ref(dsn, -1);
347 return NULL;
348 }
349 return dsn;
350 }
351
355 if (!dsn->connection) {
357 ao2_ref(dsn, -1);
358 return NULL;
359 }
360 }
361
362 return dsn;
363}
364
365/*!
366 * \brief Get a DB handle via a DSN or directly
367 *
368 * If single db connection then get the DB handle via DSN
369 * else by requesting a connection directly
370 *
371 * \param dsn_name Name of the DSN as found in res_odbc.conf
372 * \param dsn The pointer to the DSN
373 * \retval NULL Unable to retrieve the DB handle
374 * \retval non-NULL The retrieved DB handle
375 */
376static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn)
377{
378 struct odbc_obj *obj = NULL;
379
382 if (dsn) {
383 *dsn = get_dsn(dsn_name);
384 if (*dsn) {
385 obj = (*dsn)->connection;
386 }
387 }
388 } else {
389 obj = ast_odbc_request_obj(dsn_name, 0);
390 }
392
393 return obj;
394}
395
396/*!
397 * \brief Release an ODBC obj or a DSN
398 *
399 * If single db connection then unlock and unreference the DSN
400 * else release the ODBC obj
401 *
402 * \param obj The pointer to the ODBC obj to release
403 * \param dsn The pointer to the dsn to unlock and unreference
404 */
405static inline void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
406{
407 if (dsn && *dsn) {
408 /* If multiple connections are not enabled then the guarantee
409 * of a single connection already exists and holding on to the
410 * connection would prevent any other user from acquiring it
411 * indefinitely.
412 */
413 if (ast_odbc_get_max_connections((*dsn)->name) < 2) {
414 ast_odbc_release_obj((*dsn)->connection);
415 (*dsn)->connection = NULL;
416 }
417 ao2_unlock(*dsn);
418 ao2_ref(*dsn, -1);
419 *dsn = NULL;
420 /* Some callers may provide both an obj and dsn. To ensure that
421 * the connection is not released twice we set it to NULL here if
422 * present.
423 */
424 if (obj) {
425 *obj = NULL;
426 }
427 } else if (obj && *obj) {
429 *obj = NULL;
430 }
431}
432
434
435static int resultcount = 0;
436
441
442static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
443
444static void odbc_datastore_free(void *data)
445{
446 struct odbc_datastore *result = data;
447 struct odbc_datastore_row *row;
448
449 if (!result) {
450 return;
451 }
452
454 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
455 ast_free(row);
456 }
460}
461
462/*!
463 * \brief Common execution function for SQL queries.
464 *
465 * \param obj DB connection
466 * \param data The query to execute
467 * \param silent If true, do not print warnings on failure
468 * \retval NULL Failed to execute query
469 * \retval non-NULL The executed statement
470 */
471static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
472{
473 int res;
474 char *sql = data;
475 SQLHSTMT stmt;
476
477 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
478 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
479 ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
480 return NULL;
481 }
482
483 res = ast_odbc_execute_sql(obj, stmt, sql);
484 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
485 if (res == SQL_ERROR && !silent) {
486 int i;
487 SQLINTEGER nativeerror=0, numfields=0;
488 SQLSMALLINT diagbytes=0;
489 unsigned char state[10], diagnostic[256];
490
491 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
492 for (i = 0; i < numfields; i++) {
493 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
494 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
495 if (i > 10) {
496 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
497 break;
498 }
499 }
500 }
501
502 if (!silent) {
503 ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
504 }
505 SQLCloseCursor(stmt);
506 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
507 return NULL;
508 }
509
510 return stmt;
511}
512
513static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
514{
515 return execute(obj, data, 0);
516}
517
518static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
519{
520 return execute(obj, data, 1);
521}
522
523/*
524 * Master control routine
525 */
526static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
527{
528 struct odbc_obj *obj = NULL;
529 struct acf_odbc_query *query;
530 char *t, varname[15];
531 int i, dsn_num, bogus_chan = 0;
532 int transactional = 0;
534 AST_APP_ARG(field)[100];
535 );
537 AST_APP_ARG(field)[100];
538 );
539 SQLHSTMT stmt = NULL;
540 SQLLEN rows=0;
541 struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
542 struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
543 const char *status = "FAILURE";
544 struct dsn *dsn = NULL;
545
546 if (!buf || !insertbuf) {
547 return -1;
548 }
549
551 AST_RWLIST_TRAVERSE(&queries, query, list) {
552 if (!strcmp(query->acf->name, cmd)) {
553 break;
554 }
555 }
556
557 if (!query) {
558 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
560 if (chan) {
561 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
562 }
563 return -1;
564 }
565
567 if (args.argc < query->minargs) {
568 ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
569 args.argc, cmd, query->minargs);
571 return -1;
572 }
573
574 if (!chan) {
575 if (!(chan = ast_dummy_channel_alloc())) {
577 return -1;
578 }
579 bogus_chan = 1;
580 }
581
582 if (!bogus_chan) {
584 }
585
586 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
587 /* We only get here if sql_write is set. sql_insert is optional however. */
588 if (query->sql_insert) {
589 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
590 }
591
592 /* Parse our arguments */
593 t = value ? ast_strdupa(value) : "";
594
595 if (!s || !t) {
596 ast_log(LOG_ERROR, "Out of memory\n");
598 if (!bogus_chan) {
600 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
601 } else {
602 ast_channel_unref(chan);
603 }
604 return -1;
605 }
606
607 snprintf(varname, sizeof(varname), "%u", args.argc);
608 pbx_builtin_pushvar_helper(chan, "ARGC", varname);
609 for (i = 0; i < args.argc; i++) {
610 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
611 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
612 }
613
614 /* Parse values, just like arguments */
616 for (i = 0; i < values.argc; i++) {
617 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
618 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
619 }
620
621 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
622 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
623
624 ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
625 if (query->sql_insert) {
626 ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
627 }
628
629 if (bogus_chan) {
630 chan = ast_channel_unref(chan);
631 } else {
632 /* Restore prior values */
633 pbx_builtin_setvar_helper(chan, "ARGC", NULL);
634
635 for (i = 0; i < args.argc; i++) {
636 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
637 pbx_builtin_setvar_helper(chan, varname, NULL);
638 }
639
640 for (i = 0; i < values.argc; i++) {
641 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
642 pbx_builtin_setvar_helper(chan, varname, NULL);
643 }
644 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
645 }
646
647 /*!\note
648 * Okay, this part is confusing. Transactions belong to a single database
649 * handle. Therefore, when working with transactions, we CANNOT failover
650 * to multiple DSNs. We MUST have a single handle all the way through the
651 * transaction, or else we CANNOT enforce atomicity.
652 */
653 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
654 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
655 if (transactional) {
656 /* This can only happen second time through or greater. */
657 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
658 }
659
660 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
661 transactional = 1;
662 } else {
663 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
664 transactional = 0;
665 }
666
667 if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
668 break;
669 }
670 if (!transactional) {
671 release_obj_or_dsn (&obj, &dsn);
672 }
673 }
674 }
675
676 if (stmt) {
677 SQLRowCount(stmt, &rows);
678 SQLCloseCursor(stmt);
679 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
680
681 if (rows != 0) {
682 status = "SUCCESS";
683
684 } else if (query->sql_insert) {
685 if (!transactional) {
686 release_obj_or_dsn (&obj, &dsn);
687 }
688
689 for (transactional = 0, dsn_num = 0; dsn_num < 5; dsn_num++) {
690 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
691 if (transactional) {
692 /* This can only happen second time through or greater. */
693 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
694 } else {
695 release_obj_or_dsn (&obj, &dsn);
696 }
697
698 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
699 transactional = 1;
700 } else {
701 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
702 transactional = 0;
703 }
704 if (obj) {
706 }
707 }
708 if (stmt) {
709 status = "FAILOVER";
710 SQLRowCount(stmt, &rows);
711 SQLCloseCursor(stmt);
712 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
713 break;
714 }
715 }
716 }
717 }
718
720
721 /* Output the affected rows, for all cases. In the event of failure, we
722 * flag this as -1 rows. Note that this is different from 0 affected rows
723 * which would be the case if we succeeded in our query, but the values did
724 * not change. */
725 if (!bogus_chan) {
726 snprintf(varname, sizeof(varname), "%d", (int)rows);
727 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
728 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
729 }
730
731 if (!transactional) {
732 release_obj_or_dsn (&obj, &dsn);
733 }
734
735 if (!bogus_chan) {
737 }
738
739 return 0;
740}
741
742static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
743{
744 struct odbc_obj *obj = NULL;
745 struct acf_odbc_query *query;
746 char varname[15], rowcount[12] = "-1";
747 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
748 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn_num, bogus_chan = 0;
750 AST_APP_ARG(field)[100];
751 );
752 SQLHSTMT stmt = NULL;
753 SQLSMALLINT colcount=0;
754 SQLLEN indicator;
755 SQLSMALLINT collength;
756 struct odbc_datastore *resultset = NULL;
757 struct odbc_datastore_row *row = NULL;
758 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
759 const char *status = "FAILURE";
760 struct dsn *dsn = NULL;
761
762 if (!sql || !colnames) {
763 if (chan) {
764 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
765 }
766 return -1;
767 }
768
769 ast_str_reset(colnames);
770
772 AST_RWLIST_TRAVERSE(&queries, query, list) {
773 if (!strcmp(query->acf->name, cmd)) {
774 break;
775 }
776 }
777
778 if (!query) {
779 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
781 if (chan) {
782 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
783 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
784 }
785 return -1;
786 }
787
789 if (args.argc < query->minargs) {
790 ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
791 args.argc, cmd, query->minargs);
793 return -1;
794 }
795
796 if (!chan) {
797 if (!(chan = ast_dummy_channel_alloc())) {
799 return -1;
800 }
801 bogus_chan = 1;
802 }
803
804 if (!bogus_chan) {
806 }
807
808 snprintf(varname, sizeof(varname), "%u", args.argc);
809 pbx_builtin_pushvar_helper(chan, "ARGC", varname);
810 for (x = 0; x < args.argc; x++) {
811 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
812 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
813 }
814
815 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
816
817 if (bogus_chan) {
818 chan = ast_channel_unref(chan);
819 } else {
820 /* Restore prior values */
821 pbx_builtin_setvar_helper(chan, "ARGC", NULL);
822
823 for (x = 0; x < args.argc; x++) {
824 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
825 pbx_builtin_setvar_helper(chan, varname, NULL);
826 }
827 }
828
829 /* Save these flags, so we can release the lock */
830 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
831 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
832 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
833 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
834 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
837 return -1;
838 }
839 AST_LIST_HEAD_INIT(resultset);
840 if (query->rowlimit) {
841 rowlimit = query->rowlimit;
842 } else {
843 rowlimit = INT_MAX;
844 }
845 multirow = 1;
846 } else if (!bogus_chan) {
847 if (query->rowlimit > 1) {
848 rowlimit = query->rowlimit;
849 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
850 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
851 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
854 return -1;
855 }
856 AST_LIST_HEAD_INIT(resultset);
857 }
858 }
860
861 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
862 if (!ast_strlen_zero(query->readhandle[dsn_num])) {
863 obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
864 if (!obj) {
865 continue;
866 }
868 }
869 if (stmt) {
870 break;
871 }
872 release_obj_or_dsn (&obj, &dsn);
873 }
874
875 if (!stmt) {
876 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
877 release_obj_or_dsn (&obj, &dsn);
878 if (!bogus_chan) {
879 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
881 }
882 odbc_datastore_free(resultset);
883 return -1;
884 }
885
886 res = SQLNumResultCols(stmt, &colcount);
887 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
888 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
889 SQLCloseCursor(stmt);
890 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
891 release_obj_or_dsn (&obj, &dsn);
892 if (!bogus_chan) {
893 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
895 }
896 odbc_datastore_free(resultset);
897 return -1;
898 }
899
900 if (colcount <= 0) {
901 ast_verb(4, "Returned %d columns [%s]\n", colcount, ast_str_buffer(sql));
902 buf[0] = '\0';
903 SQLCloseCursor(stmt);
904 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
905 release_obj_or_dsn (&obj, &dsn);
906 if (!bogus_chan) {
907 pbx_builtin_setvar_helper(chan, "ODBCROWS", "0");
908 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "NODATA");
910 }
911 odbc_datastore_free(resultset);
912 return 0;
913 }
914
915 res = SQLFetch(stmt);
916 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
917 int res1 = -1;
918 if (res == SQL_NO_DATA) {
919 ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
920 res1 = 0;
921 buf[0] = '\0';
922 ast_copy_string(rowcount, "0", sizeof(rowcount));
923 status = "NODATA";
924 } else {
925 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
926 status = "FETCHERROR";
927 }
928 SQLCloseCursor(stmt);
929 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
930 release_obj_or_dsn (&obj, &dsn);
931 if (!bogus_chan) {
932 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
933 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
935 }
936 odbc_datastore_free(resultset);
937 return res1;
938 }
939
940 status = "SUCCESS";
941
942 for (y = 0; y < rowlimit; y++) {
943 buf[0] = '\0';
944 for (x = 0; x < colcount; x++) {
945 int i;
946 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
947 char *ptrcoldata;
948
949 if (!coldata) {
950 odbc_datastore_free(resultset);
951 SQLCloseCursor(stmt);
952 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
953 release_obj_or_dsn (&obj, &dsn);
954 if (!bogus_chan) {
955 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
957 }
958 return -1;
959 }
960
961 if (y == 0) {
962 char colname[256];
963 SQLLEN octetlength = 0;
964
965 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
966 ast_debug(3, "Got collength of %d for column '%s' (offset %d)\n", (int)collength, colname, x);
967 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
968 snprintf(colname, sizeof(colname), "field%d", x);
969 }
970
971 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
972
973 ast_str_make_space(&coldata, octetlength + 1);
974
975 if (ast_str_strlen(colnames)) {
976 ast_str_append(&colnames, 0, ",");
977 }
978 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
979
980 if (resultset) {
981 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
982 if (!tmp) {
983 ast_log(LOG_ERROR, "No space for a new resultset?\n");
984 odbc_datastore_free(resultset);
985 SQLCloseCursor(stmt);
986 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
987 release_obj_or_dsn (&obj, &dsn);
988 if (!bogus_chan) {
989 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
990 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
992 }
993 return -1;
994 }
995 resultset = tmp;
996 strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
997 }
998 }
999
1000 buflen = strlen(buf);
1001 res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
1002 if (indicator == SQL_NULL_DATA) {
1003 ast_debug(3, "Got NULL data\n");
1004 ast_str_reset(coldata);
1005 res = SQL_SUCCESS;
1006 }
1007
1008 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1009 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
1010 y = -1;
1011 buf[0] = '\0';
1012 goto end_acf_read;
1013 }
1014
1015 ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
1016
1017 if (x) {
1018 buf[buflen++] = ',';
1019 }
1020
1021 /* Copy data, encoding '\' and ',' for the argument parser */
1022 ptrcoldata = ast_str_buffer(coldata);
1023 for (i = 0; i < ast_str_strlen(coldata); i++) {
1024 if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
1025 buf[buflen++] = '\\';
1026 }
1027 buf[buflen++] = ptrcoldata[i];
1028
1029 if (buflen >= len - 2) {
1030 break;
1031 }
1032
1033 if (ptrcoldata[i] == '\0') {
1034 break;
1035 }
1036 }
1037
1038 buf[buflen] = '\0';
1039 ast_debug(2, "buf is now set to '%s'\n", buf);
1040 }
1041 ast_debug(2, "buf is now set to '%s'\n", buf);
1042
1043 if (resultset) {
1044 row = ast_calloc(1, sizeof(*row) + buflen + 1);
1045 if (!row) {
1046 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
1047 status = "MEMERROR";
1048 goto end_acf_read;
1049 }
1050 strcpy((char *)row + sizeof(*row), buf);
1051 AST_LIST_INSERT_TAIL(resultset, row, list);
1052
1053 /* Get next row */
1054 res = SQLFetch(stmt);
1055 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1056 if (res != SQL_NO_DATA) {
1057 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1058 }
1059 /* Number of rows in the resultset */
1060 y++;
1061 break;
1062 }
1063 }
1064 }
1065
1066end_acf_read:
1067 if (!bogus_chan) {
1068 snprintf(rowcount, sizeof(rowcount), "%d", y);
1069 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
1070 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
1071 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
1072 if (resultset) {
1073 struct ast_datastore *odbc_store;
1074 if (multirow) {
1075 int uid;
1077 snprintf(buf, len, "%d", uid);
1078 } else {
1079 /* Name of the query is name of the resultset */
1080 ast_copy_string(buf, cmd, len);
1081
1082 /* If there's one with the same name already, free it */
1083 ast_channel_lock(chan);
1084 if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
1085 ast_channel_datastore_remove(chan, odbc_store);
1086 ast_datastore_free(odbc_store);
1087 }
1088 ast_channel_unlock(chan);
1089 }
1090 odbc_store = ast_datastore_alloc(&odbc_info, buf);
1091 if (!odbc_store) {
1092 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
1093 odbc_datastore_free(resultset);
1094 SQLCloseCursor(stmt);
1095 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1096 release_obj_or_dsn (&obj, &dsn);
1097 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
1099 return -1;
1100 }
1101 odbc_store->data = resultset;
1102 ast_channel_lock(chan);
1103 ast_channel_datastore_add(chan, odbc_store);
1104 ast_channel_unlock(chan);
1105 }
1106 }
1107 SQLCloseCursor(stmt);
1108 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1109 release_obj_or_dsn (&obj, &dsn);
1110 if (resultset && !multirow) {
1111 /* Fetch the first resultset */
1112 if (!acf_fetch(chan, "", buf, buf, len)) {
1113 buf[0] = '\0';
1114 }
1115 }
1116 if (!bogus_chan) {
1118 }
1119 return 0;
1120}
1121
1122static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
1123{
1124 char *out = buf;
1125
1126 for (; *data && out - buf < len; data++) {
1127 if (*data == character) {
1128 *out = character;
1129 out++;
1130 }
1131 *out++ = *data;
1132 }
1133 *out = '\0';
1134
1135 return 0;
1136}
1137
1138static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1139{
1140 return acf_escape(chan, cmd, data, buf, len, '\'');
1141}
1142
1144 .name = "SQL_ESC",
1145 .read = acf_escape_ticks,
1146 .write = NULL,
1147};
1148
1149static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1150{
1151 return acf_escape(chan, cmd, data, buf, len, '\\');
1152}
1153
1155 .name = "SQL_ESC_BACKSLASHES",
1156 .read = acf_escape_backslashes,
1157 .write = NULL,
1158};
1159
1160static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1161{
1162 struct ast_datastore *store;
1163 struct odbc_datastore *resultset;
1164 struct odbc_datastore_row *row;
1165
1166 if (!chan) {
1167 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1168 return -1;
1169 }
1170
1171 ast_channel_lock(chan);
1172 store = ast_channel_datastore_find(chan, &odbc_info, data);
1173 if (!store) {
1174 ast_channel_unlock(chan);
1175 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
1176 return -1;
1177 }
1178 resultset = store->data;
1179 AST_LIST_LOCK(resultset);
1180 row = AST_LIST_REMOVE_HEAD(resultset, list);
1181 AST_LIST_UNLOCK(resultset);
1182 if (!row) {
1183 /* Cleanup datastore */
1184 ast_channel_datastore_remove(chan, store);
1185 ast_datastore_free(store);
1186 ast_channel_unlock(chan);
1187 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
1188 return -1;
1189 }
1190 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
1191 ast_channel_unlock(chan);
1192 ast_copy_string(buf, row->data, len);
1193 ast_free(row);
1194 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
1195 return 0;
1196}
1197
1199 .name = "ODBC_FETCH",
1200 .read = acf_fetch,
1201 .write = NULL,
1202};
1203
1204static char *app_odbcfinish = "ODBCFinish";
1205
1206static int exec_odbcfinish(struct ast_channel *chan, const char *data)
1207{
1208 struct ast_datastore *store;
1209
1210 ast_channel_lock(chan);
1211 store = ast_channel_datastore_find(chan, &odbc_info, data);
1212 if (store) {
1213 ast_channel_datastore_remove(chan, store);
1214 ast_datastore_free(store);
1215 }
1216 ast_channel_unlock(chan);
1217 return 0;
1218}
1219
1220static int free_acf_query(struct acf_odbc_query *query)
1221{
1222 if (query) {
1223 if (query->acf) {
1224 if (query->acf->name)
1225 ast_free((char *)query->acf->name);
1226 ast_string_field_free_memory(query->acf);
1227 ast_free(query->acf);
1228 }
1229 ast_free(query->sql_read);
1230 ast_free(query->sql_write);
1231 ast_free(query->sql_insert);
1232 ast_free(query);
1233 }
1234 return 0;
1235}
1236
1237static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
1238{
1239 const char *tmp;
1240 const char *tmp2 = NULL;
1241 int i;
1242
1243 if (!cfg || !catg) {
1244 return EINVAL;
1245 }
1246
1247 if (!(*query = ast_calloc(1, sizeof(**query)))) {
1248 return ENOMEM;
1249 }
1250
1251 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
1252 char *tmp2 = ast_strdupa(tmp);
1253 AST_DECLARE_APP_ARGS(writeconf,
1254 AST_APP_ARG(dsn)[5];
1255 );
1256 AST_STANDARD_APP_ARGS(writeconf, tmp2);
1257 for (i = 0; i < 5; i++) {
1258 if (!ast_strlen_zero(writeconf.dsn[i]))
1259 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
1260 }
1261 }
1262
1263 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
1264 char *tmp2 = ast_strdupa(tmp);
1265 AST_DECLARE_APP_ARGS(readconf,
1266 AST_APP_ARG(dsn)[5];
1267 );
1268 AST_STANDARD_APP_ARGS(readconf, tmp2);
1269 for (i = 0; i < 5; i++) {
1270 if (!ast_strlen_zero(readconf.dsn[i]))
1271 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
1272 }
1273 } else {
1274 /* If no separate readhandle, then use the writehandle for reading */
1275 for (i = 0; i < 5; i++) {
1276 if (!ast_strlen_zero((*query)->writehandle[i]))
1277 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
1278 }
1279 }
1280
1281 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")) ||
1282 (tmp2 = ast_variable_retrieve(cfg, catg, "read"))) {
1283 if (!tmp) {
1284 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
1285 tmp = tmp2;
1286 }
1287 if (*tmp != '\0') { /* non-empty string */
1288 if (!((*query)->sql_read = ast_strdup(tmp))) {
1289 free_acf_query(*query);
1290 *query = NULL;
1291 return ENOMEM;
1292 }
1293 }
1294 }
1295
1296 if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
1297 free_acf_query(*query);
1298 *query = NULL;
1299 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
1300 return EINVAL;
1301 }
1302
1303 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")) ||
1304 (tmp2 = ast_variable_retrieve(cfg, catg, "write"))) {
1305 if (!tmp) {
1306 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
1307 tmp = tmp2;
1308 }
1309 if (*tmp != '\0') { /* non-empty string */
1310 if (!((*query)->sql_write = ast_strdup(tmp))) {
1311 free_acf_query(*query);
1312 *query = NULL;
1313 return ENOMEM;
1314 }
1315 }
1316 }
1317
1318 if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
1319 free_acf_query(*query);
1320 *query = NULL;
1321 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
1322 return EINVAL;
1323 }
1324
1325 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
1326 if (*tmp != '\0') { /* non-empty string */
1327 if (!((*query)->sql_insert = ast_strdup(tmp))) {
1328 free_acf_query(*query);
1329 *query = NULL;
1330 return ENOMEM;
1331 }
1332 }
1333 }
1334
1335 /* Allow escaping of embedded commas in fields to be turned off */
1336 ast_set_flag((*query), OPT_ESCAPECOMMAS);
1337 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
1338 if (ast_false(tmp))
1340 }
1341
1342 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
1343 if (strcasecmp(tmp, "multirow") == 0)
1344 ast_set_flag((*query), OPT_MULTIROW);
1345 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
1346 sscanf(tmp, "%30d", &((*query)->rowlimit));
1347 }
1348
1349 if ((tmp = ast_variable_retrieve(cfg, catg, "minargs"))) {
1350 sscanf(tmp, "%30d", &((*query)->minargs));
1351 }
1352
1353 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
1354 if (!(*query)->acf) {
1355 free_acf_query(*query);
1356 *query = NULL;
1357 return ENOMEM;
1358 }
1359 if (ast_string_field_init((*query)->acf, 128)) {
1360 free_acf_query(*query);
1361 *query = NULL;
1362 return ENOMEM;
1363 }
1364
1365 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
1366 if (ast_asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
1367 (*query)->acf->name = NULL;
1368 }
1369 } else {
1370 if (ast_asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
1371 (*query)->acf->name = NULL;
1372 }
1373 }
1374
1375 if (!(*query)->acf->name) {
1376 free_acf_query(*query);
1377 *query = NULL;
1378 return ENOMEM;
1379 }
1380
1381 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
1382 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
1383 } else {
1384 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
1385 }
1386
1387 if (ast_strlen_zero((*query)->acf->syntax)) {
1388 free_acf_query(*query);
1389 *query = NULL;
1390 return ENOMEM;
1391 }
1392
1393 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
1394 ast_string_field_set((*query)->acf, synopsis, tmp);
1395 } else {
1396 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
1397 }
1398
1399 if (ast_strlen_zero((*query)->acf->synopsis)) {
1400 free_acf_query(*query);
1401 *query = NULL;
1402 return ENOMEM;
1403 }
1404
1405 if ((*query)->sql_read && (*query)->sql_write) {
1406 ast_string_field_build((*query)->acf, desc,
1407 "Runs the following query, as defined in func_odbc.conf, performing\n"
1408 "substitution of the arguments into the query as specified by ${ARG1},\n"
1409 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
1410 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1411 "%s"
1412 "\nRead:\n%s\n\nWrite:\n%s%s%s",
1413 (*query)->sql_insert ?
1414 "If the write query affects no rows, the insert query will be\n"
1415 "performed.\n" : "",
1416 (*query)->sql_read,
1417 (*query)->sql_write,
1418 (*query)->sql_insert ? "\n\nInsert:\n" : "",
1419 (*query)->sql_insert ? (*query)->sql_insert : "");
1420 } else if ((*query)->sql_read) {
1421 ast_string_field_build((*query)->acf, desc,
1422 "Runs the following query, as defined in func_odbc.conf, performing\n"
1423 "substitution of the arguments into the query as specified by ${ARG1},\n"
1424 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s",
1425 (*query)->sql_read);
1426 } else if ((*query)->sql_write) {
1427 ast_string_field_build((*query)->acf, desc,
1428 "Runs the following query, as defined in func_odbc.conf, performing\n"
1429 "substitution of the arguments into the query as specified by ${ARG1},\n"
1430 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
1431 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1432 "This function may only be set.\n%s\nSQL:\n%s%s%s",
1433 (*query)->sql_insert ?
1434 "If the write query affects no rows, the insert query will be\n"
1435 "performed.\n" : "",
1436 (*query)->sql_write,
1437 (*query)->sql_insert ? "\n\nInsert:\n" : "",
1438 (*query)->sql_insert ? (*query)->sql_insert : "");
1439 } else {
1440 free_acf_query(*query);
1441 *query = NULL;
1442 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
1443 return EINVAL;
1444 }
1445
1446 if (ast_strlen_zero((*query)->acf->desc)) {
1447 free_acf_query(*query);
1448 *query = NULL;
1449 return ENOMEM;
1450 }
1451
1452 if ((*query)->sql_read) {
1453 (*query)->acf->read = acf_odbc_read;
1454 }
1455
1456 if ((*query)->sql_write) {
1457 (*query)->acf->write = acf_odbc_write;
1458 }
1459
1460 return 0;
1461}
1462
1463static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1464{
1466 AST_APP_ARG(field)[100];
1467 );
1468 struct ast_str *sql;
1469 char *char_args, varname[15];
1470 struct acf_odbc_query *query;
1471 struct ast_channel *chan;
1472 int i;
1473
1474 switch (cmd) {
1475 case CLI_INIT:
1476 e->command = "odbc read";
1477 e->usage =
1478 "Usage: odbc read <name> <args> [exec]\n"
1479 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1480 " optionally executes the function. This function is intended for\n"
1481 " testing purposes. Remember to quote arguments containing spaces.\n";
1482 return NULL;
1483 case CLI_GENERATE:
1484 if (a->pos == 2) {
1485 int wordlen = strlen(a->word), which = 0;
1486 /* Complete function name */
1488 AST_RWLIST_TRAVERSE(&queries, query, list) {
1489 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1490 if (++which > a->n) {
1491 char *res = ast_strdup(query->acf->name);
1493 return res;
1494 }
1495 }
1496 }
1498 return NULL;
1499 } else if (a->pos == 4) {
1500 static const char * const completions[] = { "exec", NULL };
1501 return ast_cli_complete(a->word, completions, a->n);
1502 } else {
1503 return NULL;
1504 }
1505 }
1506
1507 if (a->argc < 4 || a->argc > 5) {
1508 return CLI_SHOWUSAGE;
1509 }
1510
1511 sql = ast_str_thread_get(&sql_buf, 16);
1512 if (!sql) {
1513 return CLI_FAILURE;
1514 }
1515
1517 AST_RWLIST_TRAVERSE(&queries, query, list) {
1518 if (!strcmp(query->acf->name, a->argv[2])) {
1519 break;
1520 }
1521 }
1522
1523 if (!query) {
1524 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1526 return CLI_SHOWUSAGE;
1527 }
1528
1529 if (!query->sql_read) {
1530 ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
1532 return CLI_SUCCESS;
1533 }
1534
1535 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
1536
1537 /* Evaluate function */
1538 char_args = ast_strdupa(a->argv[3]);
1539
1540 chan = ast_dummy_channel_alloc();
1541 if (!chan) {
1543 return CLI_FAILURE;
1544 }
1545
1546 AST_STANDARD_APP_ARGS(args, char_args);
1547 for (i = 0; i < args.argc; i++) {
1548 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1549 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1550 }
1551
1552 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
1553 chan = ast_channel_unref(chan);
1554
1555 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
1556 /* Execute the query */
1557 struct odbc_obj *obj = NULL;
1558 struct dsn *dsn = NULL;
1559 int dsn_num, executed = 0;
1560 SQLHSTMT stmt;
1561 int rows = 0, res, x;
1562 SQLSMALLINT colcount = 0, collength;
1563 SQLLEN indicator, octetlength;
1564 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
1565 char colname[256];
1566
1567 if (!coldata) {
1569 return CLI_SUCCESS;
1570 }
1571
1572 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1573 if (ast_strlen_zero(query->readhandle[dsn_num])) {
1574 continue;
1575 }
1576 obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
1577 if (!obj) {
1578 continue;
1579 }
1580 ast_debug(1, "Found handle %s\n", query->readhandle[dsn_num]);
1581
1582 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1583 release_obj_or_dsn (&obj, &dsn);
1584 continue;
1585 }
1586
1587 executed = 1;
1588
1589 res = SQLNumResultCols(stmt, &colcount);
1590 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1591 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
1592 SQLCloseCursor(stmt);
1593 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1594 release_obj_or_dsn (&obj, &dsn);
1596 return CLI_SUCCESS;
1597 }
1598
1599 if (colcount <= 0) {
1600 SQLCloseCursor(stmt);
1601 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1602 release_obj_or_dsn (&obj, &dsn);
1603 ast_cli(a->fd, "Returned %d columns. Query executed on handle %d:%s [%s]\n", colcount, dsn_num, query->readhandle[dsn_num], ast_str_buffer(sql));
1605 return CLI_SUCCESS;
1606 }
1607
1608 res = SQLFetch(stmt);
1609 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1610 SQLCloseCursor(stmt);
1611 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1612 release_obj_or_dsn (&obj, &dsn);
1613 if (res == SQL_NO_DATA) {
1614 ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn_num, query->readhandle[dsn_num], ast_str_buffer(sql));
1615 break;
1616 } else {
1617 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1618 }
1620 return CLI_SUCCESS;
1621 }
1622 for (;;) {
1623 for (x = 0; x < colcount; x++) {
1624 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
1625 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
1626 snprintf(colname, sizeof(colname), "field%d", x);
1627 }
1628
1629 octetlength = 0;
1630 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
1631
1632 res = ast_odbc_ast_str_SQLGetData(&coldata, octetlength + 1, stmt, x + 1, SQL_CHAR, &indicator);
1633 if (indicator == SQL_NULL_DATA) {
1634 ast_str_set(&coldata, 0, "(nil)");
1635 res = SQL_SUCCESS;
1636 }
1637
1638 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1639 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
1640 SQLCloseCursor(stmt);
1641 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1642 release_obj_or_dsn (&obj, &dsn);
1644 return CLI_SUCCESS;
1645 }
1646
1647 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
1648 }
1649 rows++;
1650
1651 /* Get next row */
1652 res = SQLFetch(stmt);
1653 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1654 break;
1655 }
1656 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
1657 }
1658 SQLCloseCursor(stmt);
1659 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1660 release_obj_or_dsn (&obj, &dsn);
1661 ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn_num, query->readhandle[dsn_num]);
1662 break;
1663 }
1664 release_obj_or_dsn (&obj, &dsn);
1665
1666 if (!executed) {
1667 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
1668 }
1669 } else { /* No execution, just print out the resulting SQL */
1670 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1671 }
1673 return CLI_SUCCESS;
1674}
1675
1676static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1677{
1679 AST_APP_ARG(field)[100];
1680 );
1682 AST_APP_ARG(field)[100];
1683 );
1684 struct ast_str *sql;
1685 char *char_args, *char_values, varname[15];
1686 struct acf_odbc_query *query;
1687 struct ast_channel *chan;
1688 int i;
1689
1690 switch (cmd) {
1691 case CLI_INIT:
1692 e->command = "odbc write";
1693 e->usage =
1694 "Usage: odbc write <name> <args> <value> [exec]\n"
1695 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1696 " optionally executes the function. This function is intended for\n"
1697 " testing purposes. Remember to quote arguments containing spaces.\n";
1698 return NULL;
1699 case CLI_GENERATE:
1700 if (a->pos == 2) {
1701 int wordlen = strlen(a->word), which = 0;
1702 /* Complete function name */
1704 AST_RWLIST_TRAVERSE(&queries, query, list) {
1705 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1706 if (++which > a->n) {
1707 char *res = ast_strdup(query->acf->name);
1709 return res;
1710 }
1711 }
1712 }
1714 return NULL;
1715 } else if (a->pos == 5) {
1716 static const char * const completions[] = { "exec", NULL };
1717 return ast_cli_complete(a->word, completions, a->n);
1718 } else {
1719 return NULL;
1720 }
1721 }
1722
1723 if (a->argc < 5 || a->argc > 6) {
1724 return CLI_SHOWUSAGE;
1725 }
1726
1727 sql = ast_str_thread_get(&sql_buf, 16);
1728 if (!sql) {
1729 return CLI_FAILURE;
1730 }
1731
1733 AST_RWLIST_TRAVERSE(&queries, query, list) {
1734 if (!strcmp(query->acf->name, a->argv[2])) {
1735 break;
1736 }
1737 }
1738
1739 if (!query) {
1740 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1742 return CLI_SHOWUSAGE;
1743 }
1744
1745 if (!query->sql_write) {
1746 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
1748 return CLI_SUCCESS;
1749 }
1750
1751 /* FIXME: The code below duplicates code found in acf_odbc_write but
1752 * lacks the newer sql_insert additions. */
1753
1754 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
1755
1756 /* Evaluate function */
1757 char_args = ast_strdupa(a->argv[3]);
1758 char_values = ast_strdupa(a->argv[4]);
1759
1760 chan = ast_dummy_channel_alloc();
1761 if (!chan) {
1763 return CLI_FAILURE;
1764 }
1765
1766 AST_STANDARD_APP_ARGS(args, char_args);
1767 for (i = 0; i < args.argc; i++) {
1768 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1769 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1770 }
1771
1772 /* Parse values, just like arguments */
1773 AST_STANDARD_APP_ARGS(values, char_values);
1774 for (i = 0; i < values.argc; i++) {
1775 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
1776 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
1777 }
1778
1779 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
1780 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
1781 ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
1782 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
1783
1784 chan = ast_channel_unref(chan);
1785
1786 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
1787 /* Execute the query */
1788 struct odbc_obj *obj = NULL;
1789 struct dsn *dsn = NULL;
1790 int dsn_num, executed = 0;
1791 SQLHSTMT stmt;
1792 SQLLEN rows = -1;
1793
1794 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1795 if (ast_strlen_zero(query->writehandle[dsn_num])) {
1796 continue;
1797 }
1798 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
1799 if (!obj) {
1800 continue;
1801 }
1802 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1803 release_obj_or_dsn (&obj, &dsn);
1804 continue;
1805 }
1806
1807 SQLRowCount(stmt, &rows);
1808 SQLCloseCursor(stmt);
1809 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1810 release_obj_or_dsn (&obj, &dsn);
1811 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn_num, query->writehandle[dsn_num]);
1812 executed = 1;
1813 break;
1814 }
1815
1816 if (!executed) {
1817 ast_cli(a->fd, "Failed to execute query.\n");
1818 }
1819 } else { /* No execution, just print out the resulting SQL */
1820 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1821 }
1823 return CLI_SUCCESS;
1824}
1825
1826static struct ast_cli_entry cli_func_odbc[] = {
1827 AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
1828 AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
1829};
1830
1831static int load_module(void)
1832{
1833 int res = 0;
1834 struct ast_config *cfg;
1835 char *catg;
1836 const char *s;
1837 struct ast_flags config_flags = { 0 };
1838
1841
1842 cfg = ast_config_load(config, config_flags);
1843 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
1844 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
1846 }
1847
1849 if ((s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
1851 } else {
1853 }
1854
1855 dsns = NULL;
1856
1860 if (!dsns) {
1861 ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1864 }
1865 }
1867
1869 for (catg = ast_category_browse(cfg, NULL);
1870 catg;
1871 catg = ast_category_browse(cfg, catg)) {
1872 struct acf_odbc_query *query = NULL;
1873 int err;
1874
1875 if (!strcasecmp(catg, "general")) {
1876 continue;
1877 }
1878
1879 if ((err = init_acf_query(cfg, catg, &query))) {
1880 if (err == ENOMEM)
1881 ast_log(LOG_ERROR, "Out of memory\n");
1882 else if (err == EINVAL)
1883 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
1884 else
1885 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
1886 } else {
1887 AST_RWLIST_INSERT_HEAD(&queries, query, list);
1888 ast_custom_function_register(query->acf);
1889 }
1890 }
1891
1892 ast_config_destroy(cfg);
1896
1898 return res;
1899}
1900
1901static int unload_module(void)
1902{
1903 struct acf_odbc_query *query;
1904 int res = 0;
1905
1907 while (!AST_RWLIST_EMPTY(&queries)) {
1908 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
1910 free_acf_query(query);
1911 }
1912
1918
1919 /* Allow any threads waiting for this lock to pass (avoids a race) */
1921 usleep(1);
1923
1925
1926 if (dsns) {
1927 ao2_ref(dsns, -1);
1928 }
1929 return res;
1930}
1931
1932static int reload(void)
1933{
1934 int res = 0;
1935 struct ast_config *cfg;
1936 struct acf_odbc_query *oldquery;
1937 char *catg;
1938 const char *s;
1939 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
1940
1941 cfg = ast_config_load(config, config_flags);
1943 return 0;
1944
1946
1947 if (dsns) {
1948 ao2_ref(dsns, -1);
1949 dsns = NULL;
1950 }
1951
1952 if (cfg && (s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
1954 } else {
1956 }
1957
1961 if (!dsns) {
1962 ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1964 return 0;
1965 }
1966 }
1968
1970
1971 while (!AST_RWLIST_EMPTY(&queries)) {
1972 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
1973 ast_custom_function_unregister(oldquery->acf);
1974 free_acf_query(oldquery);
1975 }
1976
1977 if (!cfg) {
1978 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
1979 goto reload_out;
1980 }
1981
1982 for (catg = ast_category_browse(cfg, NULL);
1983 catg;
1984 catg = ast_category_browse(cfg, catg)) {
1985 struct acf_odbc_query *query = NULL;
1986
1987 if (!strcasecmp(catg, "general")) {
1988 continue;
1989 }
1990
1991 if (init_acf_query(cfg, catg, &query)) {
1992 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
1993 } else {
1994 AST_RWLIST_INSERT_HEAD(&queries, query, list);
1995 ast_custom_function_register(query->acf);
1996 }
1997 }
1998
1999 ast_config_destroy(cfg);
2000reload_out:
2002 return res;
2003}
2004
2005/* XXX need to revise usecount - set if query_lock is set */
2006
2008 .support_level = AST_MODULE_SUPPORT_CORE,
2009 .load = load_module,
2010 .unload = unload_module,
2011 .reload = reload,
2012 .requires = "res_odbc",
jack_status_t status
Definition: app_jack.c:146
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
@ OBJ_SEARCH_PARTIAL_KEY
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
@ OBJ_SEARCH_MASK
Search option field mask.
Definition: astobj2.h:1072
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static int tmp()
Definition: bt_open.c:389
static char * dsn
Definition: cdr_odbc.c:55
static const char desc[]
Definition: cdr_radius.c:84
static PGresult * result
Definition: cel_pgsql.c:84
General Asterisk PBX channel definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_channel_lock(chan)
Definition: channel.h:2922
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
#define ast_channel_unlock(chan)
Definition: channel.h:2923
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1841
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
static const char name[]
Definition: format_mp3.c:68
static char * synopsis
Definition: func_enum.c:154
odbc_option_flags
Definition: func_odbc.c:126
@ OPT_ESCAPECOMMAS
Definition: func_odbc.c:127
@ OPT_MULTIROW
Definition: func_odbc.c:128
struct ao2_container * dsns
Definition: func_odbc.c:176
static ast_rwlock_t single_db_connection_lock
Definition: func_odbc.c:124
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
Definition: func_odbc.c:471
static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
Definition: func_odbc.c:513
static struct ast_custom_function escape_function
Definition: func_odbc.c:1143
static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
Definition: func_odbc.c:526
static int exec_odbcfinish(struct ast_channel *chan, const char *data)
Definition: func_odbc.c:1206
static char * cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1676
static struct ast_custom_function escape_backslashes_function
Definition: func_odbc.c:1154
static struct ast_threadstorage colnames_buf
Definition: func_odbc.c:440
static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
Definition: func_odbc.c:518
static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
Definition: func_odbc.c:742
static const struct ast_datastore_info odbc_info
Definition: func_odbc.c:146
static int connection_dead(struct odbc_obj *connection)
Determine if the connection has died.
Definition: func_odbc.c:282
static struct ast_threadstorage sql2_buf
Definition: func_odbc.c:438
static char * cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1463
static struct ast_custom_function fetch_function
Definition: func_odbc.c:1198
static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
Definition: func_odbc.c:1237
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1160
static int dsn_cmp(void *obj, void *arg, int flags)
Definition: func_odbc.c:198
static char * config
Definition: func_odbc.c:118
static struct dsn * get_dsn(const char *name)
Retrieve a DSN, or create it if it does not exist.
Definition: func_odbc.c:322
static void odbc_datastore_free(void *data)
Definition: func_odbc.c:444
static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1138
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
Definition: func_odbc.c:1122
#define DEFAULT_SINGLE_DB_CONNECTION
Definition: func_odbc.c:120
static int dsn_hash(const void *obj, const int flags)
Definition: func_odbc.c:178
static char * app_odbcfinish
Definition: func_odbc.c:1204
static struct odbc_obj * get_odbc_obj(const char *dsn_name, struct dsn **dsn)
Get a DB handle via a DSN or directly.
Definition: func_odbc.c:376
static void dsn_destructor(void *obj)
Definition: func_odbc.c:227
static struct dsn * create_dsn(const char *name)
Create a DSN and connect to the database.
Definition: func_odbc.c:243
static struct ast_threadstorage coldata_buf
Definition: func_odbc.c:439
static int load_module(void)
Definition: func_odbc.c:1831
static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1149
static int unload_module(void)
Definition: func_odbc.c:1901
static int reload(void)
Definition: func_odbc.c:1932
static int single_db_connection
Definition: func_odbc.c:122
#define DSN_BUCKETS
Definition: func_odbc.c:174
static struct ast_cli_entry cli_func_odbc[]
Definition: func_odbc.c:1826
static int free_acf_query(struct acf_odbc_query *query)
Definition: func_odbc.c:1220
static int resultcount
Definition: func_odbc.c:435
static void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
Release an ODBC obj or a DSN.
Definition: func_odbc.c:405
static struct ast_threadstorage sql_buf
Definition: func_odbc.c:437
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
@ CONFIG_FLAG_FILEUNCHANGED
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
#define AST_RWLIST_EMPTY
Definition: linkedlists.h:452
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
Definition: linkedlists.h:333
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:626
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:718
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:415
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
#define ast_rwlock_wrlock(a)
Definition: lock.h:236
#define ast_rwlock_rdlock(a)
Definition: lock.h:235
#define AST_RWLOCK_DEFINE_STATIC(rwlock)
Definition: lock.h:543
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
#define ast_rwlock_unlock(a)
Definition: lock.h:234
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, without removing any previously set value.
ODBC resource manager.
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:804
unsigned int ast_odbc_get_max_connections(const char *name)
Return the current configured maximum number of connections for a class.
Definition: res_odbc.c:848
SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind)
Wrapper for SQLGetData to use with dynamic strings.
Definition: res_odbc.c:498
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
Definition: res_odbc.c:360
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:469
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
struct odbc_obj * ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
Retrieve an ODBC transaction connection with the given ODBC class name.
#define NULL
Definition: resample.c:96
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_str_append_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string, with escaping of commas.
Definition: strings.h:1076
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
#define ast_str_make_space(buf, new_len)
Definition: strings.h:828
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Generic container type.
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
const char * uid
Definition: datastore.h:65
void * data
Definition: datastore.h:66
Structure used to handle boolean flags.
Definition: utils.h:199
Support for dynamic strings.
Definition: strings.h:623
Data source name.
Definition: func_odbc.c:167
struct odbc_obj * connection
Definition: func_odbc.c:169
char name[0]
Definition: func_odbc.c:171
struct odbc_datastore_row::@175 list
char names[0]
Definition: func_odbc.c:160
ODBC container.
Definition: res_odbc.h:46
SQLHDBC con
Definition: res_odbc.h:47
int value
Definition: syslog.c:37
const char * args
static struct test_val a
static int indicator
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
FILE * out
Definition: utils/frame.c:33
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define ast_assert(a)
Definition: utils.h:739
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666