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