Asterisk - The Open Source Telephony Project  21.4.1
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"
44 #include "asterisk/res_odbc_transaction.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 
118 static char *config = "func_odbc.conf";
119 
120 #define DEFAULT_SINGLE_DB_CONNECTION 0
121 
122 static int single_db_connection;
123 
124 AST_RWLOCK_DEFINE_STATIC(single_db_connection_lock);
125 
126 enum odbc_option_flags {
127  OPT_ESCAPECOMMAS = (1 << 0),
128  OPT_MULTIROW = (1 << 1),
129 };
130 
132  AST_RWLIST_ENTRY(acf_odbc_query) list;
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 
144 static void odbc_datastore_free(void *data);
145 
146 static 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  */
167 struct 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 
176 struct ao2_container *dsns;
177 
178 static 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;
187  case OBJ_SEARCH_OBJECT:
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 
198 static 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) {
206  case OBJ_SEARCH_OBJECT:
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 
227 static 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  */
243 static 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 
259  dsn->connection = ast_odbc_request_obj(name, 0);
260  if (!dsn->connection) {
261  ao2_ref(dsn, -1);
262  return NULL;
263  }
264 
265  if (!ao2_link_flags(dsns, dsn, OBJ_NOLOCK)) {
266  ao2_ref(dsn, -1);
267  return NULL;
268  }
269 
270  return dsn;
271 }
272 
273 static 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  */
300  stmt = ast_odbc_direct_execute(connection, silent_execute, "SELECT 1");
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  */
322 static 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);
331  dsn = ao2_find(dsns, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
332  if (!dsn) {
333  dsn = create_dsn(name);
334  }
335  ao2_unlock(dsns);
336 
337  if (!dsn) {
338  return NULL;
339  }
340 
341  ao2_lock(dsn);
342  if (!dsn->connection) {
343  dsn->connection = ast_odbc_request_obj(name, 0);
344  if (!dsn->connection) {
345  ao2_unlock(dsn);
346  ao2_ref(dsn, -1);
347  return NULL;
348  }
349  return dsn;
350  }
351 
352  if (connection_dead(dsn->connection)) {
354  dsn->connection = ast_odbc_request_obj(name, 0);
355  if (!dsn->connection) {
356  ao2_unlock(dsn);
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  */
376 static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn)
377 {
378  struct odbc_obj *obj = NULL;
379 
380  ast_rwlock_rdlock(&single_db_connection_lock);
381  if (single_db_connection) {
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  }
391  ast_rwlock_unlock(&single_db_connection_lock);
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  */
405 static 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) {
428  ast_odbc_release_obj(*obj);
429  *obj = NULL;
430  }
431 }
432 
434 
435 static int resultcount = 0;
436 
437 AST_THREADSTORAGE(sql_buf);
438 AST_THREADSTORAGE(sql2_buf);
439 AST_THREADSTORAGE(coldata_buf);
440 AST_THREADSTORAGE(colnames_buf);
441 
442 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
443 
444 static 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 
453  AST_LIST_LOCK(result);
454  while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
455  ast_free(row);
456  }
457  AST_LIST_UNLOCK(result);
458  AST_LIST_HEAD_DESTROY(result);
459  ast_free(result);
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  */
471 static 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 
513 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
514 {
515  return execute(obj, data, 0);
516 }
517 
518 static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
519 {
520  return execute(obj, data, 1);
521 }
522 
523 /*
524  * Master control routine
525  */
526 static 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 
566  AST_STANDARD_APP_ARGS(args, s);
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) {
583  ast_autoservice_start(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) {
599  ast_autoservice_stop(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) {
705  stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
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) {
736  ast_autoservice_stop(chan);
737  }
738 
739  return 0;
740 }
741 
742 static 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 
788  AST_STANDARD_APP_ARGS(args, s);
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) {
805  ast_autoservice_start(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);
836  ast_autoservice_stop(chan);
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);
853  ast_autoservice_stop(chan);
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  }
867  stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
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);
880  ast_autoservice_stop(chan);
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);
894  ast_autoservice_stop(chan);
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");
909  ast_autoservice_stop(chan);
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);
934  ast_autoservice_stop(chan);
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");
956  ast_autoservice_stop(chan);
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");
991  ast_autoservice_stop(chan);
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 
1066 end_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;
1076  uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
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");
1098  ast_autoservice_stop(chan);
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) {
1117  ast_autoservice_stop(chan);
1118  }
1119  return 0;
1120 }
1121 
1122 static 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 
1138 static 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 
1143 static struct ast_custom_function escape_function = {
1144  .name = "SQL_ESC",
1145  .read = acf_escape_ticks,
1146  .write = NULL,
1147 };
1148 
1149 static 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 
1154 static struct ast_custom_function escape_backslashes_function = {
1155  .name = "SQL_ESC_BACKSLASHES",
1156  .read = acf_escape_backslashes,
1157  .write = NULL,
1158 };
1159 
1160 static 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 
1198 static struct ast_custom_function fetch_function = {
1199  .name = "ODBC_FETCH",
1200  .read = acf_fetch,
1201  .write = NULL,
1202 };
1203 
1204 static char *app_odbcfinish = "ODBCFinish";
1205 
1206 static 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 
1220 static 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 
1237 static 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))
1339  ast_clear_flag((*query), OPT_ESCAPECOMMAS);
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 
1463 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1464 {
1465  AST_DECLARE_APP_ARGS(args,
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 
1676 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1677 {
1679  AST_APP_ARG(field)[100];
1680  );
1681  AST_DECLARE_APP_ARGS(args,
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 
1826 static 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 
1831 static 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 
1839  res |= ast_custom_function_register(&fetch_function);
1840  res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
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);
1845  return AST_MODULE_LOAD_DECLINE;
1846  }
1847 
1848  ast_rwlock_wrlock(&single_db_connection_lock);
1849  if ((s = ast_variable_retrieve(cfg, "general", "single_db_connection"))) {
1850  single_db_connection = ast_true(s);
1851  } else {
1852  single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
1853  }
1854 
1855  dsns = NULL;
1856 
1857  if (single_db_connection) {
1858  dsns = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, DSN_BUCKETS,
1859  dsn_hash, NULL, dsn_cmp);
1860  if (!dsns) {
1861  ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1862  ast_rwlock_unlock(&single_db_connection_lock);
1863  return AST_MODULE_LOAD_DECLINE;
1864  }
1865  }
1866  ast_rwlock_unlock(&single_db_connection_lock);
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);
1893  res |= ast_custom_function_register(&escape_function);
1894  res |= ast_custom_function_register(&escape_backslashes_function);
1895  ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
1896 
1898  return res;
1899 }
1900 
1901 static 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);
1909  ast_custom_function_unregister(query->acf);
1910  free_acf_query(query);
1911  }
1912 
1913  res |= ast_custom_function_unregister(&escape_function);
1914  res |= ast_custom_function_unregister(&escape_backslashes_function);
1915  res |= ast_custom_function_unregister(&fetch_function);
1916  res |= ast_unregister_application(app_odbcfinish);
1917  ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
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 
1932 static 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);
1942  if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
1943  return 0;
1944 
1945  ast_rwlock_wrlock(&single_db_connection_lock);
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"))) {
1953  single_db_connection = ast_true(s);
1954  } else {
1955  single_db_connection = DEFAULT_SINGLE_DB_CONNECTION;
1956  }
1957 
1958  if (single_db_connection) {
1959  dsns = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, DSN_BUCKETS,
1960  dsn_hash, NULL, dsn_cmp);
1961  if (!dsns) {
1962  ast_log(LOG_ERROR, "Could not initialize DSN container\n");
1963  ast_rwlock_unlock(&single_db_connection_lock);
1964  return 0;
1965  }
1966  }
1967  ast_rwlock_unlock(&single_db_connection_lock);
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);
2000 reload_out:
2002  return res;
2003 }
2004 
2005 /* XXX need to revise usecount - set if query_lock is set */
2006 
2007 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
2008  .support_level = AST_MODULE_SUPPORT_CORE,
2009  .load = load_module,
2010  .unload = unload_module,
2011  .reload = reload,
2012  .requires = "res_odbc",
2013 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
SQLHDBC con
Definition: res_odbc.h:47
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
Definition: func_odbc.c:471
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
String manipulation functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
Data source name.
Definition: func_odbc.c:167
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
struct odbc_obj * connection
Definition: func_odbc.c:169
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
Structure for a data store type.
Definition: datastore.h:31
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 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
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition: astobj2.h:1554
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
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
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
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...
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
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
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
const char * uid
Definition: datastore.h:65
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1846
#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
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
Configuration File Parser.
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
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
ODBC container.
Definition: res_odbc.h:46
static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
Definition: func_odbc.c:526
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
ODBC resource manager.
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
char name[0]
Definition: func_odbc.c:171
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#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 connection_dead(struct odbc_obj *connection)
Determine if the connection has died.
Definition: func_odbc.c:282
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
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
#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
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
Structure used to handle boolean flags.
Definition: utils.h:199
static struct dsn * create_dsn(const char *name)
Create a DSN and connect to the database.
Definition: func_odbc.c:243
const char * usage
Definition: cli.h:177
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...
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:469
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
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 * data
Definition: datastore.h:66
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#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
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
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 release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
Release an ODBC obj or a DSN.
Definition: func_odbc.c:405
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
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
Generic container type.
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
Search option field mask.
Definition: astobj2.h:1072
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
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
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
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_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
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
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521