Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


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  <support_level>core</support_level>
33  ***/
34 
35 #include "asterisk.h"
36 
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414997 $")
38 
39 #include "asterisk/module.h"
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/config.h"
44 #include "asterisk/res_odbc.h"
45 #include "asterisk/app.h"
46 #include "asterisk/cli.h"
47 #include "asterisk/strings.h"
48 
49 /*** DOCUMENTATION
50  <function name="ODBC_FETCH" language="en_US">
51  <synopsis>
52  Fetch a row from a multirow query.
53  </synopsis>
54  <syntax>
55  <parameter name="result-id" required="true" />
56  </syntax>
57  <description>
58  <para>For queries which are marked as mode=multirow, the original
59  query returns a <replaceable>result-id</replaceable> from which results
60  may be fetched. This function implements the actual fetch of the results.</para>
61  <para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
62  <variablelist>
63  <variable name="ODBC_FETCH_STATUS">
64  <value name="SUCESS">
65  If rows are available.
66  </value>
67  <value name="FAILURE">
68  If no rows are available.
69  </value>
70  </variable>
71  </variablelist>
72  </description>
73  </function>
74  <application name="ODBCFinish" language="en_US">
75  <synopsis>
76  Clear the resultset of a sucessful multirow query.
77  </synopsis>
78  <syntax>
79  <parameter name="result-id" required="true" />
80  </syntax>
81  <description>
82  <para>For queries which are marked as mode=multirow, this will clear
83  any remaining rows of the specified resultset.</para>
84  </description>
85  </application>
86  <function name="SQL_ESC" language="en_US">
87  <synopsis>
88  Escapes single ticks for use in SQL statements.
89  </synopsis>
90  <syntax>
91  <parameter name="string" required="true" />
92  </syntax>
93  <description>
94  <para>Used in SQL templates to escape data which may contain single ticks
95  <literal>'</literal> which are otherwise used to delimit data.</para>
96  <para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'</para>
97  </description>
98  </function>
99  ***/
100 
101 static char *config = "func_odbc.conf";
102 
104  OPT_ESCAPECOMMAS = (1 << 0),
105  OPT_MULTIROW = (1 << 1),
106 };
107 
110  char readhandle[5][30];
111  char writehandle[5][30];
112  char *sql_read;
113  char *sql_write;
114  char *sql_insert;
115  unsigned int flags;
116  int rowlimit;
117  struct ast_custom_function *acf;
118 };
119 
120 static void odbc_datastore_free(void *data);
121 
122 static const struct ast_datastore_info odbc_info = {
123  .type = "FUNC_ODBC",
124  .destroy = odbc_datastore_free,
125 };
126 
127 /* For storing each result row */
130  char data[0];
131 };
132 
133 /* For storing each result set */
136  char names[0];
137 };
138 
140 
141 static int resultcount = 0;
142 
147 
148 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
149 
150 static void odbc_datastore_free(void *data)
151 {
152  struct odbc_datastore *result = data;
153  struct odbc_datastore_row *row;
154 
155  if (!result) {
156  return;
157  }
158 
159  AST_LIST_LOCK(result);
160  while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
161  ast_free(row);
162  }
163  AST_LIST_UNLOCK(result);
164  AST_LIST_HEAD_DESTROY(result);
165  ast_free(result);
166 }
167 
168 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
169 {
170  int res;
171  char *sql = data;
172  SQLHSTMT stmt;
173 
174  res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
175  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
176  ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
177  return NULL;
178  }
179 
180  res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
181  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
182  if (res == SQL_ERROR) {
183  int i;
184  SQLINTEGER nativeerror=0, numfields=0;
185  SQLSMALLINT diagbytes=0;
186  unsigned char state[10], diagnostic[256];
187 
188  SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
189  for (i = 0; i < numfields; i++) {
190  SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
191  ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
192  if (i > 10) {
193  ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
194  break;
195  }
196  }
197  }
198 
199  ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
200  SQLCloseCursor(stmt);
201  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
202  return NULL;
203  }
204 
205  return stmt;
206 }
207 
208 /*
209  * Master control routine
210  */
211 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
212 {
213  struct odbc_obj *obj = NULL;
214  struct acf_odbc_query *query;
215  char *t, varname[15];
216  int i, dsn, bogus_chan = 0;
217  int transactional = 0;
219  AST_APP_ARG(field)[100];
220  );
222  AST_APP_ARG(field)[100];
223  );
224  SQLHSTMT stmt = NULL;
225  SQLLEN rows=0;
226  struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
227  struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
228  const char *status = "FAILURE";
229 
230  if (!buf || !insertbuf) {
231  return -1;
232  }
233 
235  AST_RWLIST_TRAVERSE(&queries, query, list) {
236  if (!strcmp(query->acf->name, cmd)) {
237  break;
238  }
239  }
240 
241  if (!query) {
242  ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
244  if (chan) {
245  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
246  }
247  return -1;
248  }
249 
250  if (!chan) {
251  if (!(chan = ast_dummy_channel_alloc())) {
253  return -1;
254  }
255  bogus_chan = 1;
256  }
257 
258  if (!bogus_chan) {
259  ast_autoservice_start(chan);
260  }
261 
262  ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
263  /* We only get here if sql_write is set. sql_insert is optional however. */
264  if (query->sql_insert) {
265  ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
266  }
267 
268  /* Parse our arguments */
269  t = value ? ast_strdupa(value) : "";
270 
271  if (!s || !t) {
272  ast_log(LOG_ERROR, "Out of memory\n");
274  if (!bogus_chan) {
275  ast_autoservice_stop(chan);
276  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
277  } else {
278  ast_channel_unref(chan);
279  }
280  return -1;
281  }
282 
284  for (i = 0; i < args.argc; i++) {
285  snprintf(varname, sizeof(varname), "ARG%d", i + 1);
286  pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
287  }
288 
289  /* Parse values, just like arguments */
291  for (i = 0; i < values.argc; i++) {
292  snprintf(varname, sizeof(varname), "VAL%d", i + 1);
293  pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
294  }
295 
296  /* Additionally set the value as a whole (but push an empty string if value is NULL) */
297  pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
298 
299  ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
300  if (query->sql_insert) {
301  ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
302  }
303 
304  if (bogus_chan) {
305  chan = ast_channel_unref(chan);
306  } else {
307  /* Restore prior values */
308  for (i = 0; i < args.argc; i++) {
309  snprintf(varname, sizeof(varname), "ARG%d", i + 1);
310  pbx_builtin_setvar_helper(chan, varname, NULL);
311  }
312 
313  for (i = 0; i < values.argc; i++) {
314  snprintf(varname, sizeof(varname), "VAL%d", i + 1);
315  pbx_builtin_setvar_helper(chan, varname, NULL);
316  }
317  pbx_builtin_setvar_helper(chan, "VALUE", NULL);
318  }
319 
320  /*!\note
321  * Okay, this part is confusing. Transactions belong to a single database
322  * handle. Therefore, when working with transactions, we CANNOT failover
323  * to multiple DSNs. We MUST have a single handle all the way through the
324  * transaction, or else we CANNOT enforce atomicity.
325  */
326  for (dsn = 0; dsn < 5; dsn++) {
327  if (!ast_strlen_zero(query->writehandle[dsn])) {
328  if (transactional) {
329  /* This can only happen second time through or greater. */
330  ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
331  }
332 
333  if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
334  transactional = 1;
335  } else {
336  obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
337  transactional = 0;
338  }
339 
340  if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
341  break;
342  }
343 
344  if (obj && !transactional) {
346  obj = NULL;
347  }
348  }
349  }
350 
351  if (stmt) {
352  SQLRowCount(stmt, &rows);
353  SQLCloseCursor(stmt);
354  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
355 
356  if (rows != 0) {
357  status = "SUCCESS";
358 
359  } else if (query->sql_insert) {
360  if (obj && !transactional) {
362  obj = NULL;
363  }
364 
365  for (transactional = 0, dsn = 0; dsn < 5; dsn++) {
366  if (!ast_strlen_zero(query->writehandle[dsn])) {
367  if (transactional) {
368  /* This can only happen second time through or greater. */
369  ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
370  } else if (obj) {
372  obj = NULL;
373  }
374 
375  if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
376  transactional = 1;
377  } else {
378  obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
379  transactional = 0;
380  }
381  if (obj) {
382  stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
383  }
384  }
385  if (stmt) {
386  status = "FAILOVER";
387  SQLRowCount(stmt, &rows);
388  SQLCloseCursor(stmt);
389  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
390  break;
391  }
392  }
393  }
394  }
395 
397 
398  /* Output the affected rows, for all cases. In the event of failure, we
399  * flag this as -1 rows. Note that this is different from 0 affected rows
400  * which would be the case if we succeeded in our query, but the values did
401  * not change. */
402  if (!bogus_chan) {
403  snprintf(varname, sizeof(varname), "%d", (int)rows);
404  pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
405  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
406  }
407 
408  if (obj && !transactional) {
410  obj = NULL;
411  }
412 
413  if (!bogus_chan) {
414  ast_autoservice_stop(chan);
415  }
416 
417  return 0;
418 }
419 
420 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
421 {
422  struct odbc_obj *obj = NULL;
423  struct acf_odbc_query *query;
424  char varname[15], rowcount[12] = "-1";
425  struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
426  int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn, bogus_chan = 0;
428  AST_APP_ARG(field)[100];
429  );
430  SQLHSTMT stmt = NULL;
431  SQLSMALLINT colcount=0;
432  SQLLEN indicator;
433  SQLSMALLINT collength;
434  struct odbc_datastore *resultset = NULL;
435  struct odbc_datastore_row *row = NULL;
436  struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
437  const char *status = "FAILURE";
438 
439  if (!sql || !colnames) {
440  if (chan) {
441  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
442  }
443  return -1;
444  }
445 
446  ast_str_reset(colnames);
447 
449  AST_RWLIST_TRAVERSE(&queries, query, list) {
450  if (!strcmp(query->acf->name, cmd)) {
451  break;
452  }
453  }
454 
455  if (!query) {
456  ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
458  if (chan) {
459  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
460  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
461  }
462  return -1;
463  }
464 
465  if (!chan) {
466  if (!(chan = ast_dummy_channel_alloc())) {
468  return -1;
469  }
470  bogus_chan = 1;
471  }
472 
473  if (!bogus_chan) {
474  ast_autoservice_start(chan);
475  }
476 
478  for (x = 0; x < args.argc; x++) {
479  snprintf(varname, sizeof(varname), "ARG%d", x + 1);
480  pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
481  }
482 
483  ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
484 
485  if (bogus_chan) {
486  chan = ast_channel_unref(chan);
487  } else {
488  /* Restore prior values */
489  for (x = 0; x < args.argc; x++) {
490  snprintf(varname, sizeof(varname), "ARG%d", x + 1);
491  pbx_builtin_setvar_helper(chan, varname, NULL);
492  }
493  }
494 
495  /* Save these flags, so we can release the lock */
496  escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
497  if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
498  if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
499  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
500  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
501  ast_autoservice_stop(chan);
502  return -1;
503  }
504  AST_LIST_HEAD_INIT(resultset);
505  if (query->rowlimit) {
506  rowlimit = query->rowlimit;
507  } else {
508  rowlimit = INT_MAX;
509  }
510  multirow = 1;
511  } else if (!bogus_chan) {
512  if (query->rowlimit > 1) {
513  rowlimit = query->rowlimit;
514  if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
515  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
516  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
517  ast_autoservice_stop(chan);
518  return -1;
519  }
520  AST_LIST_HEAD_INIT(resultset);
521  }
522  }
524 
525  for (dsn = 0; dsn < 5; dsn++) {
526  if (!ast_strlen_zero(query->readhandle[dsn])) {
527  obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
528  if (obj) {
530  }
531  }
532  if (stmt) {
533  break;
534  }
535  if (obj) {
537  obj = NULL;
538  }
539  }
540 
541  if (!stmt) {
542  ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
543  if (obj) {
545  obj = NULL;
546  }
547  if (!bogus_chan) {
548  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
549  ast_autoservice_stop(chan);
550  }
551  odbc_datastore_free(resultset);
552  return -1;
553  }
554 
555  res = SQLNumResultCols(stmt, &colcount);
556  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
557  ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
558  SQLCloseCursor(stmt);
559  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
561  obj = NULL;
562  if (!bogus_chan) {
563  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
564  ast_autoservice_stop(chan);
565  }
566  odbc_datastore_free(resultset);
567  return -1;
568  }
569 
570  res = SQLFetch(stmt);
571  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
572  int res1 = -1;
573  if (res == SQL_NO_DATA) {
574  ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
575  res1 = 0;
576  buf[0] = '\0';
577  ast_copy_string(rowcount, "0", sizeof(rowcount));
578  status = "NODATA";
579  } else {
580  ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
581  status = "FETCHERROR";
582  }
583  SQLCloseCursor(stmt);
584  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
586  obj = NULL;
587  if (!bogus_chan) {
588  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
589  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
590  ast_autoservice_stop(chan);
591  }
592  odbc_datastore_free(resultset);
593  return res1;
594  }
595 
596  status = "SUCCESS";
597 
598  for (y = 0; y < rowlimit; y++) {
599  buf[0] = '\0';
600  for (x = 0; x < colcount; x++) {
601  int i;
602  struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
603  char *ptrcoldata;
604 
605  if (!coldata) {
606  odbc_datastore_free(resultset);
607  SQLCloseCursor(stmt);
608  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
610  obj = NULL;
611  if (!bogus_chan) {
612  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
613  ast_autoservice_stop(chan);
614  }
615  return -1;
616  }
617 
618  if (y == 0) {
619  char colname[256];
620  SQLULEN maxcol = 0;
621 
622  res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
623  ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
624  if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
625  snprintf(colname, sizeof(colname), "field%d", x);
626  }
627 
628  ast_str_make_space(&coldata, maxcol + 1);
629 
630  if (ast_str_strlen(colnames)) {
631  ast_str_append(&colnames, 0, ",");
632  }
633  ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
634 
635  if (resultset) {
636  void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
637  if (!tmp) {
638  ast_log(LOG_ERROR, "No space for a new resultset?\n");
639  odbc_datastore_free(resultset);
640  SQLCloseCursor(stmt);
641  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
643  obj = NULL;
644  if (!bogus_chan) {
645  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
646  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
647  ast_autoservice_stop(chan);
648  }
649  return -1;
650  }
651  resultset = tmp;
652  strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
653  }
654  }
655 
656  buflen = strlen(buf);
657  res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
658  if (indicator == SQL_NULL_DATA) {
659  ast_debug(3, "Got NULL data\n");
660  ast_str_reset(coldata);
661  res = SQL_SUCCESS;
662  }
663 
664  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
665  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
666  y = -1;
667  buf[0] = '\0';
668  goto end_acf_read;
669  }
670 
671  ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
672 
673  if (x) {
674  buf[buflen++] = ',';
675  }
676 
677  /* Copy data, encoding '\' and ',' for the argument parser */
678  ptrcoldata = ast_str_buffer(coldata);
679  for (i = 0; i < ast_str_strlen(coldata); i++) {
680  if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
681  buf[buflen++] = '\\';
682  }
683  buf[buflen++] = ptrcoldata[i];
684 
685  if (buflen >= len - 2) {
686  break;
687  }
688 
689  if (ptrcoldata[i] == '\0') {
690  break;
691  }
692  }
693 
694  buf[buflen] = '\0';
695  ast_debug(2, "buf is now set to '%s'\n", buf);
696  }
697  ast_debug(2, "buf is now set to '%s'\n", buf);
698 
699  if (resultset) {
700  row = ast_calloc(1, sizeof(*row) + buflen + 1);
701  if (!row) {
702  ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
703  status = "MEMERROR";
704  goto end_acf_read;
705  }
706  strcpy((char *)row + sizeof(*row), buf);
707  AST_LIST_INSERT_TAIL(resultset, row, list);
708 
709  /* Get next row */
710  res = SQLFetch(stmt);
711  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
712  if (res != SQL_NO_DATA) {
713  ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
714  }
715  /* Number of rows in the resultset */
716  y++;
717  break;
718  }
719  }
720  }
721 
722 end_acf_read:
723  if (!bogus_chan) {
724  snprintf(rowcount, sizeof(rowcount), "%d", y);
725  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
726  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
727  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
728  if (resultset) {
729  int uid;
730  struct ast_datastore *odbc_store;
731  if (multirow) {
732  uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
733  snprintf(buf, len, "%d", uid);
734  } else {
735  /* Name of the query is name of the resultset */
736  ast_copy_string(buf, cmd, len);
737 
738  /* If there's one with the same name already, free it */
739  ast_channel_lock(chan);
740  if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
741  ast_channel_datastore_remove(chan, odbc_store);
742  ast_datastore_free(odbc_store);
743  }
744  ast_channel_unlock(chan);
745  }
746  odbc_store = ast_datastore_alloc(&odbc_info, buf);
747  if (!odbc_store) {
748  ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
749  odbc_datastore_free(resultset);
750  SQLCloseCursor(stmt);
751  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
753  obj = NULL;
754  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
755  ast_autoservice_stop(chan);
756  return -1;
757  }
758  odbc_store->data = resultset;
759  ast_channel_lock(chan);
760  ast_channel_datastore_add(chan, odbc_store);
761  ast_channel_unlock(chan);
762  }
763  }
764  SQLCloseCursor(stmt);
765  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
767  obj = NULL;
768  if (resultset && !multirow) {
769  /* Fetch the first resultset */
770  if (!acf_fetch(chan, "", buf, buf, len)) {
771  buf[0] = '\0';
772  }
773  }
774  if (!bogus_chan) {
775  ast_autoservice_stop(chan);
776  }
777  return 0;
778 }
779 
780 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
781 {
782  char *out = buf;
783 
784  for (; *data && out - buf < len; data++) {
785  if (*data == '\'') {
786  *out = '\'';
787  out++;
788  }
789  *out++ = *data;
790  }
791  *out = '\0';
792 
793  return 0;
794 }
795 
797  .name = "SQL_ESC",
798  .read = acf_escape,
799  .write = NULL,
800 };
801 
802 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
803 {
804  struct ast_datastore *store;
805  struct odbc_datastore *resultset;
806  struct odbc_datastore_row *row;
807 
808  if (!chan) {
809  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
810  return -1;
811  }
812 
813  ast_channel_lock(chan);
814  store = ast_channel_datastore_find(chan, &odbc_info, data);
815  if (!store) {
816  ast_channel_unlock(chan);
817  pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
818  return -1;
819  }
820  resultset = store->data;
821  AST_LIST_LOCK(resultset);
822  row = AST_LIST_REMOVE_HEAD(resultset, list);
823  AST_LIST_UNLOCK(resultset);
824  if (!row) {
825  /* Cleanup datastore */
826  ast_channel_datastore_remove(chan, store);
827  ast_datastore_free(store);
828  ast_channel_unlock(chan);
829  pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
830  return -1;
831  }
832  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
833  ast_channel_unlock(chan);
834  ast_copy_string(buf, row->data, len);
835  ast_free(row);
836  pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
837  return 0;
838 }
839 
841  .name = "ODBC_FETCH",
842  .read = acf_fetch,
843  .write = NULL,
844 };
845 
846 static char *app_odbcfinish = "ODBCFinish";
847 
848 static int exec_odbcfinish(struct ast_channel *chan, const char *data)
849 {
850  struct ast_datastore *store;
851 
852  ast_channel_lock(chan);
853  store = ast_channel_datastore_find(chan, &odbc_info, data);
854  if (store) {
855  ast_channel_datastore_remove(chan, store);
856  ast_datastore_free(store);
857  }
858  ast_channel_unlock(chan);
859  return 0;
860 }
861 
862 static int free_acf_query(struct acf_odbc_query *query)
863 {
864  if (query) {
865  if (query->acf) {
866  if (query->acf->name)
867  ast_free((char *)query->acf->name);
868  ast_string_field_free_memory(query->acf);
869  ast_free(query->acf);
870  }
871  ast_free(query->sql_read);
872  ast_free(query->sql_write);
873  ast_free(query->sql_insert);
874  ast_free(query);
875  }
876  return 0;
877 }
878 
879 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
880 {
881  const char *tmp;
882  const char *tmp2;
883  int i;
884 
885  if (!cfg || !catg) {
886  return EINVAL;
887  }
888 
889  if (!(*query = ast_calloc(1, sizeof(**query)))) {
890  return ENOMEM;
891  }
892 
893  if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
894  char *tmp2 = ast_strdupa(tmp);
895  AST_DECLARE_APP_ARGS(writeconf,
896  AST_APP_ARG(dsn)[5];
897  );
898  AST_STANDARD_APP_ARGS(writeconf, tmp2);
899  for (i = 0; i < 5; i++) {
900  if (!ast_strlen_zero(writeconf.dsn[i]))
901  ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
902  }
903  }
904 
905  if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
906  char *tmp2 = ast_strdupa(tmp);
907  AST_DECLARE_APP_ARGS(readconf,
908  AST_APP_ARG(dsn)[5];
909  );
910  AST_STANDARD_APP_ARGS(readconf, tmp2);
911  for (i = 0; i < 5; i++) {
912  if (!ast_strlen_zero(readconf.dsn[i]))
913  ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
914  }
915  } else {
916  /* If no separate readhandle, then use the writehandle for reading */
917  for (i = 0; i < 5; i++) {
918  if (!ast_strlen_zero((*query)->writehandle[i]))
919  ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
920  }
921  }
922 
923  if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")) ||
924  (tmp2 = ast_variable_retrieve(cfg, catg, "read"))) {
925  if (!tmp) {
926  ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
927  tmp = tmp2;
928  }
929  if (*tmp != '\0') { /* non-empty string */
930  if (!((*query)->sql_read = ast_strdup(tmp))) {
931  free_acf_query(*query);
932  *query = NULL;
933  return ENOMEM;
934  }
935  }
936  }
937 
938  if ((*query)->sql_read && ast_strlen_zero((*query)->readhandle[0])) {
939  free_acf_query(*query);
940  *query = NULL;
941  ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
942  return EINVAL;
943  }
944 
945  if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")) ||
946  (tmp2 = ast_variable_retrieve(cfg, catg, "write"))) {
947  if (!tmp) {
948  ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
949  tmp = tmp2;
950  }
951  if (*tmp != '\0') { /* non-empty string */
952  if (!((*query)->sql_write = ast_strdup(tmp))) {
953  free_acf_query(*query);
954  *query = NULL;
955  return ENOMEM;
956  }
957  }
958  }
959 
960  if ((*query)->sql_write && ast_strlen_zero((*query)->writehandle[0])) {
961  free_acf_query(*query);
962  *query = NULL;
963  ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
964  return EINVAL;
965  }
966 
967  if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
968  if (*tmp != '\0') { /* non-empty string */
969  if (!((*query)->sql_insert = ast_strdup(tmp))) {
970  free_acf_query(*query);
971  *query = NULL;
972  return ENOMEM;
973  }
974  }
975  }
976 
977  /* Allow escaping of embedded commas in fields to be turned off */
978  ast_set_flag((*query), OPT_ESCAPECOMMAS);
979  if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
980  if (ast_false(tmp))
981  ast_clear_flag((*query), OPT_ESCAPECOMMAS);
982  }
983 
984  if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
985  if (strcasecmp(tmp, "multirow") == 0)
986  ast_set_flag((*query), OPT_MULTIROW);
987  if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
988  sscanf(tmp, "%30d", &((*query)->rowlimit));
989  }
990 
991  (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
992  if (!(*query)->acf) {
993  free_acf_query(*query);
994  *query = NULL;
995  return ENOMEM;
996  }
997  if (ast_string_field_init((*query)->acf, 128)) {
998  free_acf_query(*query);
999  *query = NULL;
1000  return ENOMEM;
1001  }
1002 
1003  if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
1004  if (ast_asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
1005  (*query)->acf->name = NULL;
1006  }
1007  } else {
1008  if (ast_asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
1009  (*query)->acf->name = NULL;
1010  }
1011  }
1012 
1013  if (!(*query)->acf->name) {
1014  free_acf_query(*query);
1015  *query = NULL;
1016  return ENOMEM;
1017  }
1018 
1019  if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
1020  ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
1021  } else {
1022  ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
1023  }
1024 
1025  if (ast_strlen_zero((*query)->acf->syntax)) {
1026  free_acf_query(*query);
1027  *query = NULL;
1028  return ENOMEM;
1029  }
1030 
1031  if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
1032  ast_string_field_set((*query)->acf, synopsis, tmp);
1033  } else {
1034  ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
1035  }
1036 
1037  if (ast_strlen_zero((*query)->acf->synopsis)) {
1038  free_acf_query(*query);
1039  *query = NULL;
1040  return ENOMEM;
1041  }
1042 
1043  if ((*query)->sql_read && (*query)->sql_write) {
1044  ast_string_field_build((*query)->acf, desc,
1045  "Runs the following query, as defined in func_odbc.conf, performing\n"
1046  "substitution of the arguments into the query as specified by ${ARG1},\n"
1047  "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
1048  "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1049  "%s"
1050  "\nRead:\n%s\n\nWrite:\n%s%s%s",
1051  (*query)->sql_insert ?
1052  "If the write query affects no rows, the insert query will be\n"
1053  "performed.\n" : "",
1054  (*query)->sql_read,
1055  (*query)->sql_write,
1056  (*query)->sql_insert ? "\n\nInsert:\n" : "",
1057  (*query)->sql_insert ? (*query)->sql_insert : "");
1058  } else if ((*query)->sql_read) {
1059  ast_string_field_build((*query)->acf, desc,
1060  "Runs the following query, as defined in func_odbc.conf, performing\n"
1061  "substitution of the arguments into the query as specified by ${ARG1},\n"
1062  "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s",
1063  (*query)->sql_read);
1064  } else if ((*query)->sql_write) {
1065  ast_string_field_build((*query)->acf, desc,
1066  "Runs the following query, as defined in func_odbc.conf, performing\n"
1067  "substitution of the arguments into the query as specified by ${ARG1},\n"
1068  "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
1069  "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
1070  "This function may only be set.\n%s\nSQL:\n%s%s%s",
1071  (*query)->sql_insert ?
1072  "If the write query affects no rows, the insert query will be\n"
1073  "performed.\n" : "",
1074  (*query)->sql_write,
1075  (*query)->sql_insert ? "\n\nInsert:\n" : "",
1076  (*query)->sql_insert ? (*query)->sql_insert : "");
1077  } else {
1078  free_acf_query(*query);
1079  *query = NULL;
1080  ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
1081  return EINVAL;
1082  }
1083 
1084  if (ast_strlen_zero((*query)->acf->desc)) {
1085  free_acf_query(*query);
1086  *query = NULL;
1087  return ENOMEM;
1088  }
1089 
1090  if ((*query)->sql_read) {
1091  (*query)->acf->read = acf_odbc_read;
1092  }
1093 
1094  if ((*query)->sql_write) {
1095  (*query)->acf->write = acf_odbc_write;
1096  }
1097 
1098  return 0;
1099 }
1100 
1101 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1102 {
1104  AST_APP_ARG(field)[100];
1105  );
1106  struct ast_str *sql;
1107  char *char_args, varname[10];
1108  struct acf_odbc_query *query;
1109  struct ast_channel *chan;
1110  int i;
1111 
1112  switch (cmd) {
1113  case CLI_INIT:
1114  e->command = "odbc read";
1115  e->usage =
1116  "Usage: odbc read <name> <args> [exec]\n"
1117  " Evaluates the SQL provided in the ODBC function <name>, and\n"
1118  " optionally executes the function. This function is intended for\n"
1119  " testing purposes. Remember to quote arguments containing spaces.\n";
1120  return NULL;
1121  case CLI_GENERATE:
1122  if (a->pos == 2) {
1123  int wordlen = strlen(a->word), which = 0;
1124  /* Complete function name */
1126  AST_RWLIST_TRAVERSE(&queries, query, list) {
1127  if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1128  if (++which > a->n) {
1129  char *res = ast_strdup(query->acf->name);
1131  return res;
1132  }
1133  }
1134  }
1136  return NULL;
1137  } else if (a->pos == 4) {
1138  return a->n == 0 ? ast_strdup("exec") : NULL;
1139  } else {
1140  return NULL;
1141  }
1142  }
1143 
1144  if (a->argc < 4 || a->argc > 5) {
1145  return CLI_SHOWUSAGE;
1146  }
1147 
1148  sql = ast_str_thread_get(&sql_buf, 16);
1149  if (!sql) {
1150  return CLI_FAILURE;
1151  }
1152 
1154  AST_RWLIST_TRAVERSE(&queries, query, list) {
1155  if (!strcmp(query->acf->name, a->argv[2])) {
1156  break;
1157  }
1158  }
1159 
1160  if (!query) {
1161  ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1163  return CLI_SHOWUSAGE;
1164  }
1165 
1166  if (!query->sql_read) {
1167  ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
1169  return CLI_SUCCESS;
1170  }
1171 
1172  ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
1173 
1174  /* Evaluate function */
1175  char_args = ast_strdupa(a->argv[3]);
1176 
1177  chan = ast_dummy_channel_alloc();
1178  if (!chan) {
1180  return CLI_FAILURE;
1181  }
1182 
1183  AST_STANDARD_APP_ARGS(args, char_args);
1184  for (i = 0; i < args.argc; i++) {
1185  snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1186  pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1187  }
1188 
1189  ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
1190  chan = ast_channel_unref(chan);
1191 
1192  if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
1193  /* Execute the query */
1194  struct odbc_obj *obj = NULL;
1195  int dsn, executed = 0;
1196  SQLHSTMT stmt;
1197  int rows = 0, res, x;
1198  SQLSMALLINT colcount = 0, collength;
1199  SQLLEN indicator;
1200  struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
1201  char colname[256];
1202  SQLULEN maxcol;
1203 
1204  if (!coldata) {
1206  return CLI_SUCCESS;
1207  }
1208 
1209  for (dsn = 0; dsn < 5; dsn++) {
1210  if (ast_strlen_zero(query->readhandle[dsn])) {
1211  continue;
1212  }
1213  ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
1214  if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
1215  continue;
1216  }
1217 
1218  ast_debug(1, "Got obj\n");
1219  if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1220  ast_odbc_release_obj(obj);
1221  obj = NULL;
1222  continue;
1223  }
1224 
1225  executed = 1;
1226 
1227  res = SQLNumResultCols(stmt, &colcount);
1228  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1229  ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
1230  SQLCloseCursor(stmt);
1231  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1232  ast_odbc_release_obj(obj);
1233  obj = NULL;
1235  return CLI_SUCCESS;
1236  }
1237 
1238  res = SQLFetch(stmt);
1239  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1240  SQLCloseCursor(stmt);
1241  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1242  ast_odbc_release_obj(obj);
1243  obj = NULL;
1244  if (res == SQL_NO_DATA) {
1245  ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
1246  break;
1247  } else {
1248  ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
1249  }
1251  return CLI_SUCCESS;
1252  }
1253  for (;;) {
1254  for (x = 0; x < colcount; x++) {
1255  maxcol = 0;
1256 
1257  res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
1258  if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
1259  snprintf(colname, sizeof(colname), "field%d", x);
1260  }
1261 
1262  res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
1263  if (indicator == SQL_NULL_DATA) {
1264  ast_str_set(&coldata, 0, "(nil)");
1265  res = SQL_SUCCESS;
1266  }
1267 
1268  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1269  ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
1270  SQLCloseCursor(stmt);
1271  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1272  ast_odbc_release_obj(obj);
1273  obj = NULL;
1275  return CLI_SUCCESS;
1276  }
1277 
1278  ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
1279  }
1280  rows++;
1281 
1282  /* Get next row */
1283  res = SQLFetch(stmt);
1284  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1285  break;
1286  }
1287  ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
1288  }
1289  SQLCloseCursor(stmt);
1290  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1291  ast_odbc_release_obj(obj);
1292  obj = NULL;
1293  ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
1294  break;
1295  }
1296  if (obj) {
1297  ast_odbc_release_obj(obj);
1298  obj = NULL;
1299  }
1300 
1301  if (!executed) {
1302  ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
1303  }
1304  } else { /* No execution, just print out the resulting SQL */
1305  ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1306  }
1308  return CLI_SUCCESS;
1309 }
1310 
1311 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1312 {
1314  AST_APP_ARG(field)[100];
1315  );
1317  AST_APP_ARG(field)[100];
1318  );
1319  struct ast_str *sql;
1320  char *char_args, *char_values, varname[10];
1321  struct acf_odbc_query *query;
1322  struct ast_channel *chan;
1323  int i;
1324 
1325  switch (cmd) {
1326  case CLI_INIT:
1327  e->command = "odbc write";
1328  e->usage =
1329  "Usage: odbc write <name> <args> <value> [exec]\n"
1330  " Evaluates the SQL provided in the ODBC function <name>, and\n"
1331  " optionally executes the function. This function is intended for\n"
1332  " testing purposes. Remember to quote arguments containing spaces.\n";
1333  return NULL;
1334  case CLI_GENERATE:
1335  if (a->pos == 2) {
1336  int wordlen = strlen(a->word), which = 0;
1337  /* Complete function name */
1339  AST_RWLIST_TRAVERSE(&queries, query, list) {
1340  if (!strncasecmp(query->acf->name, a->word, wordlen)) {
1341  if (++which > a->n) {
1342  char *res = ast_strdup(query->acf->name);
1344  return res;
1345  }
1346  }
1347  }
1349  return NULL;
1350  } else if (a->pos == 5) {
1351  return a->n == 0 ? ast_strdup("exec") : NULL;
1352  } else {
1353  return NULL;
1354  }
1355  }
1356 
1357  if (a->argc < 5 || a->argc > 6) {
1358  return CLI_SHOWUSAGE;
1359  }
1360 
1361  sql = ast_str_thread_get(&sql_buf, 16);
1362  if (!sql) {
1363  return CLI_FAILURE;
1364  }
1365 
1367  AST_RWLIST_TRAVERSE(&queries, query, list) {
1368  if (!strcmp(query->acf->name, a->argv[2])) {
1369  break;
1370  }
1371  }
1372 
1373  if (!query) {
1374  ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
1376  return CLI_SHOWUSAGE;
1377  }
1378 
1379  if (!query->sql_write) {
1380  ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
1382  return CLI_SUCCESS;
1383  }
1384 
1385  /* FIXME: The code below duplicates code found in acf_odbc_write but
1386  * lacks the newer sql_insert additions. */
1387 
1388  ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
1389 
1390  /* Evaluate function */
1391  char_args = ast_strdupa(a->argv[3]);
1392  char_values = ast_strdupa(a->argv[4]);
1393 
1394  chan = ast_dummy_channel_alloc();
1395  if (!chan) {
1397  return CLI_FAILURE;
1398  }
1399 
1400  AST_STANDARD_APP_ARGS(args, char_args);
1401  for (i = 0; i < args.argc; i++) {
1402  snprintf(varname, sizeof(varname), "ARG%d", i + 1);
1403  pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
1404  }
1405 
1406  /* Parse values, just like arguments */
1407  AST_STANDARD_APP_ARGS(values, char_values);
1408  for (i = 0; i < values.argc; i++) {
1409  snprintf(varname, sizeof(varname), "VAL%d", i + 1);
1410  pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
1411  }
1412 
1413  /* Additionally set the value as a whole (but push an empty string if value is NULL) */
1414  pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
1415  ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
1416  ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
1417 
1418  chan = ast_channel_unref(chan);
1419 
1420  if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
1421  /* Execute the query */
1422  struct odbc_obj *obj = NULL;
1423  int dsn, executed = 0;
1424  SQLHSTMT stmt;
1425  SQLLEN rows = -1;
1426 
1427  for (dsn = 0; dsn < 5; dsn++) {
1428  if (ast_strlen_zero(query->writehandle[dsn])) {
1429  continue;
1430  }
1431  if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
1432  continue;
1433  }
1434  if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
1435  ast_odbc_release_obj(obj);
1436  obj = NULL;
1437  continue;
1438  }
1439 
1440  SQLRowCount(stmt, &rows);
1441  SQLCloseCursor(stmt);
1442  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1443  ast_odbc_release_obj(obj);
1444  obj = NULL;
1445  ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
1446  executed = 1;
1447  break;
1448  }
1449 
1450  if (!executed) {
1451  ast_cli(a->fd, "Failed to execute query.\n");
1452  }
1453  } else { /* No execution, just print out the resulting SQL */
1454  ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
1455  }
1457  return CLI_SUCCESS;
1458 }
1459 
1460 static struct ast_cli_entry cli_func_odbc[] = {
1461  AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
1462  AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
1463 };
1464 
1465 static int load_module(void)
1466 {
1467  int res = 0;
1468  struct ast_config *cfg;
1469  char *catg;
1470  struct ast_flags config_flags = { 0 };
1471 
1475 
1476  cfg = ast_config_load(config, config_flags);
1477  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
1478  ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
1480  return AST_MODULE_LOAD_DECLINE;
1481  }
1482 
1483  for (catg = ast_category_browse(cfg, NULL);
1484  catg;
1485  catg = ast_category_browse(cfg, catg)) {
1486  struct acf_odbc_query *query = NULL;
1487  int err;
1488 
1489  if ((err = init_acf_query(cfg, catg, &query))) {
1490  if (err == ENOMEM)
1491  ast_log(LOG_ERROR, "Out of memory\n");
1492  else if (err == EINVAL)
1493  ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
1494  else
1495  ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
1496  } else {
1498  ast_custom_function_register(query->acf);
1499  }
1500  }
1501 
1502  ast_config_destroy(cfg);
1505 
1507  return res;
1508 }
1509 
1510 static int unload_module(void)
1511 {
1512  struct acf_odbc_query *query;
1513  int res = 0;
1514 
1516  while (!AST_RWLIST_EMPTY(&queries)) {
1517  query = AST_RWLIST_REMOVE_HEAD(&queries, list);
1518  ast_custom_function_unregister(query->acf);
1519  free_acf_query(query);
1520  }
1521 
1526 
1527  /* Allow any threads waiting for this lock to pass (avoids a race) */
1529  usleep(1);
1531 
1533  return 0;
1534 }
1535 
1536 static int reload(void)
1537 {
1538  int res = 0;
1539  struct ast_config *cfg;
1540  struct acf_odbc_query *oldquery;
1541  char *catg;
1542  struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
1543 
1544  cfg = ast_config_load(config, config_flags);
1546  return 0;
1547 
1549 
1550  while (!AST_RWLIST_EMPTY(&queries)) {
1551  oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
1552  ast_custom_function_unregister(oldquery->acf);
1553  free_acf_query(oldquery);
1554  }
1555 
1556  if (!cfg) {
1557  ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
1558  goto reload_out;
1559  }
1560 
1561  for (catg = ast_category_browse(cfg, NULL);
1562  catg;
1563  catg = ast_category_browse(cfg, catg)) {
1564  struct acf_odbc_query *query = NULL;
1565 
1566  if (init_acf_query(cfg, catg, &query)) {
1567  ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
1568  } else {
1570  ast_custom_function_register(query->acf);
1571  }
1572  }
1573 
1574  ast_config_destroy(cfg);
1575 reload_out:
1577  return res;
1578 }
1579 
1580 /* XXX need to revise usecount - set if query_lock is set */
1581 
1583  .load = load_module,
1584  .unload = unload_module,
1585  .reload = reload,
1586  );
1587 
const char * type
Definition: datastore.h:32
SQLHDBC con
Definition: res_odbc.h:48
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
struct odbc_obj * ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname)
Retrieve a stored ODBC object, if a transaction has been started.
Definition: res_odbc.c:1456
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
static char * app_odbcfinish
Definition: func_odbc.c:846
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
#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:332
String manipulation functions.
static struct ast_custom_function fetch_function
Definition: func_odbc.c:840
#define ast_strdup(a)
Definition: astmm.h:109
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
#define ast_set_flag(p, flag)
Definition: utils.h:70
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
static int unload_module(void)
Definition: func_odbc.c:1510
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
static struct ast_threadstorage sql_buf
Definition: func_odbc.c:143
static int exec_odbcfinish(struct ast_channel *chan, const char *data)
Definition: func_odbc.c:848
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Definition: pbx.c:4468
Definition: cli.h:146
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:837
Configuration File Parser.
static int reload(void)
Definition: func_odbc.c:1536
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:900
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:780
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
Structure for a data store object.
Definition: datastore.h:54
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:2604
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
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...
Definition: pbx.c:10513
int value
Definition: syslog.c:39
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
struct odbc_datastore_row::@136 list
static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
Definition: func_odbc.c:168
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
#define ast_verb(level,...)
Definition: logger.h:243
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return * the previous value of *p. This can be used to handle reference co...
Definition: lock.h:603
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:65
static struct ast_custom_function escape_function
Definition: func_odbc.c:796
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:638
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:874
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_asprintf(a, b, c...)
Definition: astmm.h:121
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:703
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static struct ast_datastore_info odbc_info
Definition: func_odbc.c:122
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:211
int ast_str_make_space(struct ast_str **buf, size_t new_len)
Definition: strings.h:588
const int fd
Definition: cli.h:153
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:249
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
static int load_module(void)
Definition: func_odbc.c:1465
static int resultcount
Definition: func_odbc.c:141
static struct ast_threadstorage coldata_buf
Definition: func_odbc.c:145
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
ODBC resource manager.
static char * cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1101
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
const char *const * argv
Definition: cli.h:155
static const char desc[]
Definition: cdr_radius.c:85
static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
Definition: func_odbc.c:420
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
#define AST_RWLIST_EMPTY
Definition: linkedlists.h:451
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_odbc.c:802
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:594
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
#define CLI_SHOWUSAGE
Definition: cli.h:44
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
odbc_option_flags
Definition: func_odbc.c:103
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static struct ast_threadstorage sql2_buf
Definition: func_odbc.c:144
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
Definition: datastore.c:98
#define LOG_NOTICE
Definition: logger.h:133
static char * synopsis
Definition: func_enum.c:156
static char * cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: func_odbc.c:1311
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static void odbc_datastore_free(void *data)
Definition: func_odbc.c:150
#define ast_channel_unlock(chan)
Definition: channel.h:2467
#define CLI_FAILURE
Definition: cli.h:45
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:611
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:123
const char * word
Definition: cli.h:157
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:829
static char * dsn
Definition: cdr_odbc.c:50
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:367
Structure used to handle boolean flags.
Definition: utils.h:200
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
const char * usage
Definition: cli.h:171
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...
Definition: pbx.c:10546
static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
Definition: func_odbc.c:879
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:436
#define CLI_SUCCESS
Definition: cli.h:43
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
void * data
Definition: datastore.h:56
static struct ast_cli_entry cli_func_odbc[]
Definition: func_odbc.c:1460
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
char names[0]
Definition: func_odbc.c:136
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
const int pos
Definition: cli.h:158
#define ast_realloc(a, b)
Definition: astmm.h:103
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
const char * name
Definition: pbx.h:96
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:718
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
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:669
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:1112
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static char * config
Definition: func_odbc.c:101
static struct ast_threadstorage colnames_buf
Definition: func_odbc.c:146
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2590
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:253
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2599
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
jack_status_t status
Definition: app_jack.c:143
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
static int free_acf_query(struct acf_odbc_query *query)
Definition: func_odbc.c:862