diff options
author | Mark Spencer <markster@digium.com> | 2002-09-11 17:09:48 +0000 |
---|---|---|
committer | Mark Spencer <markster@digium.com> | 2002-09-11 17:09:48 +0000 |
commit | e42d61fac695100d49c816f55ea96e15d29dd5ed (patch) | |
tree | 25caf9beca82fd5a184a14ae0b6bcccfc908f342 /apps/app_sql_postgres.c | |
parent | bdecee176e307e1bbe9f9302e27d06bd3da87577 (diff) |
Version 0.2.0 from FTP
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@523 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps/app_sql_postgres.c')
-rwxr-xr-x | apps/app_sql_postgres.c | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/apps/app_sql_postgres.c b/apps/app_sql_postgres.c new file mode 100755 index 000000000..628987ad5 --- /dev/null +++ b/apps/app_sql_postgres.c @@ -0,0 +1,546 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Connect to PostgreSQL + * + * Copyright (C) 2002, Christos Ricudis + * + * Christos Ricudis <ricudis@paiko.gr> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#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> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include <pthread.h> +#include "libpq-fe.h" + + + + +static char *tdesc = "Simple PostgreSQL Interface"; + +static char *app = "PGSQL"; + +static char *synopsis = "Do several SQLy things"; + +static char *descrip = +" PGSQL(): Do several SQLy things\n"; + +/* + +Syntax of SQL commands : + + Connect #var option-string + + Connects to a database using the option-string and stores the + connection identifier in $var + + + Query var connection-identifier query-string + + Submits query-string to database backend and stores the result + identifier in ${var} + + + Fetch statusvar result-identifier var1 var2 var3 ... varn + + Fetches a row from the query and stores end-of-table status in + ${statusvar} and columns in ${var1}..${varn} + + + Clear result-identifier + + Clears data structures associated with result-identifier + + + Disconnect connection-identifier + + Disconnects from named connection + + +EXAMPLES OF USE : + +( + $2 = Connection Identifier + $3 = Result Identifier + $4 = Fetch Status Identifier (0 = no more rows) + $5, $6 = Data variables +) + +exten => s,2,PGSQL,"Connect connid host=localhost user=asterisk dbname=credit"; +exten => s,3,PGSQL,"Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${callerid}"; +exten => s,4,PGSQL,"Fetch fetchid ${resultid} datavar1 datavar2"; +exten => s,5,GotoIf,"${fetchid}=1?s|6:s|8"; +exten => s,6,blablabla ${datavar1} ${datavar2} (does blablabla, datavar1 = username, datavar2 = credit); +exten => s,7,Goto,s|4 +exten => s,8,PGSQL,"Clear ${resultid}"; +exten => s,9,PGSQL,"Disconnect ${connid}"; + +*/ + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +extern void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value); + +#define AST_PGSQL_ID_DUMMY 0 +#define AST_PGSQL_ID_CONNID 1 +#define AST_PGSQL_ID_RESID 2 +#define AST_PGSQL_ID_FETCHID 3 + +struct ast_PGSQL_id { + int identifier_type; /* 0=dummy, 1=connid, 2=resultid */ + int identifier; + void *data; + AST_LIST_ENTRY(ast_PGSQL_id) entries; +} *ast_PGSQL_id; + +AST_LIST_HEAD(PGSQLidshead,ast_PGSQL_id) PGSQLidshead; + +static void *find_identifier(int identifier,int identifier_type) { + struct PGSQLidshead *headp; + struct ast_PGSQL_id *i; + void *res=NULL; + int found=0; + + headp=&PGSQLidshead; + + 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_PGSQL_id *i,*j; + struct PGSQLidshead *headp; + int maxidentifier=0; + + headp=&PGSQLidshead; + 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_PGSQL_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_PGSQL_id *i; + struct PGSQLidshead *headp; + int found=0; + + headp=&PGSQLidshead; + + 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,ast_PGSQL_id,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 aPGSQL_connect(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s4; + char s[100]; + char *optionstring; + char *var; + int l; + int res; + PGconn *karoto; + int id; + + + res=0; + l=strlen(data)+2; + s1=malloc(l); + strncpy(s1,data,l); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + var=strtok_r(NULL," ",&ptrptr); + optionstring=strtok_r(NULL,"\n",&ptrptr); + + karoto = PQconnectdb(optionstring); + if (PQstatus(karoto) == CONNECTION_BAD) { + ast_log(LOG_WARNING,"Connection to database using '%s' failed. postgress reports : %s\n", optionstring, + PQerrorMessage(karoto)); + res=-1; + } else { + ast_log(LOG_WARNING,"adding identifier\n"); + id=add_identifier(AST_PGSQL_ID_CONNID,karoto); + s4=&s[0]; + sprintf(s4,"%d",id); + pbx_builtin_setvar_helper(chan,var,s); + } + + free(s1); + return res; +} + +static int aPGSQL_query(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s2,*s3,*s4,*s5; + char s[100]; + char *querystring; + char *var; + int l; + int res,nres; + PGconn *karoto; + PGresult *PGSQLres; + int id,id1; + + + res=0; + l=strlen(data)+2; + s1=malloc(l); + s2=malloc(l); + strcpy(s1,data); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + s3=strtok_r(NULL," ",&ptrptr); + while (1) { // ugly trick to make branches with break; + var=s3; + s4=strtok_r(NULL," ",&ptrptr); + id=atoi(s4); + querystring=strtok_r(NULL,"\n",&ptrptr); + if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) { + ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_query\n",id); + res=-1; + break; + } + PGSQLres=PQexec(karoto,querystring); + if (PGSQLres==NULL) { + ast_log(LOG_WARNING,"aPGSQL_query: Connection Error (connection identifier = %d, error message : %s)\n",id,PQerrorMessage(karoto)); + res=-1; + break; + } + if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE || + PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR || + PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) { + ast_log(LOG_WARNING,"aPGSQL_query: Query Error (connection identifier : %d, error message : %s)\n",id,PQcmdStatus(PGSQLres)); + res=-1; + break; + } + nres=PQnfields(PGSQLres); + id1=add_identifier(AST_PGSQL_ID_RESID,PGSQLres); + s5=&s[0]; + sprintf(s5,"%d",id1); + pbx_builtin_setvar_helper(chan,var,s); + break; + } + + free(s1); + free(s2); + return(res); +} + + +static int aPGSQL_fetch(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s2,*s3,*s4,*s5,*s6,*s7; + char s[100]; + char *var; + int l; + int res; + PGresult *PGSQLres; + int id,id1,i,j,fnd; + int *lalares=NULL; + int nres; + struct ast_var_t *variables; + struct varshead *headp; + + headp=&chan->varshead; + + res=0; + l=strlen(data)+2; + s7=NULL; + s1=malloc(l); + s2=malloc(l); + strcpy(s1,data); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + s3=strtok_r(NULL," ",&ptrptr); + while (1) { // ugly trick to make branches with break; + var=s3; // fetchid + fnd=0; + + AST_LIST_TRAVERSE(headp,variables,entries) { + if (strncasecmp(ast_var_name(variables),s3,strlen(s3))==0) { + s7=ast_var_value(variables); + fnd=1; + break; + } + } + + if (fnd==0) { + s7="0"; + pbx_builtin_setvar_helper(chan,s3,s7); + } + + s4=strtok_r(NULL," ",&ptrptr); + id=atoi(s4); // resultid + if ((PGSQLres=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) { + ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_fetch\n",id); + res=-1; + break; + } + id=atoi(s7); //fetchid + if ((lalares=find_identifier(id,AST_PGSQL_ID_FETCHID))==NULL) { + i=0; + } else { + i=*lalares; + free(lalares); + del_identifier(id,AST_PGSQL_ID_FETCHID); + } + nres=PQnfields(PGSQLres); + ast_log(LOG_WARNING,"ast_PGSQL_fetch : nres = %d i = %d ;\n",nres,i); + for (j=0;j<nres;j++) { + s5=strtok_r(NULL," ",&ptrptr); + if (s5==NULL) { + ast_log(LOG_WARNING,"ast_PGSQL_fetch : More tuples (%d) than variables (%d)\n",nres,j); + break; + } + + s6=PQgetvalue(PGSQLres,i,j); + if (s6==NULL) { + ast_log(LOG_WARNING,"PWgetvalue(res,%d,%d) returned NULL in ast_PGSQL_fetch\n",i,j); + break; + } + ast_log(LOG_WARNING,"===setting variable '%s' to '%s'\n",s5,s6); + pbx_builtin_setvar_helper(chan,s5,s6); + } + i++; + if (i<PQntuples(PGSQLres)) { + lalares=malloc(sizeof(int)); + *lalares=i; + id1=add_identifier(AST_PGSQL_ID_FETCHID,lalares); + } else { + id1=0; + } + s5=&s[0]; + sprintf(s5,"%d",id1); + ast_log(LOG_WARNING,"Setting var '%s' to value '%s'\n",s3,s); + pbx_builtin_setvar_helper(chan,s3,s); + break; + } + + free(s1); + free(s2); + return(res); +} + +static int aPGSQL_reset(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s3; + int l; + PGconn *karoto; + int id; + + + l=strlen(data)+2; + s1=malloc(l); + strcpy(s1,data); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + s3=strtok_r(NULL," ",&ptrptr); + id=atoi(s3); + if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) { + ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_reset\n",id); + } else { + PQreset(karoto); + } + free(s1); + return(0); + +} + +static int aPGSQL_clear(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s3; + int l; + PGresult *karoto; + int id; + + + l=strlen(data)+2; + s1=malloc(l); + strcpy(s1,data); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + s3=strtok_r(NULL," ",&ptrptr); + id=atoi(s3); + if ((karoto=find_identifier(id,AST_PGSQL_ID_RESID))==NULL) { + ast_log(LOG_WARNING,"Invalid result identifier %d passed in aPGSQL_clear\n",id); + } else { + PQclear(karoto); + del_identifier(id,AST_PGSQL_ID_RESID); + } + free(s1); + return(0); + +} + + + + +static int aPGSQL_disconnect(struct ast_channel *chan, void *data) { + + char *ptrptr; + char *s1,*s3; + int l; + PGconn *karoto; + int id; + + + l=strlen(data)+2; + s1=malloc(l); + strcpy(s1,data); + strtok_r(s1," ",&ptrptr); // eat the first token, we already know it :P + s3=strtok_r(NULL," ",&ptrptr); + id=atoi(s3); + if ((karoto=find_identifier(id,AST_PGSQL_ID_CONNID))==NULL) { + ast_log(LOG_WARNING,"Invalid connection identifier %d passed in aPGSQL_disconnect\n",id); + } else { + PQfinish(karoto); + del_identifier(id,AST_PGSQL_ID_CONNID); + } + free(s1); + return(0); + +} + +static int aPGSQL_debug(struct ast_channel *chan, void *data) { + ast_log(LOG_WARNING,"Debug : %s\n",(char *)data); + return(0); +} + + + +static int PGSQL_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + int result; + + if (!data) { + ast_log(LOG_WARNING, "APP_PGSQL requires an argument (see manual)\n"); + return -1; + } + LOCAL_USER_ADD(u); + result=0; + + if (strncasecmp("connect",data,strlen("connect"))==0) { + result=(aPGSQL_connect(chan,data)); + } else if (strncasecmp("query",data,strlen("query"))==0) { + result=(aPGSQL_query(chan,data)); + } else if (strncasecmp("fetch",data,strlen("fetch"))==0) { + result=(aPGSQL_fetch(chan,data)); + } else if (strncasecmp("reset",data,strlen("reset"))==0) { + result=(aPGSQL_reset(chan,data)); + } else if (strncasecmp("clear",data,strlen("clear"))==0) { + result=(aPGSQL_clear(chan,data)); + } else if (strncasecmp("debug",data,strlen("debug"))==0) { + result=(aPGSQL_debug(chan,data)); + } else if (strncasecmp("disconnect",data,strlen("disconnect"))==0) { + result=(aPGSQL_disconnect(chan,data)); + } else { + ast_log(LOG_WARNING, "Unknown APP_PGSQL argument : %s\n",(char *)data); + result=-1; + } + + LOCAL_USER_REMOVE(u); + return result; + +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + struct PGSQLidshead *headp; + + headp=&PGSQLidshead; + + AST_LIST_HEAD_INIT(headp); + return ast_register_application(app, PGSQL_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} |