app_mysql.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2004, Constantine Filin and Christos Ricudis
  5. *
  6. * Christos Ricudis <ricudis@itc.auth.gr>
  7. * Constantine Filin <cf@intermedia.net>
  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. * \file
  21. * \brief MYSQL dialplan application
  22. * \ingroup applications
  23. */
  24. /*** MODULEINFO
  25. <depend>mysqlclient</depend>
  26. <defaultenabled>no</defaultenabled>
  27. <support_level>deprecated</support_level>
  28. <replacement>func_odbc</replacement>
  29. ***/
  30. #include "asterisk.h"
  31. #include <mysql/mysql.h>
  32. #include "asterisk/file.h"
  33. #include "asterisk/logger.h"
  34. #include "asterisk/channel.h"
  35. #include "asterisk/pbx.h"
  36. #include "asterisk/module.h"
  37. #include "asterisk/linkedlists.h"
  38. #include "asterisk/chanvars.h"
  39. #include "asterisk/lock.h"
  40. #include "asterisk/options.h"
  41. #include "asterisk/app.h"
  42. #include "asterisk/config.h"
  43. #define EXTRA_LOG 0
  44. enum { NULLSTRING, NULLVALUE, EMPTYSTRING } nullvalue = NULLSTRING;
  45. static const char app[] = "MYSQL";
  46. static const char synopsis[] = "Do several mySQLy things";
  47. static const char descrip[] =
  48. "MYSQL(): Do several mySQLy things\n"
  49. "Syntax:\n"
  50. " MYSQL(Set timeout <num>)\n"
  51. " Set the connection timeout, in seconds.\n"
  52. " MYSQL(Connect connid dhhost dbuser dbpass dbname [dbcharset])\n"
  53. " Connects to a database. Arguments contain standard MySQL parameters\n"
  54. " passed to function mysql_real_connect. Optional parameter dbcharset\n"
  55. " defaults to 'latin1'. Connection identifer returned in ${connid}\n"
  56. " MYSQL(Query resultid ${connid} query-string)\n"
  57. " Executes standard MySQL query contained in query-string using established\n"
  58. " connection identified by ${connid}. Result of query is stored in ${resultid}.\n"
  59. " MYSQL(Nextresult resultid ${connid}\n"
  60. " If last query returned more than one result set, it stores the next\n"
  61. " result set in ${resultid}. It's useful with stored procedures\n"
  62. " MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n"
  63. " Fetches a single row from a result set contained in ${result_identifier}.\n"
  64. " Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n"
  65. " if additional rows exist in result set.\n"
  66. " MYSQL(Clear ${resultid})\n"
  67. " Frees memory and datastructures associated with result set.\n"
  68. " MYSQL(Disconnect ${connid})\n"
  69. " Disconnects from named connection to MySQL.\n"
  70. " On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n";
  71. /*
  72. EXAMPLES OF USE :
  73. exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit utf8)
  74. exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM})
  75. exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2)
  76. exten => s,5,GotoIf(${fetchid}?6:8)
  77. exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.")
  78. exten => s,7,Goto(s,4)
  79. exten => s,8,MYSQL(Clear ${resultid})
  80. exten => s,9,MYSQL(Disconnect ${connid})
  81. */
  82. AST_MUTEX_DEFINE_STATIC(_mysql_mutex);
  83. #define MYSQL_CONFIG "app_mysql.conf"
  84. #define MYSQL_CONFIG_OLD "mysql.conf"
  85. #define AST_MYSQL_ID_DUMMY 0
  86. #define AST_MYSQL_ID_CONNID 1
  87. #define AST_MYSQL_ID_RESID 2
  88. #define AST_MYSQL_ID_FETCHID 3
  89. static int autoclear = 0;
  90. static void mysql_ds_destroy(void *data);
  91. static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
  92. static struct ast_datastore_info mysql_ds_info = {
  93. .type = "APP_ADDON_SQL_MYSQL",
  94. .destroy = mysql_ds_destroy,
  95. .chan_fixup = mysql_ds_fixup,
  96. };
  97. struct ast_MYSQL_id {
  98. struct ast_channel *owner;
  99. int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
  100. int identifier;
  101. void *data;
  102. AST_LIST_ENTRY(ast_MYSQL_id) entries;
  103. } *ast_MYSQL_id;
  104. AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head;
  105. static void mysql_ds_destroy(void *data)
  106. {
  107. /* Destroy any IDs owned by the channel */
  108. struct ast_MYSQL_id *i;
  109. if (AST_LIST_LOCK(&_mysql_ids_head)) {
  110. ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
  111. } else {
  112. AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
  113. if (i->owner == data) {
  114. AST_LIST_REMOVE_CURRENT(entries);
  115. if (i->identifier_type == AST_MYSQL_ID_CONNID) {
  116. /* Drop connection */
  117. mysql_close(i->data);
  118. } else if (i->identifier_type == AST_MYSQL_ID_RESID) {
  119. /* Drop result */
  120. mysql_free_result(i->data);
  121. }
  122. ast_free(i);
  123. }
  124. }
  125. AST_LIST_TRAVERSE_SAFE_END
  126. AST_LIST_UNLOCK(&_mysql_ids_head);
  127. }
  128. }
  129. static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
  130. {
  131. /* Destroy any IDs owned by the channel */
  132. struct ast_MYSQL_id *i;
  133. if (AST_LIST_LOCK(&_mysql_ids_head)) {
  134. ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
  135. } else {
  136. AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
  137. if (i->owner == data) {
  138. AST_LIST_REMOVE_CURRENT(entries);
  139. if (i->identifier_type == AST_MYSQL_ID_CONNID) {
  140. /* Drop connection */
  141. mysql_close(i->data);
  142. } else if (i->identifier_type == AST_MYSQL_ID_RESID) {
  143. /* Drop result */
  144. mysql_free_result(i->data);
  145. }
  146. ast_free(i);
  147. }
  148. }
  149. AST_LIST_TRAVERSE_SAFE_END
  150. AST_LIST_UNLOCK(&_mysql_ids_head);
  151. }
  152. }
  153. /* helpful procs */
  154. static void *find_identifier(int identifier, int identifier_type)
  155. {
  156. struct MYSQLidshead *headp = &_mysql_ids_head;
  157. struct ast_MYSQL_id *i;
  158. void *res=NULL;
  159. int found=0;
  160. if (AST_LIST_LOCK(headp)) {
  161. ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
  162. } else {
  163. AST_LIST_TRAVERSE(headp, i, entries) {
  164. if ((i->identifier == identifier) && (i->identifier_type == identifier_type)) {
  165. found = 1;
  166. res = i->data;
  167. break;
  168. }
  169. }
  170. if (!found) {
  171. ast_log(LOG_WARNING, "Identifier %d, identifier_type %d not found in identifier list\n", identifier, identifier_type);
  172. }
  173. AST_LIST_UNLOCK(headp);
  174. }
  175. return res;
  176. }
  177. static int add_identifier(struct ast_channel *chan, int identifier_type, void *data)
  178. {
  179. struct ast_MYSQL_id *i = NULL, *j = NULL;
  180. struct MYSQLidshead *headp = &_mysql_ids_head;
  181. int maxidentifier = 0;
  182. if (AST_LIST_LOCK(headp)) {
  183. ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
  184. return -1;
  185. } else {
  186. i = malloc(sizeof(*i));
  187. AST_LIST_TRAVERSE(headp, j, entries) {
  188. if (j->identifier > maxidentifier) {
  189. maxidentifier = j->identifier;
  190. }
  191. }
  192. i->identifier = maxidentifier + 1;
  193. i->identifier_type = identifier_type;
  194. i->data = data;
  195. i->owner = chan;
  196. AST_LIST_INSERT_HEAD(headp, i, entries);
  197. AST_LIST_UNLOCK(headp);
  198. }
  199. return i->identifier;
  200. }
  201. static int del_identifier(int identifier, int identifier_type)
  202. {
  203. struct ast_MYSQL_id *i;
  204. struct MYSQLidshead *headp = &_mysql_ids_head;
  205. int found = 0;
  206. if (AST_LIST_LOCK(headp)) {
  207. ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
  208. } else {
  209. AST_LIST_TRAVERSE(headp, i, entries) {
  210. if ((i->identifier == identifier) &&
  211. (i->identifier_type == identifier_type)) {
  212. AST_LIST_REMOVE(headp, i, entries);
  213. free(i);
  214. found = 1;
  215. break;
  216. }
  217. }
  218. AST_LIST_UNLOCK(headp);
  219. }
  220. if (found == 0) {
  221. ast_log(LOG_WARNING, "Could not find identifier %d, identifier_type %d in list to delete\n", identifier, identifier_type);
  222. return -1;
  223. } else {
  224. return 0;
  225. }
  226. }
  227. static int set_asterisk_int(struct ast_channel *chan, char *varname, int id)
  228. {
  229. if (id >= 0) {
  230. char s[12] = "";
  231. snprintf(s, sizeof(s), "%d", id);
  232. ast_debug(5, "MYSQL: setting var '%s' to value '%s'\n", varname, s);
  233. pbx_builtin_setvar_helper(chan, varname, s);
  234. }
  235. return id;
  236. }
  237. static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data)
  238. {
  239. return set_asterisk_int(chan, varname, add_identifier(chan, identifier_type, data));
  240. }
  241. static int safe_scan_int(char **data, char *delim, int def)
  242. {
  243. char *end;
  244. int res = def;
  245. char *s = strsep(data, delim);
  246. if (s) {
  247. res = strtol(s, &end, 10);
  248. if (*end)
  249. res = def; /* not an integer */
  250. }
  251. return res;
  252. }
  253. static int aMYSQL_set(struct ast_channel *chan, char *data)
  254. {
  255. char *var, *tmp;
  256. AST_DECLARE_APP_ARGS(args,
  257. AST_APP_ARG(set);
  258. AST_APP_ARG(variable);
  259. AST_APP_ARG(value);
  260. );
  261. AST_NONSTANDARD_APP_ARGS(args, data, ' ');
  262. if (args.argc == 3) {
  263. var = alloca(6 + strlen(args.variable) + 1);
  264. sprintf(var, "MYSQL_%s", args.variable);
  265. /* Make the parameter case-insensitive */
  266. for (tmp = var + 6; *tmp; tmp++)
  267. *tmp = toupper(*tmp);
  268. pbx_builtin_setvar_helper(chan, var, args.value);
  269. }
  270. return 0;
  271. }
  272. /* MYSQL operations */
  273. static int aMYSQL_connect(struct ast_channel *chan, char *data)
  274. {
  275. AST_DECLARE_APP_ARGS(args,
  276. AST_APP_ARG(connect);
  277. AST_APP_ARG(connid);
  278. AST_APP_ARG(dbhost);
  279. AST_APP_ARG(dbuser);
  280. AST_APP_ARG(dbpass);
  281. AST_APP_ARG(dbname);
  282. AST_APP_ARG(dbcharset);
  283. );
  284. MYSQL *mysql;
  285. int timeout;
  286. const char *ctimeout;
  287. AST_NONSTANDARD_APP_ARGS(args, data, ' ');
  288. if (args.argc < 6) {
  289. ast_log(LOG_WARNING, "MYSQL_connect is missing some arguments\n");
  290. return -1;
  291. }
  292. if (!(mysql = mysql_init(NULL))) {
  293. ast_log(LOG_WARNING, "mysql_init returned NULL\n");
  294. return -1;
  295. }
  296. ctimeout = pbx_builtin_getvar_helper(chan, "MYSQL_TIMEOUT");
  297. if (ctimeout && sscanf(ctimeout, "%30d", &timeout) == 1) {
  298. mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout);
  299. }
  300. if(args.dbcharset && strlen(args.dbcharset) > 2){
  301. char set_names[255];
  302. char statement[512];
  303. snprintf(set_names, sizeof(set_names), "SET NAMES %s", args.dbcharset);
  304. mysql_real_escape_string(mysql, statement, set_names, sizeof(set_names));
  305. mysql_options(mysql, MYSQL_INIT_COMMAND, set_names);
  306. mysql_options(mysql, MYSQL_SET_CHARSET_NAME, args.dbcharset);
  307. }
  308. if (! mysql_real_connect(mysql, args.dbhost, args.dbuser, args.dbpass, args.dbname, 0, NULL,
  309. #ifdef CLIENT_MULTI_STATEMENTS
  310. CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS
  311. #elif defined(CLIENT_MULTI_QUERIES)
  312. CLIENT_MULTI_QUERIES
  313. #else
  314. 0
  315. #endif
  316. )) {
  317. ast_log(LOG_WARNING, "mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed(%d): %s\n",
  318. args.dbhost, args.dbuser, args.dbname, mysql_errno(mysql), mysql_error(mysql));
  319. return -1;
  320. }
  321. add_identifier_and_set_asterisk_int(chan, args.connid, AST_MYSQL_ID_CONNID, mysql);
  322. return 0;
  323. }
  324. static int aMYSQL_query(struct ast_channel *chan, char *data)
  325. {
  326. AST_DECLARE_APP_ARGS(args,
  327. AST_APP_ARG(query);
  328. AST_APP_ARG(resultid);
  329. AST_APP_ARG(connid);
  330. AST_APP_ARG(sql);
  331. );
  332. MYSQL *mysql;
  333. MYSQL_RES *mysqlres;
  334. int connid;
  335. int mysql_query_res;
  336. AST_NONSTANDARD_APP_ARGS(args, data, ' ');
  337. if (args.argc != 4 || (connid = atoi(args.connid)) == 0) {
  338. ast_log(LOG_WARNING, "missing some arguments\n");
  339. return -1;
  340. }
  341. if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
  342. ast_log(LOG_WARNING, "Invalid connection identifier %s passed in aMYSQL_query\n", args.connid);
  343. return -1;
  344. }
  345. if ((mysql_query_res = mysql_query(mysql, args.sql)) != 0) {
  346. ast_log(LOG_WARNING, "aMYSQL_query: mysql_query failed. Error: %s\n", mysql_error(mysql));
  347. return -1;
  348. }
  349. if ((mysqlres = mysql_store_result(mysql))) {
  350. add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
  351. return 0;
  352. } else if (!mysql_field_count(mysql)) {
  353. return 0;
  354. } else
  355. ast_log(LOG_WARNING, "mysql_store_result() failed on query %s\n", args.sql);
  356. return -1;
  357. }
  358. static int aMYSQL_nextresult(struct ast_channel *chan, char *data)
  359. {
  360. MYSQL *mysql;
  361. MYSQL_RES *mysqlres;
  362. AST_DECLARE_APP_ARGS(args,
  363. AST_APP_ARG(nextresult);
  364. AST_APP_ARG(resultid);
  365. AST_APP_ARG(connid);
  366. );
  367. int connid = -1;
  368. AST_NONSTANDARD_APP_ARGS(args, data, ' ');
  369. sscanf(args.connid, "%30d", &connid);
  370. if (args.argc != 3 || connid <= 0) {
  371. ast_log(LOG_WARNING, "missing some arguments\n");
  372. return -1;
  373. }
  374. if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
  375. ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_query\n", connid);
  376. return -1;
  377. }
  378. if (mysql_more_results(mysql)) {
  379. mysql_next_result(mysql);
  380. if ((mysqlres = mysql_store_result(mysql))) {
  381. add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
  382. return 0;
  383. } else if (!mysql_field_count(mysql)) {
  384. return 0;
  385. } else
  386. ast_log(LOG_WARNING, "mysql_store_result() failed on storing next_result\n");
  387. } else
  388. ast_log(LOG_WARNING, "mysql_more_results() result set has no more results\n");
  389. return 0;
  390. }
  391. static int aMYSQL_fetch(struct ast_channel *chan, char *data)
  392. {
  393. MYSQL_RES *mysqlres;
  394. MYSQL_ROW mysqlrow;
  395. AST_DECLARE_APP_ARGS(args,
  396. AST_APP_ARG(fetch);
  397. AST_APP_ARG(resultvar);
  398. AST_APP_ARG(fetchid);
  399. AST_APP_ARG(vars);
  400. );
  401. char *s5, *parse;
  402. int resultid = -1, numFields, j;
  403. parse = ast_strdupa(data);
  404. AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
  405. sscanf(args.fetchid, "%30d", &resultid);
  406. if (args.resultvar && (resultid >= 0) ) {
  407. if ((mysqlres = find_identifier(resultid, AST_MYSQL_ID_RESID)) != NULL) {
  408. /* Grab the next row */
  409. if ((mysqlrow = mysql_fetch_row(mysqlres)) != NULL) {
  410. numFields = mysql_num_fields(mysqlres);
  411. for (j = 0; j < numFields; j++) {
  412. s5 = strsep(&args.vars, " ");
  413. if (s5 == NULL) {
  414. ast_log(LOG_WARNING, "ast_MYSQL_fetch: More fields (%d) than variables (%d)\n", numFields, j);
  415. break;
  416. }
  417. pbx_builtin_setvar_helper(chan, s5, mysqlrow[j] ? mysqlrow[j] :
  418. nullvalue == NULLSTRING ? "NULL" :
  419. nullvalue == EMPTYSTRING ? "" :
  420. NULL);
  421. }
  422. ast_debug(5, "ast_MYSQL_fetch: numFields=%d\n", numFields);
  423. set_asterisk_int(chan, args.resultvar, 1); /* try more rows */
  424. } else {
  425. ast_debug(5, "ast_MYSQL_fetch : EOF\n");
  426. set_asterisk_int(chan, args.resultvar, 0); /* no more rows */
  427. }
  428. return 0;
  429. } else {
  430. set_asterisk_int(chan, args.resultvar, 0);
  431. ast_log(LOG_WARNING, "aMYSQL_fetch: Invalid result identifier %d passed\n", resultid);
  432. }
  433. } else {
  434. ast_log(LOG_WARNING, "aMYSQL_fetch: missing some arguments\n");
  435. }
  436. return -1;
  437. }
  438. static int aMYSQL_clear(struct ast_channel *chan, char *data)
  439. {
  440. MYSQL_RES *mysqlres;
  441. int id;
  442. strsep(&data, " "); /* eat the first token, we already know it :P */
  443. id = safe_scan_int(&data, " \n", -1);
  444. if ((mysqlres = find_identifier(id, AST_MYSQL_ID_RESID)) == NULL) {
  445. ast_log(LOG_WARNING, "Invalid result identifier %d passed in aMYSQL_clear\n", id);
  446. } else {
  447. mysql_free_result(mysqlres);
  448. del_identifier(id, AST_MYSQL_ID_RESID);
  449. }
  450. return 0;
  451. }
  452. static int aMYSQL_disconnect(struct ast_channel *chan, char *data)
  453. {
  454. MYSQL *mysql;
  455. int id;
  456. strsep(&data, " "); /* eat the first token, we already know it :P */
  457. id = safe_scan_int(&data, " \n", -1);
  458. if ((mysql = find_identifier(id, AST_MYSQL_ID_CONNID)) == NULL) {
  459. ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_disconnect\n", id);
  460. } else {
  461. mysql_close(mysql);
  462. del_identifier(id, AST_MYSQL_ID_CONNID);
  463. }
  464. return 0;
  465. }
  466. static int MYSQL_exec(struct ast_channel *chan, const char *data)
  467. {
  468. int result;
  469. char sresult[10];
  470. ast_debug(5, "MYSQL: data=%s\n", data);
  471. if (!data) {
  472. ast_log(LOG_WARNING, "MYSQL requires an argument (see manual)\n");
  473. return -1;
  474. }
  475. result = 0;
  476. if (autoclear) {
  477. struct ast_datastore *mysql_store = NULL;
  478. ast_channel_lock(chan);
  479. mysql_store = ast_channel_datastore_find(chan, &mysql_ds_info, NULL);
  480. if (!mysql_store) {
  481. if (!(mysql_store = ast_datastore_alloc(&mysql_ds_info, NULL))) {
  482. ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
  483. } else {
  484. mysql_store->data = chan;
  485. ast_channel_datastore_add(chan, mysql_store);
  486. }
  487. }
  488. ast_channel_unlock(chan);
  489. }
  490. ast_mutex_lock(&_mysql_mutex);
  491. if (strncasecmp("connect", data, strlen("connect")) == 0) {
  492. result = aMYSQL_connect(chan, ast_strdupa(data));
  493. } else if (strncasecmp("query", data, strlen("query")) == 0) {
  494. result = aMYSQL_query(chan, ast_strdupa(data));
  495. } else if (strncasecmp("nextresult", data, strlen("nextresult")) == 0) {
  496. result = aMYSQL_nextresult(chan, ast_strdupa(data));
  497. } else if (strncasecmp("fetch", data, strlen("fetch")) == 0) {
  498. result = aMYSQL_fetch(chan, ast_strdupa(data));
  499. } else if (strncasecmp("clear", data, strlen("clear")) == 0) {
  500. result = aMYSQL_clear(chan, ast_strdupa(data));
  501. } else if (strncasecmp("disconnect", data, strlen("disconnect")) == 0) {
  502. result = aMYSQL_disconnect(chan, ast_strdupa(data));
  503. } else if (strncasecmp("set", data, 3) == 0) {
  504. result = aMYSQL_set(chan, ast_strdupa(data));
  505. } else {
  506. ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n", data);
  507. result = -1;
  508. }
  509. ast_mutex_unlock(&_mysql_mutex);
  510. snprintf(sresult, sizeof(sresult), "%d", result);
  511. pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult);
  512. return 0;
  513. }
  514. static int unload_module(void)
  515. {
  516. return ast_unregister_application(app);
  517. }
  518. static int load_module(void)
  519. {
  520. struct MYSQLidshead *headp = &_mysql_ids_head;
  521. struct ast_flags config_flags = { 0 };
  522. struct ast_config *cfg = ast_config_load(MYSQL_CONFIG, config_flags);
  523. const char *temp;
  524. if (!cfg) {
  525. /* Backwards compatibility ftw */
  526. cfg = ast_config_load(MYSQL_CONFIG_OLD, config_flags);
  527. }
  528. if (cfg) {
  529. if ((temp = ast_variable_retrieve(cfg, "general", "nullvalue"))) {
  530. if (!strcasecmp(temp, "nullstring")) {
  531. nullvalue = NULLSTRING;
  532. } else if (!strcasecmp(temp, "emptystring")) {
  533. nullvalue = EMPTYSTRING;
  534. } else if (!strcasecmp(temp, "null")) {
  535. nullvalue = NULLVALUE;
  536. } else {
  537. ast_log(LOG_WARNING, "Illegal value for 'nullvalue': '%s' (must be 'nullstring', 'null', or 'emptystring')\n", temp);
  538. }
  539. }
  540. if ((temp = ast_variable_retrieve(cfg, "general", "autoclear")) && ast_true(temp)) {
  541. autoclear = 1;
  542. }
  543. ast_config_destroy(cfg);
  544. }
  545. AST_LIST_HEAD_INIT(headp);
  546. return ast_register_application(app, MYSQL_exec, synopsis, descrip);
  547. }
  548. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Mysql Interface");