Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
52 <version>1.6.0</version>
53 </since>
54 <synopsis>
55 Fetch a row from a multirow query.
56 </synopsis>
57 <syntax>
58 <parameter name="result-id" required="true" />
59 </syntax>
60 <description>
61 <para>For queries which are marked as mode=multirow, the original
62 query returns a <replaceable>result-id</replaceable> from which results
63 may be fetched. This function implements the actual fetch of the results.</para>
64 <para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
65 <variablelist>
66 <variable name="ODBC_FETCH_STATUS">
67 <value name="SUCESS">
68 If rows are available.
69 </value>
70 <value name="FAILURE">
71 If no rows are available.
72 </value>
73 </variable>
74 </variablelist>
75 </description>
76 </function>
77 <application name="ODBCFinish" language="en_US">
78 <since>
79 <version>1.6.0</version>
80 </since>
81 <synopsis>
82 Clear the resultset of a sucessful multirow query.
83 </synopsis>
84 <syntax>
85 <parameter name="result-id" required="true" />
86 </syntax>
87 <description>
88 <para>For queries which are marked as mode=multirow, this will clear
89 any remaining rows of the specified resultset.</para>
90 </description>
91 </application>
92 <function name="SQL_ESC" language="en_US">
93 <since>
94 <version>1.4.0</version>
95 </since>
96 <synopsis>
97 Escapes single ticks for use in SQL statements.
98 </synopsis>
99 <syntax>
100 <parameter name="string" required="true" />
101 </syntax>
102 <description>
103 <para>Used in SQL templates to escape data which may contain single ticks
104 <literal>'</literal> which are otherwise used to delimit data.</para>
105 <example title="Escape example">
106 SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'
107 </example>
108 </description>
109 </function>
110 <function name="SQL_ESC_BACKSLASHES" language="en_US">
111 <since>
112 <version>16.26.0</version>
113 <version>18.12.0</version>
114 <version>19.4.0</version>
115 </since>
116 <synopsis>
117 Escapes backslashes for use in SQL statements.
118 </synopsis>
119 <syntax>
120 <parameter name="string" required="true" />
121 </syntax>
122 <description>
123 <para>Used in SQL templates to escape data which may contain backslashes
124 <literal></literal> which are otherwise used to escape data.</para>
125 <example title="Escape with backslashes example">
126 SELECT foo FROM bar WHERE baz='${SQL_ESC(${SQL_ESC_BACKSLASHES(${ARG1})})}'
127 </example>
128 </description>
129 </function>
130 ***/
131
132static char *config = "func_odbc.conf";
133
134#define DEFAULT_SINGLE_DB_CONNECTION 0
135
137
139
142 OPT_MULTIROW = (1 << 1),
143};
144
147 char readhandle[5][30];
148 char writehandle[5][30];
149 char *sql_read;
150 char *sql_write;
151 char *sql_insert;
152 unsigned int flags;
153 int rowlimit;
154 int minargs;
155 struct ast_custom_function *acf;
156};
157
158static void odbc_datastore_free(void *data);
159
160static const struct ast_datastore_info odbc_info = {
161 .type = "FUNC_ODBC",
162 .destroy = odbc_datastore_free,
163};
164
165/* For storing each result row */
168 char data[0];
169};
170
171/* For storing each result set */
174 char names[0];
175};
176
177/*! \brief Data source name
178 *
179 * This holds data that pertains to a DSN
180 */
181struct dsn {
182 /*! A connection to the database */
184 /*! The name of the DSN as defined in res_odbc.conf */
185 char name[0];
186};
187
188#define DSN_BUCKETS 37
189
191
192static int dsn_hash(const void *obj, const int flags)
193{
194 const struct dsn *object;
195 const char *key;
196
197 switch (flags & OBJ_SEARCH_MASK) {
198 case OBJ_SEARCH_KEY:
199 key = obj;
200 break;
202 object = obj;
203 key = object->name;
204 break;
205 default:
206 ast_assert(0);
207 return 0;
208 }
209 return ast_str_hash(key);
210}
211
212static int dsn_cmp(void *obj, void *arg, int flags)
213{
214 const struct dsn *object_left = obj;
215 const struct dsn *object_right = arg;
216 const char *right_key = arg;
217 int cmp;
218
219 switch (flags & OBJ_SEARCH_MASK) {
221 right_key = object_right->name;
222 /* Fall through */
223 case OBJ_SEARCH_KEY:
224 cmp = strcmp(object_left->name, right_key);
225 break;
227 cmp = strncmp(object_left->name, right_key, strlen(right_key));
228 break;
229 default:
230 cmp = 0;
231 break;
232 }
233
234 if (cmp) {
235 return 0;
236 }
237
238 return CMP_MATCH;
239}
240
241static void dsn_destructor(void *obj)
242{
243 struct dsn *dsn = obj;
244
245 if (dsn->connection) {
247 }
248}
249
250/*!
251 * \brief Create a DSN and connect to the database
252 *
253 * \param name The name of the DSN as found in res_odbc.conf
254 * \retval NULL Fail
255 * \retval non-NULL The newly-created structure
256 */
257static struct dsn *create_dsn(const char *name)
258{
259 struct dsn *dsn;
260
261 if (!dsns) {
262 return NULL;
263 }
264
265 dsn = ao2_alloc(sizeof(*dsn) + strlen(name) + 1, dsn_destructor);
266 if (!dsn) {
267 return NULL;
268 }
269
270 /* Safe */
271 strcpy(dsn->name, name);
272
274 if (!dsn->connection) {
275 ao2_ref(dsn, -1);
276 return NULL;
277 }
278
280 ao2_ref(dsn, -1);
281 return NULL;
282 }
283
284 return dsn;
285}
286
287static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data);
288
289/*!
290 * \brief Determine if the connection has died.
291 *
292 * \param connection The connection to check
293 * \retval 1 Yep, it's dead
294 * \retval 0 It's alive and well
295 */
297{
298 SQLINTEGER dead;
299 SQLRETURN res;
300 SQLHSTMT stmt;
301
302 if (!connection) {
303 return 1;
304 }
305
306 res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
307 if (SQL_SUCCEEDED(res)) {
308 return dead == SQL_CD_TRUE ? 1 : 0;
309 }
310
311 /* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a direct
312 * execute of a probing statement and see if that succeeds instead
313 */
315 if (!stmt) {
316 return 1;
317 }
318
319 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
320 return 0;
321}
322
323/*!
324 * \brief Retrieve a DSN, or create it if it does not exist.
325 *
326 * The created DSN is returned locked. This should be inconsequential
327 * to callers in most cases.
328 *
329 * When finished with the returned structure, the caller must call
330 * \ref release_obj_or_dsn
331 *
332 * \param name Name of the DSN as found in res_odbc.conf
333 * \retval NULL Unable to retrieve or create the DSN
334 * \retval non-NULL The retrieved/created locked DSN
335 */
336static struct dsn *get_dsn(const char *name)
337{
338 struct dsn *dsn;
339
340 if (!dsns) {
341 return NULL;
342 }
343
344 ao2_lock(dsns);
346 if (!dsn) {
348 }
350
351 if (!dsn) {
352 return NULL;
353 }
354
355 ao2_lock(dsn);
356 if (!dsn->connection) {
358 if (!dsn->connection) {
360 ao2_ref(dsn, -1);
361 return NULL;
362 }
363 return dsn;
364 }
365
369 if (!dsn->connection) {
371 ao2_ref(dsn, -1);
372 return NULL;
373 }
374 }
375
376 return dsn;
377}
378
379/*!
380 * \brief Get a DB handle via a DSN or directly
381 *
382 * If single db connection then get the DB handle via DSN
383 * else by requesting a connection directly
384 *
385 * \param dsn_name Name of the DSN as found in res_odbc.conf
386 * \param dsn The pointer to the DSN
387 * \retval NULL Unable to retrieve the DB handle
388 * \retval non-NULL The retrieved DB handle
389 */
390static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn)
391{
392 struct odbc_obj *obj = NULL;
393
396 if (dsn) {
397 *dsn = get_dsn(dsn_name);
398 if (*dsn) {
399 obj = (*dsn)->connection;
400 }
401 }
402 } else {
403 obj = ast_odbc_request_obj(dsn_name, 0);
404 }
406
407 return obj;
408}
409
410/*!
411 * \brief Release an ODBC obj or a DSN
412 *
413 * If single db connection then unlock and unreference the DSN
414 * else release the ODBC obj
415 *
416 * \param obj The pointer to the ODBC obj to release
417 * \param dsn The pointer to the dsn to unlock and unreference
418 */
419static inline void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
420{
421 if (dsn && *dsn) {
422 /* If multiple connections are not enabled then the guarantee
423 * of a single connection already exists and holding on to the
424 * connection would prevent any other user from acquiring it
425 * indefinitely.
426 */
427 if (ast_odbc_get_max_connections((*dsn)->name) < 2) {
428 ast_odbc_release_obj((*dsn)->connection);
429 (*dsn)->connection = NULL;
430 }
431 ao2_unlock(*dsn);
432 ao2_ref(*dsn, -1);
433 *dsn = NULL;
434 /* Some callers may provide both an obj and dsn. To ensure that
435 * the connection is not released twice we set it to NULL here if
436 * present.
437 */
438 if (obj) {
439 *obj = NULL;
440 }
441 } else if (obj && *obj) {
443 *obj = NULL;
444 }
445}
446
448
449static int resultcount = 0;
450
455
456static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
457
458static void odbc_datastore_free(void *data)
459{
460 struct odbc_datastore *result = data;
461 struct odbc_datastore_row *row;
462
463 if (!result) {
464 return;
465 }
466
468 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
469 ast_free(row);
470 }
474}
475
476/*!
477 * \brief Common execution function for SQL queries.
478 *
479 * \param obj DB connection
480 * \param data The query to execute
481 * \param silent If true, do not print warnings on failure
482 * \retval NULL Failed to execute query
483 * \retval non-NULL The executed statement
484 */
485static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
486{
487 int res;
488 char *sql = data;
489 SQLHSTMT stmt;
490
491 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
492 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
493 ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
494 return NULL;
495 }
496
497 res = ast_odbc_execute_sql(obj, stmt, sql);
498 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
499 if (res == SQL_ERROR && !silent) {
500 int i;
501 SQLINTEGER nativeerror=0, numfields=0;
502 SQLSMALLINT diagbytes=0;
503 unsigned char state[10], diagnostic[256];
504
505 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
506 for (i = 0; i < numfields; i++) {
507 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
508 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
509 if (i > 10) {
510 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
511 break;
512 }
513 }
514 }
515
516 if (!silent) {
517 ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
518 }
519 SQLCloseCursor(stmt);
520 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
521 return NULL;
522 }
523
524 return stmt;
525}
526
527static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
528{
529 return execute(obj, data, 0);
530}
531
532static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
533{
534 return execute(obj, data, 1);
535}
536
537/*
538 * Master control routine
539 */
540static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
541{
542 struct odbc_obj *obj = NULL;
543 struct acf_odbc_query *query;
544 char *t, varname[15];
545 int i, dsn_num, bogus_chan = 0;
546 int transactional = 0;
548 AST_APP_ARG(field)[100];
549 );
551 AST_APP_ARG(field)[100];
552 );
553 SQLHSTMT stmt = NULL;
554 SQLLEN rows=0;
555 struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
556 struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
557 const char *status = "FAILURE";
558 struct dsn *dsn = NULL;
559
560 if (!buf || !insertbuf) {
561 return -1;
562 }
563
565 AST_RWLIST_TRAVERSE(&queries, query, list) {
566 if (!strcmp(query->acf->name, cmd)) {
567 break;
568 }
569 }
570
571 if (!query) {
572 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
574 if (chan) {
575 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
576 }
577 return -1;
578 }
579
581 if (args.argc < query->minargs) {
582 ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
583 args.argc, cmd, query->minargs);
585 return -1;
586 }
587
588 if (!chan) {
589 if (!(chan = ast_dummy_channel_alloc())) {
591 return -1;
592 }
593 bogus_chan = 1;
594 }
595
596 if (!bogus_chan) {
598 }
599
600 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
601 /* We only get here if sql_write is set. sql_insert is optional however. */
602 if (query->sql_insert) {
603 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
604 }
605
606 /* Parse our arguments */
607 t = value ? ast_strdupa(value) : "";
608
609 if (!s || !t) {
610 ast_log(LOG_ERROR, "Out of memory\n");
612 if (!bogus_chan) {
614 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
615 } else {
616 ast_channel_unref(chan);
617 }
618 return -1;
619 }
620
621 snprintf(varname, sizeof(varname), "%u", args.argc);
622 pbx_builtin_pushvar_helper(chan, "ARGC", varname);
623 for (i = 0; i < args.argc; i++) {
624 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
625 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
626 }
627
628 /* Parse values, just like arguments */
630 for (i = 0; i < values.argc; i++) {
631 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
632 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
633 }
634
635 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
636 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
637
638 ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
639 if (query->sql_insert) {
640 ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
641 }
642
643 if (bogus_chan) {
644 chan = ast_channel_unref(chan);
645 } else {
646 /* Restore prior values */
647 pbx_builtin_setvar_helper(chan, "ARGC", NULL);
648
649 for (i = 0; i < args.argc; i++) {
650 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
651 pbx_builtin_setvar_helper(chan, varname, NULL);
652 }
653
654 for (i = 0; i < values.argc; i++) {
655 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
656 pbx_builtin_setvar_helper(chan, varname, NULL);
657 }
658 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
659 }
660
661 /*!\note
662 * Okay, this part is confusing. Transactions belong to a single database
663 * handle. Therefore, when working with transactions, we CANNOT failover
664 * to multiple DSNs. We MUST have a single handle all the way through the
665 * transaction, or else we CANNOT enforce atomicity.
666 */
667 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
668 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
669 if (transactional) {
670 /* This can only happen second time through or greater. */
671 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
672 }
673
674 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
675 transactional = 1;
676 } else {
677 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
678 transactional = 0;
679 }
680
681 if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
682 break;
683 }
684 if (!transactional) {
685 release_obj_or_dsn (&obj, &dsn);
686 }
687 }
688 }
689
690 if (stmt) {
691 SQLRowCount(stmt, &rows);
692 SQLCloseCursor(stmt);
693 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
694
695 if (rows != 0) {
696 status = "SUCCESS";
697
698 } else if (query->sql_insert) {
699 if (!transactional) {
700 release_obj_or_dsn (&obj, &dsn);
701 }
702
703 for (transactional = 0, dsn_num = 0; dsn_num < 5; dsn_num++) {
704 if (!ast_strlen_zero(query->writehandle[dsn_num])) {
705 if (transactional) {
706 /* This can only happen second time through or greater. */
707 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
708 } else {
709 release_obj_or_dsn (&obj, &dsn);
710 }
711
712 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
713 transactional = 1;
714 } else {
715 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
716 transactional = 0;
717 }
718 if (obj) {
720 }
721 }
722 if (stmt) {
723 status = "FAILOVER";
724 SQLRowCount(stmt, &rows);
725 SQLCloseCursor(stmt);
726 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
727 break;
728 }
729 }
730 }
731 }
732
734
735 /* Output the affected rows, for all cases. In the event of failure, we
736 * flag this as -1 rows. Note that this is different from 0 affected rows
737 * which would be the case if we succeeded in our query, but the values did
738 * not change. */
739 if (!bogus_chan) {
740 snprintf(varname, sizeof(varname), "%d", (int)rows);
741 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
742 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
743 }
744
745 if (!transactional) {
746 release_obj_or_dsn (&obj, &dsn);
747 }
748
749 if (!bogus_chan) {
751 }
752
753 return 0;
754}
755
756static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
757{
758 struct odbc_obj *obj = NULL;
759 struct acf_odbc_query *query;
760 char varname[15], rowcount[12] = "-1";
761 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
762 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn_num, bogus_chan = 0;
764 AST_APP_ARG(field)[100];
765 );
766 SQLHSTMT stmt = NULL;
767 SQLSMALLINT colcount=0;
768 SQLLEN indicator;
769 SQLSMALLINT collength;
770 struct odbc_datastore *resultset = NULL;
771 struct odbc_datastore_row *row = NULL;
772 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
773 const char *status = "FAILURE";
774 struct dsn *dsn = NULL;
775
776 if (!sql || !colnames) {
777 if (chan) {
778 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
779 }
780 return -1;
781 }
782
783 ast_str_reset(colnames);
784
786 AST_RWLIST_TRAVERSE(&queries, query, list) {
787 if (!strcmp(query->acf->name, cmd)) {
788 break;
789 }
790 }
791
792 if (!query) {
793 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
795 if (chan) {
796 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
797 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
798 }
799 return -1;
800 }
801
803 if (args.argc < query->minargs) {
804 ast_log(LOG_ERROR, "%d arguments supplied to '%s' requiring minimum %d\n",
805 args.argc, cmd, query->minargs);
807 return -1;
808 }
809
810 if (!chan) {
811 if (!(chan = ast_dummy_channel_alloc())) {
813 return -1;
814 }
815 bogus_chan = 1;
816 }
817
818 if (!bogus_chan) {
820 }
821
822 snprintf(varname, sizeof(varname), "%u", args.argc);
823 pbx_builtin_pushvar_helper(chan, "ARGC", varname);
824 for (x = 0; x < args.argc; x++) {
825 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
826 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
827 }
828
829 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
830
831 if (bogus_chan) {
832 chan = ast_channel_unref(chan);
833 } else {
834 /* Restore prior values */
835 pbx_builtin_setvar_helper(chan, "ARGC", NULL);
836
837 for (x = 0; x < args.argc; x++) {
838 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
839 pbx_builtin_setvar_helper(chan, varname, NULL);
840 }
841 }
842
843 /* Save these flags, so we can release the lock */
844 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
845 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
846 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
847 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
848 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
851 return -1;
852 }
853 AST_LIST_HEAD_INIT(resultset);
854 if (query->rowlimit) {
855 rowlimit = query->rowlimit;
856 } else {
857 rowlimit = INT_MAX;
858 }
859 multirow = 1;
860 } else if (!bogus_chan) {
861 if (query->rowlimit > 1) {
862 rowlimit = query->rowlimit;
863 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
864 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
865 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
868 return -1;
869 }
870 AST_LIST_HEAD_INIT(resultset);
871 }
872 }
874
875 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
876 if (!ast_strlen_zero(query->readhandle[dsn_num])) {
877 obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
878 if (!obj) {
879 continue;
880 }
882 }
883 if (stmt) {
884 break;
885 }
886 release_obj_or_dsn (&obj, &dsn);
887 }
888
889 if (!stmt) {
890 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
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 res = SQLNumResultCols(stmt, &colcount);
901 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
902 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
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", rowcount);
909 }
910 odbc_datastore_free(resultset);
911 return -1;
912 }
913
914 if (colcount <= 0) {
915 ast_verb(4, "Returned %d columns [%s]\n", colcount, ast_str_buffer(sql));
916 buf[0] = '\0';
917 SQLCloseCursor(stmt);
918 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
919 release_obj_or_dsn (&obj, &dsn);
920 if (!bogus_chan) {
921 pbx_builtin_setvar_helper(chan, "ODBCROWS", "0");
922 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "NODATA");
924 }
925 odbc_datastore_free(resultset);
926 return 0;
927 }
928
929 res = SQLFetch(stmt);
930 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
931 int res1 = -1;
932 if (res == SQL_NO_DATA) {
933 ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
934 res1 = 0;
935 buf[0] = '\0';
936 ast_copy_string(rowcount, "0", sizeof(rowcount));
937 status = "NODATA";
938 } else {
939 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
940 status = "FETCHERROR";
941 }
942 SQLCloseCursor(stmt);
943 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
944 release_obj_or_dsn (&obj, &dsn);
945 if (!bogus_chan) {
946 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
947 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
949 }
950 odbc_datastore_free(resultset);
951 return res1;
952 }
953
954 status = "SUCCESS";
955
956 for (y = 0; y < rowlimit; y++) {
957 buf[0] = '\0';
958 for (x = 0; x < colcount; x++) {
959 int i;
960 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
961 char *ptrcoldata;
962
963 if (!coldata) {
964 odbc_datastore_free(resultset);
965 SQLCloseCursor(stmt);
966 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
967 release_obj_or_dsn (&obj, &dsn);
968 if (!bogus_chan) {
969 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
971 }
972 return -1;
973 }
974
975 if (y == 0) {
976 char colname[256];
977 SQLLEN octetlength = 0;
978
979 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
980 ast_debug(3, "Got collength of %d for column '%s' (offset %d)\n", (int)collength, colname, x);
981 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
982 snprintf(colname, sizeof(colname), "field%d", x);
983 }
984
985 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
986
987 ast_str_make_space(&coldata, octetlength + 1);
988
989 if (ast_str_strlen(colnames)) {
990 ast_str_append(&colnames, 0, ",");
991 }
992 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
993
994 if (resultset) {
995 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
996 if (!tmp) {
997 ast_log(LOG_ERROR, "No space for a new resultset?\n");
998 odbc_datastore_free(resultset);
999 SQLCloseCursor(stmt);
1000 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1001 release_obj_or_dsn (&obj, &dsn);
1002 if (!bogus_chan) {
1003 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
1004 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
1006 }
1007 return -1;
1008 }
1009 resultset = tmp;
1010 strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
1011 }
1012 }
1013
1014 buflen = strlen(buf);
1015 res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
1016 if (indicator == SQL_NULL_DATA) {
1017 ast_debug(3, "Got NULL data\n");
1018 ast_str_reset(coldata);
1019 res = SQL_SUCCESS;
1020 }
1021
1022 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1023 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
1024 y = -1;
1025 buf[0] = '\0';
1026 goto end_acf_read;
1027 }
1028
1029 ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
1030
1031 if (x) {
1032 buf[buflen++] = ',';
1033 }
1034
1035 /* Copy data, encoding '\' and ',' for the argument parser */
1036 ptrcoldata = ast_str_buffer(coldata);
1037 for (i = 0; i < ast_str_strlen(coldata); i++) {
1038 if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
1039 buf[buflen++] = '\\';
1040 }
1041 buf[buflen++] = ptrcoldata[i];
1042
1043 if (buflen >= len - 2) {
1044 break;
1045 }
1046
1047 if (ptrcoldata[i] == '\0') {
1048 break;
1049 }
1050 }
1051
1052 buf[buflen] = '\0';
1053 ast_debug(2, "buf is now set to '%s'\n", buf);
1054 }
1055 ast_debug(2, "buf is now set to '%s'\n", buf);
1056
1057 if (resultset) {
1058 row = ast_calloc(1, sizeof(*row) + buflen + 1);
1059 if (!row) {
1060 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
1061 status = "MEMERROR";
1062 goto end_acf_read;
1063 }
1064 strcpy((char *)row + sizeof(*row), buf);
1065 AST_LIST_INSERT_TAIL(resultset, row, list);
1066
1067 /* Get next row */
1068 res = SQLFetch(stmt);
1069 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1070 if (res != SQL_NO_DATA) {
1071 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1072 }
1073 /* Number of rows in the resultset */
1074 y++;
1075 break;
1076 }
1077 }
1078 }
1079
1080end_acf_read:
1081 if (!bogus_chan) {
1082 snprintf(rowcount, sizeof(rowcount), "%d", y);
1083 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
1084 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
1085 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
1086 if (resultset) {
1087 struct ast_datastore *odbc_store;
1088 if (multirow) {
1089 int uid;
1091 snprintf(buf, len, "%d", uid);
1092 } else {
1093 /* Name of the query is name of the resultset */
1094 ast_copy_string(buf, cmd, len);
1095
1096 /* If there's one with the same name already, free it */
1097 ast_channel_lock(chan);
1098 if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
1099 ast_channel_datastore_remove(chan, odbc_store);
1100 ast_datastore_free(odbc_store);
1101 }
1102 ast_channel_unlock(chan);
1103 }
1104 odbc_store = ast_datastore_alloc(&odbc_info, buf);
1105 if (!odbc_store) {
1106 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
1107 odbc_datastore_free(resultset);
1108 SQLCloseCursor(stmt);
1109 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1110 release_obj_or_dsn (&obj, &dsn);
1111 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
1113 return -1;
1114 }
1115 odbc_store->data = resultset;
1116 ast_channel_lock(chan);
1117 ast_channel_datastore_add(chan, odbc_store);
1118 ast_channel_unlock(chan);
1119 }
1120 }
1121 SQLCloseCursor(stmt);
1122 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1123 release_obj_or_dsn (&obj, &dsn);
1124 if (resultset && !multirow) {
1125 /* Fetch the first resultset */
1126 if (!acf_fetch(chan, "", buf, buf, len)) {
1127 buf[0] = '\0';
1128 }
1129 }
1130 if (!bogus_chan) {
1132 }
1133 return 0;
1134}
1135
1136static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
1137{
1138 char *out = buf;
1139
1140 for (; *data && out - buf < len; data++) {
1141 if (*data == character) {
1142 *out = character;
1143 out++;
1144 }
1145 *out++ = *data;
1146 }
1147 *out = '\0';
1148
1149 return 0;
1150}
1151
1152static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1153{
1154 return acf_escape(chan, cmd, data, buf, len, '\'');
1155}
1156
1158 .name = "SQL_ESC",
1159 .read = acf_escape_ticks,
1160 .write = NULL,
1161};
1162
1163static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1164{
1165 return acf_escape(chan, cmd, data, buf, len, '\\');
1166}
1167
1169 .name = "SQL_ESC_BACKSLASHES",
1170 .read = acf_escape_backslashes,
1171 .write = NULL,
1172};
1173
1174static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1175{
1176 struct ast_datastore *store;
1177 struct odbc_datastore *resultset;
1178 struct odbc_datastore_row *row;
1179
1180 if (!chan) {
1181 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1182 return -1;
1183 }
1184
1185 ast_channel_lock(chan);
1186 store = ast_channel_datastore_find(chan, &odbc_info, data);
1187 if (!store) {
1188 ast_channel_unlock(chan);
1189 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
1190 return -1;
1191 }
1192 resultset = store->data;
1193 AST_LIST_LOCK(resultset);
1194 row = AST_LIST_REMOVE_HEAD(resultset, list);
1195 AST_LIST_UNLOCK(resultset);
1196 if (!row) {
1197 /* Cleanup datastore */
1198 ast_channel_datastore_remove(chan, store);
1199 ast_datastore_free(store);
1200 ast_channel_unlock(chan);
1201 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
1202 return -1;
1203 }
1204 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
1205 ast_channel_unlock(chan);
1206 ast_copy_string(buf, row->data, len);
1207 ast_free(row);
1208 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
1209 return 0;
1210}
1211
1213 .name = "ODBC_FETCH",
1214 .read = acf_fetch,
1215 .write = NULL,
1216};
1217
1218static char *app_odbcfinish = "ODBCFinish";
1219
1220static int exec_odbcfinish(struct ast_channel *chan, const char *data)
1221{
1222 struct ast_datastore *store;
1223
1224 ast_channel_lock(chan);
1225 store = ast_channel_datastore_find(chan, &odbc_info, data);
1226 if (store) {
1227 ast_channel_datastore_remove(chan, store);
1228 ast_datastore_free(store);
1229 }
1230 ast_channel_unlock(chan);
1231 return 0;
1232}
1233
1234static int free_acf_query(struct acf_odbc_query *query)
1235{
1236 if (query) {
1237 if (query->acf) {
1238 if (query->acf->name)
1239 ast_free((char *)query->acf->name);
1240 ast_string_field_free_memory(query->acf);
1241 ast_free(query->acf);
1242 }
1243 ast_free(query->sql_read);
1244 ast_free(query->sql_write);
1245 ast_free(query->sql_insert);
1246 ast_free(query);
1247 }
1248 return 0;
1249}
1250
1251static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
1252{
1253 const char *tmp;
1254 const char *tmp2 = NULL;
1255 int i;
1256
1257 if (!cfg || !catg) {
1258 return EINVAL;
1259 }
1260
1261 if (!(*query = ast_calloc(1, sizeof(**query)))) {
1262 return ENOMEM;
1263 }
1264
1265 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
1266 char *tmp2 = ast_strdupa(tmp);
1267 AST_DECLARE_APP_ARGS(writeconf,
1268 AST_APP_ARG(dsn)[5];
1269 );
1270 AST_STANDARD_APP_ARGS(writeconf, tmp2);
1271 for (i = 0; i < 5; i++) {
1272 if (!ast_strlen_zero(writeconf.dsn[i]))
1273 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
1274 }
1275 }
1276
1277 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
1278 char *tmp2 = ast_strdupa(tmp);
1279 AST_DECLARE_APP_ARGS(readconf,
1280 AST_APP_ARG(dsn)[5];
1281 );
1282 AST_STANDARD_APP_ARGS(readconf, tmp2);
1283 for (i = 0; i < 5; i++) {
1284 if (!ast_strlen_zero(readconf.dsn[i]))
1285 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
1286 }
1287 } else {
1288 /* If no separate readhandle, then use the writehandle for reading */
1289 for (i = 0; i < 5; i++) {
1290 if (!ast_strlen_zero((*query)->writehandle[i]))
1291 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
1292 }
1293 }
1294
1295 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")) ||
1296 (tmp2 = ast_variable_retrieve(cfg, catg, "read"))) {
1297 if (!tmp) {
1298 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
1299 tmp = tmp2;
1300 }
1301 if (*tmp != '\0') { /* non-empty string */
1302 if (!((*query)->sql_read = ast_strdup(tmp))) {
1303 free_acf_query(*query);
1304 *query = NULL;
1305 return ENOMEM;
1306 }
1307 }
1308 }
1309
1310 if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
1311 free_acf_query(*query);
1312 *query = NULL;
1313 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
1314 return EINVAL;
1315 }
1316
1317 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")) ||
1318 (tmp2 = ast_variable_retrieve(cfg, catg, "write"))) {
1319 if (!tmp) {
1320 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
1321 tmp = tmp2;
1322 }
1323 if (*tmp != '\0') { /* non-empty string */
1324 if (!((*query)->sql_write = ast_strdup(tmp))) {
1325 free_acf_query(*query);
1326 *query = NULL;
1327 return ENOMEM;
1328 }
1329 }
1330 }
1331
1332 if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
1333 free_acf_query(*query);
1334 *query = NULL;
1335 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
1336 return EINVAL;
1337 }
1338
1339 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
1340 if (*tmp != '\0') { /* non-empty string */
1341 if (!((*query)->sql_insert = ast_strdup(tmp))) {
1342 free_acf_query(*query);
1343 *query = NULL;
1344 return ENOMEM;
1345 }
1346 }
1347 }
1348
1349 /* Allow escaping of embedded commas in fields to be turned off */
1350 ast_set_flag((*query), OPT_ESCAPECOMMAS);
1351 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
1352 if (ast_false(tmp))
1354 }
1355
1356 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
1357 if (strcasecmp(tmp, "multirow") == 0)
1358 ast_set_flag((*query), OPT_MULTIROW);
1359 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
1360 sscanf(tmp, "%30d", &((*query)->rowlimit));
1361 }
1362
1363 if ((tmp = ast_variable_retrieve(cfg, catg, "minargs"))) {
1364 sscanf(tmp, "%30d", &((*query)->minargs));
1365 }
1366
1367 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
1368 if (!(*query)->acf) {
1369 free_acf_query(*query);
1370 *query = NULL;
1371 return ENOMEM;
1372 }
1373 if (ast_string_field_init((*query)->acf, 128)) {
1374 free_acf_query(*query);
1375 *query = NULL;
1376 return ENOMEM;
1377 }
1378
1379 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
1380 if (ast_asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
1381 (*query)->acf->name = NULL;
1382 }
1383 } else {
1384 if (ast_asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
1385 (*query)->acf->name = NULL;
1386 }
1387 }
1388
1389 if (!(*query)->acf->name) {
1390 free_acf_query(*query);
1391 *query = NULL;
1392 return ENOMEM;
1393 }
1394
1395 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
1396 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
1397 } else {
1398 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
1399 }
1400
1401 if (ast_strlen_zero((*query)->acf->syntax)) {
1402 free_acf_query(*query);
1403 *query = NULL;
1404 return ENOMEM;
1405 }
1406
1407 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
1408 ast_string_field_set((*query)->acf, synopsis, tmp);
1409 } else {
1410 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
1411 }
1412
1413 if (ast_strlen_zero((*query)->acf->synopsis)) {
1414 free_acf_query(*query);
1415 *query = NULL;
1416 return ENOMEM;
1417 }
1418
1419 if ((*query)->sql_read && (*query)->sql_write) {
1420 ast_string_field_build((*query)->acf, desc,
1421 "Runs the following query, as defined in func_odbc.conf, performing\n"
1422 "substitution of the arguments into the query as specified by ${ARG1},\n"
1423 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
1424 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1425 "%s"
1426 "\nRead:\n%s\n\nWrite:\n%s%s%s",
1427 (*query)->sql_insert ?
1428 "If the write query affects no rows, the insert query will be\n"
1429 "performed.\n" : "",
1430 (*query)->sql_read,
1431 (*query)->sql_write,
1432 (*query)->sql_insert ? "\n\nInsert:\n" : "",
1433 (*query)->sql_insert ? (*query)->sql_insert : "");
1434 } else if ((*query)->sql_read) {
1435 ast_string_field_build((*query)->acf, desc,
1436 "Runs the following query, as defined in func_odbc.conf, performing\n"
1437 "substitution of the arguments into the query as specified by ${ARG1},\n"
1438 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s",
1439 (*query)->sql_read);
1440 } else if ((*query)->sql_write) {
1441 ast_string_field_build((*query)->acf, desc,
1442 "Runs the following query, as defined in func_odbc.conf, performing\n"
1443 "substitution of the arguments into the query as specified by ${ARG1},\n"
1444 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
1445 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1446 "This function may only be set.\n%s\nSQL:\n%s%s%s",
1447 (*query)->sql_insert ?
1448 "If the write query affects no rows, the insert query will be\n"
1449 "performed.\n" : "",
1450 (*query)->sql_write,
1451 (*query)->sql_insert ? "\n\nInsert:\n" : "",
1452 (*query)->sql_insert ? (*query)->sql_insert : "");
1453 } else {
1454 free_acf_query(*query);
1455 *query = NULL;
1456 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
1457 return EINVAL;
1458 }
1459
1460 if (ast_strlen_zero((*query)->acf->desc)) {
1461 free_acf_query(*query);
1462 *query = NULL;
1463 return ENOMEM;
1464 }
1465
1466 if ((*query)->sql_read) {
1467 (*query)->acf->read = acf_odbc_read;
1468 }
1469
1470 if ((*query)->sql_write) {
1471 (*query)->acf->write = acf_odbc_write;
1472 }
1473
1474 return 0;
1475}
1476
1477static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1478{
1480 AST_APP_ARG(field)[100];
1481 );
1482 struct ast_str *sql;
1483 char *char_args, varname[15];
1484 struct acf_odbc_query *query;
1485 struct ast_channel *chan;
1486 int i;
1487
1488 switch (cmd) {
1489 case CLI_INIT:
1490 e->command = "odbc read";
1491 e->usage =
1492 "Usage: odbc read <name> <args> [exec]\n"
1493 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1494 " optionally executes the function. This function is intended for\n"
1495 " testing purposes. Remember to quote arguments containing spaces.\n";
1496 return NULL;
1497 case CLI_GENERATE:
1498 if (a->pos == 2) {
1499 int wordlen = strlen(a->word), which = 0;
1500 /* Complete function name */
1502 AST_RWLIST_TRAVERSE(&queries, query, list) {
1503 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1504 if (++which > a->n) {
1505 char *res = ast_strdup(query->acf->name);
1507 return res;
1508 }
1509 }
1510 }
1512 return NULL;
1513 } else if (a->pos == 4) {
1514 static const char * const completions[] = { "exec", NULL };
1515 return ast_cli_complete(a->word, completions, a->n);
1516 } else {
1517 return NULL;
1518 }
1519 }
1520
1521 if (a->argc < 4 || a->argc > 5) {
1522 return CLI_SHOWUSAGE;
1523 }
1524
1525 sql = ast_str_thread_get(&sql_buf, 16);
1526 if (!sql) {
1527 return CLI_FAILURE;
1528 }
1529
1531 AST_RWLIST_TRAVERSE(&queries, query, list) {
1532 if (!strcmp(query->acf->name, a->argv[2])) {
1533 break;
1534 }
1535 }
1536
1537 if (!query) {
1538 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1540 return CLI_SHOWUSAGE;
1541 }
1542
1543 if (!query->sql_read) {
1544 ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
1546 return CLI_SUCCESS;
1547 }
1548
1549 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
1550
1551 /* Evaluate function */
1552 char_args = ast_strdupa(a->argv[3]);
1553
1554 chan = ast_dummy_channel_alloc();
1555 if (!chan) {
1557 return CLI_FAILURE;
1558 }
1559
1560 AST_STANDARD_APP_ARGS(args, char_args);
1561 for (i = 0; i < args.argc; i++) {
1562 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1563 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1564 }
1565
1566 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
1567 chan = ast_channel_unref(chan);
1568
1569 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
1570 /* Execute the query */
1571 struct odbc_obj *obj = NULL;
1572 struct dsn *dsn = NULL;
1573 int dsn_num, executed = 0;
1574 SQLHSTMT stmt;
1575 int rows = 0, res, x;
1576 SQLSMALLINT colcount = 0, collength;
1577 SQLLEN indicator, octetlength;
1578 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
1579 char colname[256];
1580
1581 if (!coldata) {
1583 return CLI_SUCCESS;
1584 }
1585
1586 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1587 if (ast_strlen_zero(query->readhandle[dsn_num])) {
1588 continue;
1589 }
1590 obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
1591 if (!obj) {
1592 continue;
1593 }
1594 ast_debug(1, "Found handle %s\n", query->readhandle[dsn_num]);
1595
1596 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1597 release_obj_or_dsn (&obj, &dsn);
1598 continue;
1599 }
1600
1601 executed = 1;
1602
1603 res = SQLNumResultCols(stmt, &colcount);
1604 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1605 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
1606 SQLCloseCursor(stmt);
1607 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1608 release_obj_or_dsn (&obj, &dsn);
1610 return CLI_SUCCESS;
1611 }
1612
1613 if (colcount <= 0) {
1614 SQLCloseCursor(stmt);
1615 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1616 release_obj_or_dsn (&obj, &dsn);
1617 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));
1619 return CLI_SUCCESS;
1620 }
1621
1622 res = SQLFetch(stmt);
1623 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1624 SQLCloseCursor(stmt);
1625 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1626 release_obj_or_dsn (&obj, &dsn);
1627 if (res == SQL_NO_DATA) {
1628 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));
1629 break;
1630 } else {
1631 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1632 }
1634 return CLI_SUCCESS;
1635 }
1636 for (;;) {
1637 for (x = 0; x < colcount; x++) {
1638 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL);
1639 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
1640 snprintf(colname, sizeof(colname), "field%d", x);
1641 }
1642
1643 octetlength = 0;
1644 SQLColAttribute(stmt, x + 1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &octetlength);
1645
1646 res = ast_odbc_ast_str_SQLGetData(&coldata, octetlength + 1, stmt, x + 1, SQL_CHAR, &indicator);
1647 if (indicator == SQL_NULL_DATA) {
1648 ast_str_set(&coldata, 0, "(nil)");
1649 res = SQL_SUCCESS;
1650 }
1651
1652 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1653 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
1654 SQLCloseCursor(stmt);
1655 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1656 release_obj_or_dsn (&obj, &dsn);
1658 return CLI_SUCCESS;
1659 }
1660
1661 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
1662 }
1663 rows++;
1664
1665 /* Get next row */
1666 res = SQLFetch(stmt);
1667 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1668 break;
1669 }
1670 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
1671 }
1672 SQLCloseCursor(stmt);
1673 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1674 release_obj_or_dsn (&obj, &dsn);
1675 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]);
1676 break;
1677 }
1678 release_obj_or_dsn (&obj, &dsn);
1679
1680 if (!executed) {
1681 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
1682 }
1683 } else { /* No execution, just print out the resulting SQL */
1684 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1685 }
1687 return CLI_SUCCESS;
1688}
1689
1690static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1691{
1693 AST_APP_ARG(field)[100];
1694 );
1696 AST_APP_ARG(field)[100];
1697 );
1698 struct ast_str *sql;
1699 char *char_args, *char_values, varname[15];
1700 struct acf_odbc_query *query;
1701 struct ast_channel *chan;
1702 int i;
1703
1704 switch (cmd) {
1705 case CLI_INIT:
1706 e->command = "odbc write";
1707 e->usage =
1708 "Usage: odbc write <name> <args> <value> [exec]\n"
1709 " Evaluates the SQL provided in the ODBC function <name>, and\n"
1710 " optionally executes the function. This function is intended for\n"
1711 " testing purposes. Remember to quote arguments containing spaces.\n";
1712 return NULL;
1713 case CLI_GENERATE:
1714 if (a->pos == 2) {
1715 int wordlen = strlen(a->word), which = 0;
1716 /* Complete function name */
1718 AST_RWLIST_TRAVERSE(&queries, query, list) {
1719 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1720 if (++which > a->n) {
1721 char *res = ast_strdup(query->acf->name);
1723 return res;
1724 }
1725 }
1726 }
1728 return NULL;
1729 } else if (a->pos == 5) {
1730 static const char * const completions[] = { "exec", NULL };
1731 return ast_cli_complete(a->word, completions, a->n);
1732 } else {
1733 return NULL;
1734 }
1735 }
1736
1737 if (a->argc < 5 || a->argc > 6) {
1738 return CLI_SHOWUSAGE;
1739 }
1740
1741 sql = ast_str_thread_get(&sql_buf, 16);
1742 if (!sql) {
1743 return CLI_FAILURE;
1744 }
1745
1747 AST_RWLIST_TRAVERSE(&queries, query, list) {
1748 if (!strcmp(query->acf->name, a->argv[2])) {
1749 break;
1750 }
1751 }
1752
1753 if (!query) {
1754 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1756 return CLI_SHOWUSAGE;
1757 }
1758
1759 if (!query->sql_write) {
1760 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
1762 return CLI_SUCCESS;
1763 }
1764
1765 /* FIXME: The code below duplicates code found in acf_odbc_write but
1766 * lacks the newer sql_insert additions. */
1767
1768 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
1769
1770 /* Evaluate function */
1771 char_args = ast_strdupa(a->argv[3]);
1772 char_values = ast_strdupa(a->argv[4]);
1773
1774 chan = ast_dummy_channel_alloc();
1775 if (!chan) {
1777 return CLI_FAILURE;
1778 }
1779
1780 AST_STANDARD_APP_ARGS(args, char_args);
1781 for (i = 0; i < args.argc; i++) {
1782 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1783 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1784 }
1785
1786 /* Parse values, just like arguments */
1787 AST_STANDARD_APP_ARGS(values, char_values);
1788 for (i = 0; i < values.argc; i++) {
1789 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
1790 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
1791 }
1792
1793 /* Additionally set the value as a whole (but push an empty string if value is NULL) */
1794 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
1795 ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
1796 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
1797
1798 chan = ast_channel_unref(chan);
1799
1800 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
1801 /* Execute the query */
1802 struct odbc_obj *obj = NULL;
1803 struct dsn *dsn = NULL;
1804 int dsn_num, executed = 0;
1805 SQLHSTMT stmt;
1806 SQLLEN rows = -1;
1807
1808 for (dsn_num = 0; dsn_num < 5; dsn_num++) {
1809 if (ast_strlen_zero(query->writehandle[dsn_num])) {
1810 continue;
1811 }
1812 obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
1813 if (!obj) {
1814 continue;
1815 }
1816 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1817 release_obj_or_dsn (&obj, &dsn);
1818 continue;
1819 }
1820
1821 SQLRowCount(stmt, &rows);
1822 SQLCloseCursor(stmt);
1823 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1824 release_obj_or_dsn (&obj, &dsn);
1825 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn_num, query->writehandle[dsn_num]);
1826 executed = 1;
1827 break;
1828 }
1829
1830 if (!executed) {
1831 ast_cli(a->fd, "Failed to execute query.\n");
1832 }
1833 } else { /* No execution, just print out the resulting SQL */
1834 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1835 }
1837 return CLI_SUCCESS;
1838}
1839
1840static struct ast_cli_entry cli_func_odbc[] = {
1841 AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
1842 AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
1843};
1844
1845static int load_module(void)
1846{
1847 int res = 0;
1848 struct ast_config *cfg;
1849 char *catg;
1850 const char *s;
1851 struct ast_flags config_flags = { 0 };
1852
1855
1856 cfg = ast_config_load(config, config_flags);
1857 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
1858 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
1860 }
1861
1863 if ((s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
1865 } else {
1867 }
1868
1869 dsns = NULL;
1870
1874 if (!dsns) {
1875 ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1878 }
1879 }
1881
1883 for (catg = ast_category_browse(cfg, NULL);
1884 catg;
1885 catg = ast_category_browse(cfg, catg)) {
1886 struct acf_odbc_query *query = NULL;
1887 int err;
1888
1889 if (!strcasecmp(catg, "general")) {
1890 continue;
1891 }
1892
1893 if ((err = init_acf_query(cfg, catg, &query))) {
1894 if (err == ENOMEM)
1895 ast_log(LOG_ERROR, "Out of memory\n");
1896 else if (err == EINVAL)
1897 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
1898 else
1899 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
1900 } else {
1901 AST_RWLIST_INSERT_HEAD(&queries, query, list);
1902 ast_custom_function_register(query->acf);
1903 }
1904 }
1905
1906 ast_config_destroy(cfg);
1910
1912 return res;
1913}
1914
1915static int unload_module(void)
1916{
1917 struct acf_odbc_query *query;
1918 int res = 0;
1919
1921 while (!AST_RWLIST_EMPTY(&queries)) {
1922 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
1924 free_acf_query(query);
1925 }
1926
1932
1933 /* Allow any threads waiting for this lock to pass (avoids a race) */
1935 usleep(1);
1937
1939
1940 if (dsns) {
1941 ao2_ref(dsns, -1);
1942 }
1943 return res;
1944}
1945
1946static int reload(void)
1947{
1948 int res = 0;
1949 struct ast_config *cfg;
1950 struct acf_odbc_query *oldquery;
1951 char *catg;
1952 const char *s;
1953 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
1954
1955 cfg = ast_config_load(config, config_flags);
1957 return 0;
1958
1960
1961 if (dsns) {
1962 ao2_ref(dsns, -1);
1963 dsns = NULL;
1964 }
1965
1966 if (cfg && (s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
1968 } else {
1970 }
1971
1975 if (!dsns) {
1976 ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1978 return 0;
1979 }
1980 }
1982
1984
1985 while (!AST_RWLIST_EMPTY(&queries)) {
1986 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
1987 ast_custom_function_unregister(oldquery->acf);
1988 free_acf_query(oldquery);
1989 }
1990
1991 if (!cfg) {
1992 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
1993 goto reload_out;
1994 }
1995
1996 for (catg = ast_category_browse(cfg, NULL);
1997 catg;
1998 catg = ast_category_browse(cfg, catg)) {
1999 struct acf_odbc_query *query = NULL;
2000
2001 if (!strcasecmp(catg, "general")) {
2002 continue;
2003 }
2004
2005 if (init_acf_query(cfg, catg, &query)) {
2006 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
2007 } else {
2008 AST_RWLIST_INSERT_HEAD(&queries, query, list);
2009 ast_custom_function_register(query->acf);
2010 }
2011 }
2012
2013 ast_config_destroy(cfg);
2014reload_out:
2016 return res;
2017}
2018
2019/* XXX need to revise usecount - set if query_lock is set */
2020
2022 .support_level = AST_MODULE_SUPPORT_CORE,
2023 .load = load_module,
2024 .unload = unload_module,
2025 .reload = reload,
2026 .requires = "res_odbc",
jack_status_t status
Definition: app_jack.c:149
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 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:2414
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2423
#define ast_channel_lock(chan)
Definition: channel.h:2970
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:3006
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1328
#define ast_channel_unlock(chan)
Definition: channel.h:2971
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:2428
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:1853
@ 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:166
odbc_option_flags
Definition: func_odbc.c:140
@ OPT_ESCAPECOMMAS
Definition: func_odbc.c:141
@ OPT_MULTIROW
Definition: func_odbc.c:142
struct ao2_container * dsns
Definition: func_odbc.c:190
static ast_rwlock_t single_db_connection_lock
Definition: func_odbc.c:138
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
Definition: func_odbc.c:485
static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
Definition: func_odbc.c:527
static struct ast_custom_function escape_function
Definition: func_odbc.c:1157
static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
Definition: func_odbc.c:540
static int exec_odbcfinish(struct ast_channel *chan, const char *data)
Definition: func_odbc.c:1220
static char * cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1690
static struct ast_custom_function escape_backslashes_function
Definition: func_odbc.c:1168
static struct ast_threadstorage colnames_buf
Definition: func_odbc.c:454
static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
Definition: func_odbc.c:532
static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
Definition: func_odbc.c:756
static const struct ast_datastore_info odbc_info
Definition: func_odbc.c:160
static int connection_dead(struct odbc_obj *connection)
Determine if the connection has died.
Definition: func_odbc.c:296
static struct ast_threadstorage sql2_buf
Definition: func_odbc.c:452
static char * cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1477
static struct ast_custom_function fetch_function
Definition: func_odbc.c:1212
static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
Definition: func_odbc.c:1251
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1174
static int dsn_cmp(void *obj, void *arg, int flags)
Definition: func_odbc.c:212
static char * config
Definition: func_odbc.c:132
static struct dsn * get_dsn(const char *name)
Retrieve a DSN, or create it if it does not exist.
Definition: func_odbc.c:336
static void odbc_datastore_free(void *data)
Definition: func_odbc.c:458
static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1152
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
Definition: func_odbc.c:1136
#define DEFAULT_SINGLE_DB_CONNECTION
Definition: func_odbc.c:134
static int dsn_hash(const void *obj, const int flags)
Definition: func_odbc.c:192
static char * app_odbcfinish
Definition: func_odbc.c:1218
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:390
static void dsn_destructor(void *obj)
Definition: func_odbc.c:241
static struct dsn * create_dsn(const char *name)
Create a DSN and connect to the database.
Definition: func_odbc.c:257
static struct ast_threadstorage coldata_buf
Definition: func_odbc.c:453
static int load_module(void)
Definition: func_odbc.c:1845
static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:1163
static int unload_module(void)
Definition: func_odbc.c:1915
static int reload(void)
Definition: func_odbc.c:1946
static int single_db_connection
Definition: func_odbc.c:136
#define DSN_BUCKETS
Definition: func_odbc.c:188
static struct ast_cli_entry cli_func_odbc[]
Definition: func_odbc.c:1840
static int free_acf_query(struct acf_odbc_query *query)
Definition: func_odbc.c:1234
static int resultcount
Definition: func_odbc.c:449
static void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
Release an ODBC obj or a DSN.
Definition: func_odbc.c:419
static struct ast_threadstorage sql_buf
Definition: func_odbc.c:451
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
@ CONFIG_FLAG_FILEUNCHANGED
#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:869
#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:240
#define ast_rwlock_rdlock(a)
Definition: lock.h:239
#define AST_RWLOCK_DEFINE_STATIC(rwlock)
Definition: lock.h:547
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:761
#define ast_rwlock_unlock(a)
Definition: lock.h:238
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ 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:640
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:1559
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:181
struct odbc_obj * connection
Definition: func_odbc.c:183
char name[0]
Definition: func_odbc.c:185
struct odbc_datastore_row::@175 list
char names[0]
Definition: func_odbc.c:174
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