/* * Asterisk -- A telephony toolkit for Linux. * * ODBC Database access functions * * by Brian K. West * * source patched to use it with newer versions (e.g. 1.0-RC1 * and above by Karlheinz Hagen 06/17/2004 * * LookupCIDName and LookupBlacklist added by * Rob Fugina 01/13/2005 * * * This program is free software, distributed under the terms of * the GNU General Public License * * Copyright (c) Digium * * Based on work by Mark Spencer and Jefferson Noxon - app_db.c * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *tdesc = "Database access functions for Asterisk extension logic"; static char *g_descrip = " ODBCget(varname=family/key): Retrieves a value from the Asterisk\n" "database and stores it in the given variable. Always returns 0. If the\n" "requested key is not found, jumps to priority n+101 if available.\n"; static char *p_descrip = " ODBCput(family/key=value): Stores the given value in the Asterisk\n" "database. Always returns 0.\n"; static char *d_descrip = " ODBCdel(family/key): Deletes a key from the Asterisk database. Always\n" "returns 0.\n"; static char *dt_descrip = " ODBCdeltree(family[/keytree]): Deletes a family or keytree from the Asterisk\n" "database. Always returns 0.\n"; static char *cidname_descrip = " ODBCLookupCIDName: Looks up the Caller*ID number on the active\n" "channel in the ODBC database (family 'cidname') and sets the\n" "Caller*ID name. Does nothing if no Caller*ID was received on the\n" "channel. This is useful if you do not subscribe to Caller*ID\n" "name delivery, or if you want to change the names on some incoming\n" "calls. Always returns 0.\n"; static char *blacklist_descrip = " ODBCLookupBlacklist: Looks up the Caller*ID number on the active\n" "channel in the ODBC database (family 'blacklist'). If the\n" "number is found, and if there exists a priority n + 101,\n" "where 'n' is the priority of the current instance, then the\n" "channel will be setup to continue at that priority level.\n" "Otherwise, it returns 0. Does nothing if no Caller*ID was received on the\n" "channel.\n"; /* "Example: database put blacklist 1\n"; */ static char *g_app = "ODBCget"; static char *p_app = "ODBCput"; static char *d_app = "ODBCdel"; static char *dt_app = "ODBCdeltree"; static char *cidname_app = "ODBCLookupCIDName"; static char *blacklist_app = "ODBCLookupBlacklist"; static char *g_synopsis = "Retrieve a value from a ODBC datasource"; static char *p_synopsis = "Store a value in a ODBC datasource"; static char *d_synopsis = "Delete a key from a ODBC datasource"; static char *dt_synopsis = "Delete a family or keytree from a ODBC datasource"; static char *cidname_synopsis = "Look up CallerID Name from ODBC cidnum database"; static char *blacklist_synopsis = "Look up Caller*ID number in ODBC blacklist database"; AST_MUTEX_DEFINE_STATIC(odbc_lock); static SQLHENV ODBC_env = SQL_NULL_HANDLE; /* global ODBC Environment */ static int ODBC_res; /* global ODBC Result of Functions */ static SQLHDBC ODBC_con; /* global ODBC Connection Handle */ static SQLHSTMT ODBC_stmt; /* global ODBC Statement Handle */ static char *config = "odbc.conf"; static char *dsn = NULL, *username = NULL, *password = NULL; static int dsn_alloc = 0, username_alloc = 0, password_alloc = 0; static int connected = 0; static int ast_db_odbcget(const char *family, const char *key, char *out, int outlen); static int ast_db_odbcput(const char *family, const char *key, char *value); static int ast_db_odbcdel(const char *family, const char *key); static int ast_db_odbcdeltree(const char *family, const char *keytree); static int odbc_load_module(void); static int odbc_init(void); static int odbc_unload_module(void); static int odbc_do_query(char *sqlcmd); STANDARD_LOCAL_USER; LOCAL_USER_DECL; static int lookupblacklist_exec (struct ast_channel *chan, void *data) { char blacklist[1]; struct localuser *u; int bl = 0; LOCAL_USER_ADD (u); if (chan->cid.cid_num) { if (!ast_db_odbcget ("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist))) { if (option_verbose > 2) ast_log(LOG_NOTICE, "Blacklisted number %s found\n",chan->cid.cid_num); bl = 1; } } if (chan->cid.cid_name) { if (!ast_db_odbcget ("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist))) { if (option_verbose > 2) ast_log (LOG_NOTICE,"Blacklisted name \"%s\" found\n",chan->cid.cid_name); bl = 1; } } if (bl && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) chan->priority+=100; LOCAL_USER_REMOVE (u); return 0; } static int lookupcidname_exec (struct ast_channel *chan, void *data) { char dbname[64]; struct localuser *u; LOCAL_USER_ADD (u); if (chan->cid.cid_num) { if (!ast_db_odbcget ("cidname", chan->cid.cid_num, dbname, sizeof (dbname))) { ast_set_callerid (chan, NULL, dbname, NULL); if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID name to %s\n", dbname); } } LOCAL_USER_REMOVE (u); return 0; } static int odbcdeltree_exec(struct ast_channel *chan, void *data) { int arglen; char *argv, *family, *keytree; arglen = strlen (data); argv = alloca (arglen + 1); if (!argv) /* Why would this fail? */ { ast_log (LOG_DEBUG, "Memory allocation failed\n"); return 0; } memcpy (argv, data, arglen + 1); if (strchr (argv, '/')) { family = strsep (&argv, "/"); keytree = strsep (&argv, "\0"); if (!family || !keytree) { ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n"); return 0; } if (!strlen (keytree)) keytree = 0; } else { family = argv; keytree = 0; } if (option_verbose > 2) { if (keytree) ast_verbose (VERBOSE_PREFIX_3 "odbcdeltree: family=%s, keytree=%s\n", family, keytree); else ast_verbose (VERBOSE_PREFIX_3 "odbcdeltree: family=%s\n", family); } if (ast_db_odbcdeltree (family, keytree)) { if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcdeltree: Error deleting key from database.\n"); } return 0; } static int odbcdel_exec(struct ast_channel *chan, void *data) { int arglen; char *argv, *family, *key; arglen = strlen (data); argv = alloca (arglen + 1); if (!argv) /* Why would this fail? */ { ast_log (LOG_DEBUG, "Memory allocation failed\n"); return 0; } memcpy (argv, data, arglen + 1); if (strchr (argv, '/')) { family = strsep (&argv, "/"); key = strsep (&argv, "\0"); if (!family || !key) { ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n"); return 0; } if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcdel: family=%s, key=%s\n", family, key); if (ast_db_odbcdel (family, key)) { if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcdel: Error deleting key from database.\n"); } } else { ast_log (LOG_DEBUG, "Ignoring, no parameters\n"); } return 0; } static int odbcput_exec(struct ast_channel *chan, void *data) { int arglen; char *argv, *value, *family, *key; arglen = strlen (data); argv = alloca (arglen + 1); if (!argv) /* Why would this fail? */ { ast_log (LOG_DEBUG, "Memory allocation failed\n"); return 0; } memcpy (argv, data, arglen + 1); if (strchr (argv, '/') && strchr (argv, '=')) { family = strsep (&argv, "/"); key = strsep (&argv, "="); value = strsep (&argv, "\0"); if (!value || !family || !key) { ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n"); return 0; } if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcput: family=%s, key=%s, value=%s\n", family, key, value); if (ast_db_odbcput (family, key, value)) { if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcput: Error writing value to database.\n"); } } else { ast_log (LOG_DEBUG, "Ignoring, no parameters\n"); } return 0; } static int odbcget_exec(struct ast_channel *chan, void *data) { int arglen; char *argv, *varname, *family, *key; char dbresult[256]; arglen = strlen (data); argv = alloca (arglen + 1); if (!argv) /* Why would this fail? */ { ast_log (LOG_DEBUG, "Memory allocation failed\n"); return 0; } memcpy (argv, data, arglen + 1); if (strchr (argv, '=') && strchr (argv, '/')) { varname = strsep (&argv, "="); family = strsep (&argv, "/"); key = strsep (&argv, "\0"); if (!varname || !family || !key) { ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n"); return 0; } if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcget: varname=%s, family=%s, key=%s\n", varname, family, key); if (!ast_db_odbcget (family, key, dbresult, sizeof (dbresult) - 1)) { pbx_builtin_setvar_helper (chan, varname, dbresult); if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcget: set variable %s to %s\n", varname, dbresult); } else { if (option_verbose > 2) ast_verbose (VERBOSE_PREFIX_3 "odbcget: Value not found in database.\n"); /* Send the call to n+101 priority, where n is the current priority */ if (ast_exists_extension (chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) chan->priority += 100; } } else { ast_log (LOG_DEBUG, "Ignoring, no parameters\n"); } return 0; } static int odbc_init(void) { long int ODBC_err; short int ODBC_mlen; char ODBC_msg[200], ODBC_stat[10]; if ( ODBC_env == SQL_NULL_HANDLE || !connected) { ODBC_res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &ODBC_env); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error AllocHandle\n"); connected = 0; return -1; } ODBC_res = SQLSetEnvAttr(ODBC_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error SetEnv\n"); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; return -1; } ODBC_res = SQLAllocHandle(SQL_HANDLE_DBC, ODBC_env, &ODBC_con); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error AllocHDB %d\n", ODBC_res); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; return -1; } SQLSetConnectAttr(ODBC_con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)10, 0); } ODBC_res = SQLConnect(ODBC_con, (SQLCHAR*)dsn, SQL_NTS, (SQLCHAR*)username, SQL_NTS, (SQLCHAR*)password, SQL_NTS); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error SQLConnect %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; return -1; } else { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Connected to %s\n", dsn); connected = 1; } return 0; } static int odbc_load_module(void) { int retval; int res; struct ast_config *cfg; struct ast_variable *var; char *tmp; cfg = ast_load(config); if (!cfg) { ast_log(LOG_WARNING, "app_dbodbc: Unable to load config for unixODBC CDR's: %s\n", config); return 0; } var = ast_variable_browse(cfg, "global"); if (!var) { /* nothing configured */ return 0; } tmp = ast_variable_retrieve(cfg,"global","dsn"); if (tmp) { dsn = malloc(strlen(tmp) + 1); if (dsn != NULL) { dsn_alloc = 1; strcpy(dsn,tmp); } else { ast_log(LOG_ERROR,"app_dbodbc: Out of memory error.\n"); return -1; } } else { ast_log(LOG_WARNING,"app_dbodbc: dsn not specified. Assuming asteriskdb\n"); dsn = "asteriskdb"; } tmp = ast_variable_retrieve(cfg,"global","username"); if (tmp) { username = malloc(strlen(tmp) + 1); if (username != NULL) { username_alloc = 1; strcpy(username,tmp); } else { ast_log(LOG_ERROR,"app_dbodbc: Out of memory error.\n"); return -1; } } else { ast_log(LOG_WARNING,"app_dbodbc: username not specified. Assuming root\n"); username = "root"; } tmp = ast_variable_retrieve(cfg,"global","password"); if (tmp) { password = malloc(strlen(tmp) + 1); if (password != NULL) { password_alloc = 1; strcpy(password,tmp); } else { ast_log(LOG_ERROR,"app_dbodbc: Out of memory error.\n"); return -1; } } else { ast_log(LOG_WARNING,"app_dbodbc: database password not specified. Assuming blank\n"); password = ""; } ast_destroy(cfg); if(option_verbose > 3) { ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: dsn is %s\n",dsn); ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: username is %s\n",username); ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: password is [secret]\n"); } res = odbc_init(); if(res < 0) { ast_log(LOG_ERROR, "app_dbodbc: Unable to connect to datasource: %s\n", dsn); ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Unable to connect to datasource: %s\n", dsn); } retval = ast_register_application (g_app, odbcget_exec, g_synopsis, g_descrip); if (!retval) retval = ast_register_application (p_app, odbcput_exec, p_synopsis, p_descrip); if (!retval) retval = ast_register_application (d_app, odbcdel_exec, d_synopsis, d_descrip); if (!retval) retval = ast_register_application (dt_app, odbcdeltree_exec, dt_synopsis, dt_descrip); if (!retval) retval = ast_register_application (cidname_app, lookupcidname_exec, cidname_synopsis, cidname_descrip); if (!retval) retval = ast_register_application (blacklist_app, lookupblacklist_exec, blacklist_synopsis, blacklist_descrip); return retval; } static int odbc_do_query(char *sqlcmd) { long int ODBC_err; short int ODBC_mlen; char ODBC_msg[200], ODBC_stat[10]; ODBC_res = SQLAllocHandle(SQL_HANDLE_STMT, ODBC_con, &ODBC_stmt); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Failure in AllocStatement %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); connected = 0; return -1; } ODBC_res = SQLPrepare(ODBC_stmt, sqlcmd, SQL_NTS); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error in PREPARE %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); return -1; } ODBC_res = SQLExecute(ODBC_stmt); if((ODBC_res != SQL_SUCCESS) && (ODBC_res != SQL_SUCCESS_WITH_INFO)) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Error in Query %d\n", ODBC_res); SQLGetDiagRec(SQL_HANDLE_DBC, ODBC_con, 1, ODBC_stat, &ODBC_err, ODBC_msg, 100, &ODBC_mlen); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); connected = 0; return -1; } else { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Query Successful!\n"); connected = 1; } return 0; } static int odbc_unload_module(void) { int retval; if (connected) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: Disconnecting from %s\n", dsn); SQLFreeHandle(SQL_HANDLE_STMT, ODBC_stmt); SQLDisconnect(ODBC_con); SQLFreeHandle(SQL_HANDLE_DBC, ODBC_con); SQLFreeHandle(SQL_HANDLE_ENV, ODBC_env); connected = 0; } if (dsn && dsn_alloc) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: free dsn\n"); free(dsn); dsn = NULL; dsn_alloc = 0; } if (username && username_alloc) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: free username\n"); free(username); username = NULL; username_alloc = 0; } if (password && password_alloc) { if(option_verbose > 3) ast_verbose( VERBOSE_PREFIX_4 "app_dbodbc: free password\n"); free(password); password = NULL; password_alloc = 0; } retval = ast_unregister_application (dt_app); retval |= ast_unregister_application (d_app); retval |= ast_unregister_application (p_app); retval |= ast_unregister_application (g_app); retval |= ast_unregister_application (cidname_app); retval |= ast_unregister_application (blacklist_app); return retval; } static int ast_db_odbcget(const char *family, const char *key, char *value, int valuelen) { int res; long int ODBC_err; char sqlcmd[1024]; char tmp[256] = ""; memset(sqlcmd,0,1024); ast_mutex_lock(&odbc_lock); sprintf(sqlcmd, "SELECT DISTINCT astvalue FROM astdb WHERE astfamily='%s' AND astkey='%s' LIMIT 1", family, key); res = odbc_do_query(sqlcmd); SQLBindCol(ODBC_stmt, 1, SQL_C_CHAR, &tmp, 50, &ODBC_err); if((ODBC_res = SQLFetch(ODBC_stmt) != SQL_NO_DATA)) { strcpy(value, tmp); } else { res = -1; } ast_mutex_unlock(&odbc_lock); return res; } static int ast_db_odbcput(const char *family, const char *key, char *value) { int res; char sqlcmd[1024]; memset(sqlcmd,0,1024); ast_mutex_lock(&odbc_lock); sprintf(sqlcmd, "DELETE FROM astdb WHERE astfamily='%s' AND astkey='%s'", family, key); res = odbc_do_query(sqlcmd); memset(sqlcmd,0,1024); sprintf(sqlcmd, "INSERT INTO astdb (astfamily,astkey,astvalue) VALUES ('%s','%s','%s')", family, key, value); res = odbc_do_query(sqlcmd); ast_mutex_unlock(&odbc_lock); return res; } static int ast_db_odbcdel(const char *family, const char *key) { int res; char sqlcmd[1024]; memset(sqlcmd,0,1024); ast_mutex_lock(&odbc_lock); sprintf(sqlcmd, "DELETE FROM astdb WHERE astfamily='%s' AND astkey='%s'", family, key); res = odbc_do_query(sqlcmd); ast_mutex_unlock(&odbc_lock); return res; } static int ast_db_odbcdeltree(const char *family, const char *keytree) { int res; char sqlcmd[1024]; memset(sqlcmd,0,1024); ast_mutex_lock(&odbc_lock); sprintf(sqlcmd, "DELETE FROM astdb WHERE astfamily='%s'", family); res = odbc_do_query(sqlcmd); ast_mutex_unlock(&odbc_lock); return res; } int unload_module (void) { STANDARD_HANGUP_LOCALUSERS; return odbc_unload_module(); } int reload(void) { connected = 0; odbc_unload_module(); return odbc_load_module(); } int load_module (void) { return odbc_load_module(); } char *description (void) { return tdesc; } int usecount (void) { int res; STANDARD_USECOUNT (res); return res; } char *key () { return ASTERISK_GPL_KEY; }