diff -ruN asterisk-1.2.0-old/apps/app_sql_mysql.c asterisk-1.2.0-new/apps/app_sql_mysql.c --- asterisk-1.2.0-old/apps/app_sql_mysql.c 1970-01-01 01:00:00.000000000 +0100 +++ asterisk-1.2.0-new/apps/app_sql_mysql.c 2005-06-07 18:36:28.000000000 +0200 @@ -0,0 +1,445 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Connect to MySQL + * + * Copyright (C) 2004, Constantine Filin and Christos Ricudis + * + * Christos Ricudis <ricudis@itc.auth.gr> + * Constantine Filin <cf@intermedia.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> + +#include <mysql.h> + +#include <asterisk/file.h> +#include <asterisk/logger.h> +#include <asterisk/channel.h> +#include <asterisk/pbx.h> +#include <asterisk/module.h> +#include <asterisk/linkedlists.h> +#include <asterisk/chanvars.h> +#include <asterisk/lock.h> + +#define EXTRA_LOG 0 + +static char *tdesc = "Simple Mysql Interface"; + +static char *app = "MYSQL"; + +static char *synopsis = "Do several mySQLy things"; + +static char *descrip = +"MYSQL(): Do several mySQLy things\n" +"Syntax:\n" +" MYSQL(Connect connid dhhost dbuser dbpass dbname)\n" +" Connects to a database. Arguments contain standard MySQL parameters\n" +" passed to function mysql_real_connect. Connection identifer returned\n" +" in ${var}\n" +" MYSQL(Query resultid ${connid} query-string)\n" +" Executes standard MySQL query contained in query-string using established\n" +" connection identified by ${connection_identifier}. Result of query is\n" +" is stored in ${var}.\n" +" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n" +" Fetches a single row from a result set contained in ${result_identifier}.\n" +" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n" +" if additional rows exist in result set.\n" +" MYSQL(Clear ${resultid})\n" +" Frees memory and datastructures associated with result set.\n" +" MYSQL(Disconnect ${connid})\n" +" Disconnects from named connection to MySQL.\n" +" On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n"; + +/* +EXAMPLES OF USE : + +exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit) +exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM}) +exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2) +exten => s,5,GotoIf(${fetchid}?6:8) +exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.") +exten => s,7,Goto(s,4) +exten => s,8,MYSQL(Clear ${resultid}) +exten => s,9,MYSQL(Disconnect ${connid}) +*/ + +STANDARD_LOCAL_USER; +LOCAL_USER_DECL; + +AST_MUTEX_DEFINE_STATIC(_mysql_mutex); + +#define AST_MYSQL_ID_DUMMY 0 +#define AST_MYSQL_ID_CONNID 1 +#define AST_MYSQL_ID_RESID 2 +#define AST_MYSQL_ID_FETCHID 3 + +struct ast_MYSQL_id { + int identifier_type; /* 0=dummy, 1=connid, 2=resultid */ + int identifier; + void *data; + AST_LIST_ENTRY(ast_MYSQL_id) entries; +} *ast_MYSQL_id; + +AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head; + +/* helpful procs */ +static void *find_identifier(int identifier,int identifier_type) { + struct MYSQLidshead *headp; + struct ast_MYSQL_id *i; + void *res=NULL; + int found=0; + + headp=&_mysql_ids_head; + + if (AST_LIST_LOCK(headp)) { + ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); + } else { + AST_LIST_TRAVERSE(headp,i,entries) { + if ((i->identifier==identifier) && (i->identifier_type==identifier_type)) { + found=1; + res=i->data; + break; + } + } + if (!found) { + ast_log(LOG_WARNING,"Identifier %d, identifier_type %d not found in identifier list\n",identifier,identifier_type); + } + AST_LIST_UNLOCK(headp); + } + + return res; +} + +static int add_identifier(int identifier_type,void *data) { + struct ast_MYSQL_id *i,*j; + struct MYSQLidshead *headp; + int maxidentifier=0; + + headp=&_mysql_ids_head; + i=NULL; + j=NULL; + + if (AST_LIST_LOCK(headp)) { + ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); + return(-1); + } else { + i=malloc(sizeof(struct ast_MYSQL_id)); + AST_LIST_TRAVERSE(headp,j,entries) { + if (j->identifier>maxidentifier) { + maxidentifier=j->identifier; + } + } + i->identifier=maxidentifier+1; + i->identifier_type=identifier_type; + i->data=data; + AST_LIST_INSERT_HEAD(headp,i,entries); + AST_LIST_UNLOCK(headp); + } + return i->identifier; +} + +static int del_identifier(int identifier,int identifier_type) { + struct ast_MYSQL_id *i; + struct MYSQLidshead *headp; + int found=0; + + headp=&_mysql_ids_head; + + if (AST_LIST_LOCK(headp)) { + ast_log(LOG_WARNING,"Unable to lock identifiers list\n"); + } else { + AST_LIST_TRAVERSE(headp,i,entries) { + if ((i->identifier==identifier) && + (i->identifier_type==identifier_type)) { + AST_LIST_REMOVE(headp,i,entries); + free(i); + found=1; + break; + } + } + AST_LIST_UNLOCK(headp); + } + + if (found==0) { + ast_log(LOG_WARNING,"Could not find identifier %d, identifier_type %d in list to delete\n",identifier,identifier_type); + return(-1); + } else { + return(0); + } +} + +static int set_asterisk_int(struct ast_channel *chan, char *varname, int id) { + if( id>=0 ) { + char s[100] = ""; + snprintf(s, sizeof(s)-1, "%d", id); +#if EXTRA_LOG + ast_log(LOG_WARNING,"MYSQL: setting var '%s' to value '%s'\n",varname,s); +#endif + pbx_builtin_setvar_helper(chan,varname,s); + } + return id; +} + +static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data) { + return set_asterisk_int(chan,varname,add_identifier(identifier_type,data)); +} + +static int safe_scan_int( char** data, char* delim, int def ) { + char* end; + int res = def; + char* s = strsep(data,delim); + if( s ) { + res = strtol(s,&end,10); + if (*end) res = def; /* not an integer */ + } + return res; +} + +/* MYSQL operations */ +static int aMYSQL_connect(struct ast_channel *chan, char *data) { + + MYSQL *mysql; + + char *connid_var; + char *dbhost; + char *dbuser; + char *dbpass; + char *dbname; + + strsep(&data," "); // eat the first token, we already know it :P + + connid_var=strsep(&data," "); + dbhost=strsep(&data," "); + dbuser=strsep(&data," "); + dbpass=strsep(&data," "); + dbname=strsep(&data,"\n"); + + if( connid_var && dbhost && dbuser && dbpass && dbname ) { + mysql = mysql_init(NULL); + if (mysql) { + if (mysql_real_connect(mysql,dbhost,dbuser,dbpass,dbname,0,NULL,0)) { + add_identifier_and_set_asterisk_int(chan,connid_var,AST_MYSQL_ID_CONNID,mysql); + return 0; + } + else { + ast_log(LOG_WARNING,"mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed\n",dbhost,dbuser,dbname); + } + } + else { + ast_log(LOG_WARNING,"myslq_init returned NULL\n"); + } + } + else { + ast_log(LOG_WARNING,"MYSQL(connect is missing some arguments\n"); + } + + return -1; +} + +static int aMYSQL_query(struct ast_channel *chan, char *data) { + + MYSQL *mysql; + MYSQL_RES *mysqlres; + + char *resultid_var; + int connid; + char *querystring; + + strsep(&data," "); // eat the first token, we already know it :P + + resultid_var = strsep(&data," "); + connid = safe_scan_int(&data," ",-1); + querystring = strsep(&data,"\n"); + + if (resultid_var && (connid>=0) && querystring) { + if ((mysql=find_identifier(connid,AST_MYSQL_ID_CONNID))!=NULL) { + mysql_query(mysql,querystring); + if ((mysqlres=mysql_use_result(mysql))!=NULL) { + add_identifier_and_set_asterisk_int(chan,resultid_var,AST_MYSQL_ID_RESID,mysqlres); + return 0; + } + else if( mysql_field_count(mysql)==0 ) { + return 0; // See http://dev.mysql.com/doc/mysql/en/mysql_field_count.html + } + else { + ast_log(LOG_WARNING,"aMYSQL_query: mysql_store_result() failed on query %s\n",querystring); + } + } + else { + ast_log(LOG_WARNING,"aMYSQL_query: Invalid connection identifier %d passed in aMYSQL_query\n",connid); + } + } + else { + ast_log(LOG_WARNING,"aMYSQL_query: missing some arguments\n"); + } + + return -1; +} + + +static int aMYSQL_fetch(struct ast_channel *chan, char *data) { + + MYSQL_RES *mysqlres; + MYSQL_ROW mysqlrow; + + char *fetchid_var,*s5,*s6; + int resultid,numFields,j; + + strsep(&data," "); // eat the first token, we already know it :P + + fetchid_var = strsep(&data," "); + resultid = safe_scan_int(&data," ",-1); + + if (fetchid_var && (resultid>=0) ) { + if ((mysqlres=find_identifier(resultid,AST_MYSQL_ID_RESID))!=NULL) { + /* Grab the next row */ + if ((mysqlrow=mysql_fetch_row(mysqlres))!=NULL) { + numFields=mysql_num_fields(mysqlres); + for (j=0;j<numFields;j++) { + s5=strsep(&data," "); + if (s5==NULL) { + ast_log(LOG_WARNING,"ast_MYSQL_fetch: More fields (%d) than variables (%d)\n",numFields,j); + break; + } + s6=mysqlrow[j]; + pbx_builtin_setvar_helper(chan,s5, s6 ? s6 : "NULL"); + } +#ifdef EXTRA_LOG + ast_log(LOG_WARNING,"ast_MYSQL_fetch: numFields=%d\n",numFields); +#endif + set_asterisk_int(chan,fetchid_var,1); // try more rows + } else { +#if EXTRA_LOG + ast_log(LOG_WARNING,"ast_MYSQL_fetch : EOF\n"); +#endif + set_asterisk_int(chan,fetchid_var,0); // no more rows + } + return 0; + } + else { + ast_log(LOG_WARNING,"aMYSQL_fetch: Invalid result identifier %d passed\n",resultid); + } + } + else { + ast_log(LOG_WARNING,"aMYSQL_fetch: missing some arguments\n"); + } + + return -1; +} + +static int aMYSQL_clear(struct ast_channel *chan, char *data) { + + MYSQL_RES *mysqlres; + + int id; + strsep(&data," "); // eat the first token, we already know it :P + id = safe_scan_int(&data," \n",-1); + if ((mysqlres=find_identifier(id,AST_MYSQL_ID_RESID))==NULL) { + ast_log(LOG_WARNING,"Invalid result identifier %d passed in aMYSQL_clear\n",id); + } else { + mysql_free_result(mysqlres); + del_identifier(id,AST_MYSQL_ID_RESID); + } + + return 0; +} + +static int aMYSQL_disconnect(struct ast_channel *chan, char *data) { + + MYSQL *mysql; + int id; + strsep(&data," "); // eat the first token, we already know it :P + + id = safe_scan_int(&data," \n",-1); + if ((mysql=find_identifier(id,AST_MYSQL_ID_CONNID))==NULL) { + ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aMYSQL_disconnect\n",id); + } else { + mysql_close(mysql); + del_identifier(id,AST_MYSQL_ID_CONNID); + } + + return 0; +} + +static int MYSQL_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + int result; + char sresult[10]; + +#if EXTRA_LOG + fprintf(stderr,"MYSQL_exec: data=%s\n",(char*)data); +#endif + + if (!data) { + ast_log(LOG_WARNING, "APP_MYSQL requires an argument (see manual)\n"); + return -1; + } + + LOCAL_USER_ADD(u); + result=0; + + ast_mutex_lock(&_mysql_mutex); + + if (strncasecmp("connect",data,strlen("connect"))==0) { + result=aMYSQL_connect(chan,ast_strdupa(data)); + } else if (strncasecmp("query",data,strlen("query"))==0) { + result=aMYSQL_query(chan,ast_strdupa(data)); + } else if (strncasecmp("fetch",data,strlen("fetch"))==0) { + result=aMYSQL_fetch(chan,ast_strdupa(data)); + } else if (strncasecmp("clear",data,strlen("clear"))==0) { + result=aMYSQL_clear(chan,ast_strdupa(data)); + } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) { + result=aMYSQL_disconnect(chan,ast_strdupa(data)); + } else { + ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n",(char *)data); + result=-1; + } + + ast_mutex_unlock(&_mysql_mutex); + + LOCAL_USER_REMOVE(u); + snprintf(sresult, sizeof(sresult), "%d", result); + pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult); + return 0; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + struct MYSQLidshead *headp = &_mysql_ids_head; + AST_LIST_HEAD_INIT(headp); + return ast_register_application(app, MYSQL_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +}