You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/apps/dsm/mods/mod_mysql/ModMysql.cpp

669 lines
21 KiB

/*
* Copyright (C) 2009 TelTech Systems Inc.
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. This program is released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ModMysql.h"
#include "log.h"
#include "AmUtils.h"
#include "DSMSession.h"
#include "AmSession.h"
#include "AmPlaylist.h"
#include <stdio.h>
#include <fstream>
SC_EXPORT(SCMysqlModule);
SCMysqlModule::SCMysqlModule() {
}
SCMysqlModule::~SCMysqlModule() {
}
DSMAction* SCMysqlModule::getAction(const string& from_str) {
string cmd;
string params;
splitCmd(from_str, cmd, params);
DEF_CMD("mysql.connect", SCMyConnectAction);
DEF_CMD("mysql.disconnect", SCMyDisconnectAction);
DEF_CMD("mysql.execute", SCMyExecuteAction);
DEF_CMD("mysql.query", SCMyQueryAction);
DEF_CMD("mysql.queryGetResult", SCMyQueryGetResultAction);
DEF_CMD("mysql.getResult", SCMyGetResultAction);
DEF_CMD("mysql.getClientVersion", SCMyGetClientVersion);
DEF_CMD("mysql.resolveQueryParams", SCMyResolveQueryParams);
DEF_CMD("mysql.saveResult", SCMySaveResultAction);
DEF_CMD("mysql.useResult", SCMyUseResultAction);
DEF_CMD("mysql.playDBAudio", SCMyPlayDBAudioAction);
DEF_CMD("mysql.playDBAudioFront", SCMyPlayDBAudioFrontAction);
DEF_CMD("mysql.playDBAudioLooped", SCMyPlayDBAudioLoopedAction);
DEF_CMD("mysql.getFileFromDB", SCMyGetFileFromDBAction);
DEF_CMD("mysql.putFileToDB", SCMyPutFileToDBAction);
DEF_CMD("mysql.escape", SCMyEscapeAction);
return NULL;
}
DSMCondition* SCMysqlModule::getCondition(const string& from_str) {
string cmd;
string params;
splitCmd(from_str, cmd, params);
if (cmd == "mysql.hasResult") {
return new MyHasResultCondition(params, false);
}
if (cmd == "mysql.connected") {
return new MyConnectedCondition(params, true);
}
return NULL;
}
mysqlpp::Connection* getMyDSMSessionConnection(DSMSession* sc_sess) {
if (sc_sess->avar.find(MY_AKEY_CONNECTION) == sc_sess->avar.end()) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR("No connection to database");
return NULL;
}
AmObject* ao = NULL; mysqlpp::Connection* res = NULL;
try {
if (!isArgAObject(sc_sess->avar[MY_AKEY_CONNECTION])) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR("No connection to database (not AmObject)");
return NULL;
}
ao = sc_sess->avar[MY_AKEY_CONNECTION].asObject();
} catch (...){
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR("No connection to database (not AmObject)");
return NULL;
}
if (NULL == ao || NULL == (res = dynamic_cast<mysqlpp::Connection*>(ao))) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR("No connection to database (not mysqlpp::Connection)");
return NULL;
}
return res;
}
mysqlpp::StoreQueryResult* getMyDSMQueryResult(DSMSession* sc_sess) {
if (sc_sess->avar.find(MY_AKEY_RESULT) == sc_sess->avar.end()) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NORESULT);
sc_sess->SET_STRERROR("No result available");
return NULL;
}
AmObject* ao = NULL; mysqlpp::StoreQueryResult* res = NULL;
try {
assertArgAObject(sc_sess->avar[MY_AKEY_RESULT]);
ao = sc_sess->avar[MY_AKEY_RESULT].asObject();
} catch (...){
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NORESULT);
sc_sess->SET_STRERROR("Result object has wrong type");
return NULL;
}
if (NULL == ao || NULL == (res = dynamic_cast<mysqlpp::StoreQueryResult*>(ao))) {
sc_sess->SET_STRERROR("Result object has wrong type");
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NORESULT);
return NULL;
}
return res;
}
string replaceQueryParams(const string& q, DSMSession* sc_sess,
map<string,string>* event_params) {
string res = q;
size_t repl_pos = 0;
while (repl_pos<res.length()) {
size_t rstart = res.find_first_of("$#", repl_pos);
repl_pos = rstart+1;
if (rstart == string::npos)
break;
if (rstart && res[rstart-1] == '\\') // escaped
continue;
size_t rend = res.find_first_of(" ,()$#\t;'\"", rstart+1);
if (rend==string::npos)
rend = res.length();
switch(res[rstart]) {
case '$':
res.replace(rstart, rend-rstart,
sc_sess->var[res.substr(rstart+1, rend-rstart-1)]); break;
case '#':
if (NULL!=event_params) {
res.replace(rstart, rend-rstart,
(*event_params)[res.substr(rstart+1, rend-rstart-1)]); break;
}
default: break;
}
}
return res;
}
string str_between(const string s, char b, char e) {
size_t pos1 = s.find(b);
if (b == '\0' || pos1 == string::npos)
pos1 = 0;
else
pos1++;
size_t pos2 = s.find(e, pos1);
if (e == '\0' || pos2 == string::npos)
pos2 = s.length();
return s.substr(pos1, pos2-pos1);
}
EXEC_ACTION_START(SCMyConnectAction) {
string f_arg = resolveVars(arg, sess, sc_sess, event_params);
string db_url = f_arg.length()?f_arg:sc_sess->var["config.db_url"];
if (db_url.empty() || db_url.length() < 11 || db_url.substr(0, 8) != "mysql://") {
ERROR("missing correct db_url config or connect parameter\n");
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("missing correct db_url config or connect parameter\n");
return false;
}
// split url
db_url = db_url.substr(8);
string db_user = str_between(db_url, '\0', ':');
string db_pwd = str_between(db_url, ':', '@');
string db_host = str_between(db_url, '@', '/');
string db_db = str_between(db_url, '/', '\0');
DSMMyConnection* conn = NULL;
try {
conn = new DSMMyConnection(db_db.c_str(), db_host.c_str(), db_user.c_str(), db_pwd.c_str());
// save connection for later use
AmArg c_arg;
c_arg.setBorrowedPointer(conn);
sc_sess->avar[MY_AKEY_CONNECTION] = c_arg;
// for garbage collection
sc_sess->transferOwnership(conn);
sc_sess->CLR_ERRNO;
} catch (const mysqlpp::ConnectionFailed& e) {
ERROR("DB connection failed with error %d: '%s'\n",e.errnum(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.errno"] = int2str(e.errnum());
sc_sess->var["db.ereason"] = e.what();
} catch (const mysqlpp::Exception& e) {
ERROR("DB connection failed: '%s'\n", e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyDisconnectAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
try {
conn->disconnect();
// connection object might be reused - but its safer to create a new one
sc_sess->avar[MY_AKEY_CONNECTION] = AmArg();
sc_sess->CLR_ERRNO;
} catch (const mysqlpp::Exception& e) {
ERROR("DB disconnect failed: '%s'\n", e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyResolveQueryParams) {
sc_sess->var["db.qstr"] =
replaceQueryParams(arg, sc_sess, event_params);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyExecuteAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(arg, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::SimpleResult res = query.execute();
if (res) {
sc_sess->CLR_ERRNO;
sc_sess->var["db.rows"] = int2str((int)res.rows());
sc_sess->var["db.info"] = res.info();
sc_sess->var["db.insert_id"] = int2str((int)res.insert_id());
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(res.info());
sc_sess->var["db.info"] = res.info();
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyQueryAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(arg, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::StoreQueryResult res = query.store();
if (res) {
// MySQL++ does not allow working with pointers here, so copy construct it
DSMMyStoreQueryResult* m_res = new DSMMyStoreQueryResult(res);
// save result for later use
AmArg c_arg;
c_arg.setBorrowedPointer(m_res);
sc_sess->avar[MY_AKEY_RESULT] = c_arg;
// for garbage collection
sc_sess->transferOwnership(m_res);
sc_sess->CLR_ERRNO;
sc_sess->var["db.rows"] = int2str((unsigned int)res.num_rows());
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR("query did not have a result");
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyQueryGetResultAction, ',', true);
EXEC_ACTION_START(SCMyQueryGetResultAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::StoreQueryResult res = query.store();
if (res) {
unsigned int rowindex_i = 0;
string rowindex = resolveVars(par2, sess, sc_sess, event_params);
if (rowindex.length()) {
if (str2int(rowindex, rowindex_i)) {
ERROR("row index '%s' not understood\n", rowindex.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("row index '"+rowindex+"' not understood\n");
return false;
}
}
if (res.size() <= rowindex_i) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("row index out of result rows bounds");
return false;
}
// get all columns
for (size_t i = 0; i < res.field_names()->size(); i++) {
sc_sess->var[res.field_name(i)] =
(string)res[rowindex_i][res.field_name(i).c_str()];
}
sc_sess->CLR_ERRNO;
sc_sess->var["db.rows"] = int2str((unsigned int)res.num_rows());
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyGetResultAction, ',', true);
EXEC_ACTION_START(SCMyGetResultAction) {
mysqlpp::StoreQueryResult* res = getMyDSMQueryResult(sc_sess);
if (NULL == res)
return false;
if (!res) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NORESULT);
sc_sess->SET_STRERROR("No result from query");
return false;
}
unsigned int rowindex_i = 0;
string rowindex = resolveVars(par1, sess, sc_sess, event_params);
string colname = resolveVars(par2, sess, sc_sess, event_params);
if (rowindex.length()) {
if (str2int(rowindex, rowindex_i)) {
ERROR("row index '%s' not understood\n", rowindex.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("row index '"+rowindex+"' not understood");
return false;
}
}
if (res->size() <= rowindex_i) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("row index out of result rows bounds");
return false;
}
DBG("rowindex_i = %d\n", rowindex_i);
if (colname.length()) {
// get only this column
try {
sc_sess->var[colname] =
(string)(*res)[(int)rowindex_i][colname.c_str()];
} catch (const mysqlpp::BadFieldName& e) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOCOLUMN);
sc_sess->SET_STRERROR("bad field name '"+colname+"'");
return false;
}
} else {
// get all columns
for (size_t i = 0; i < res->field_names()->size(); i++) {
sc_sess->var[res->field_name(i)] =
(string)(*res)[rowindex_i][res->field_name(i).c_str()];
}
}
sc_sess->CLR_ERRNO;
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyGetClientVersion) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
sc_sess->var[resolveVars(arg, sess, sc_sess, event_params)] =
conn->client_version();
sc_sess->CLR_ERRNO;
} EXEC_ACTION_END;
MATCH_CONDITION_START(MyHasResultCondition) {
mysqlpp::StoreQueryResult* res = getMyDSMQueryResult(sc_sess);
if (NULL == res)
return false;
if (!res || !(res->size())) {
return false;
}
return true;
} MATCH_CONDITION_END;
MATCH_CONDITION_START(MyConnectedCondition) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
return conn->connected();
} MATCH_CONDITION_END;
EXEC_ACTION_START(SCMySaveResultAction) {
sc_sess->avar[resolveVars(arg, sess, sc_sess, event_params)] = sc_sess->avar[MY_AKEY_RESULT];
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyUseResultAction) {
sc_sess->avar[MY_AKEY_RESULT] = sc_sess->avar[resolveVars(arg, sess, sc_sess, event_params)];
} EXEC_ACTION_END;
bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
map<string,string>* event_params, const string& par1, const string& par2,
bool looped, bool front) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
EXEC_ACTION_STOP;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::UseQueryResult res = query.use();
if (res) {
mysqlpp::Row row = res.fetch_row();
if (!row) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
EXEC_ACTION_STOP;
}
FILE *t_file = tmpfile();
if (NULL == t_file) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("tmpfile() failed: "+string(strerror(errno)));
EXEC_ACTION_STOP;
}
fwrite(row.at(0).data(), 1, row.at(0).size(), t_file);
rewind(t_file);
DSMDisposableAudioFile* a_file = new DSMDisposableAudioFile();
if (a_file->fpopen(par2, AmAudioFile::Read, t_file)) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fpopen failed!");
EXEC_ACTION_STOP;
}
a_file->loop = looped;
sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL), front);
sc_sess->transferOwnership(a_file);
sc_sess->CLR_ERRNO;
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR("query does not have result");
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
return false;
}
CONST_ACTION_2P(SCMyPlayDBAudioAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ false, /*front = */ false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyPlayDBAudioFrontAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioFrontAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ false, /*front = */ true);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyPlayDBAudioLoopedAction, ',', true);
EXEC_ACTION_START(SCMyPlayDBAudioLoopedAction) {
playDBAudio(sess, sc_sess, event, event_params, par1, par2,
/*looped = */ true, /*front = */ false);
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyGetFileFromDBAction, ',', true);
EXEC_ACTION_START(SCMyGetFileFromDBAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
string fname = resolveVars(par2, sess, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::UseQueryResult res = query.use();
if (res) {
mysqlpp::Row row = res.fetch_row();
if (!row) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
return false;
}
FILE *t_file = fopen(fname.c_str(), "wb");
if (NULL == t_file) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fopen() failed for file '"+fname+"': "+string(strerror(errno)));
return false;
}
fwrite(row.at(0).data(), 1, row.at(0).size(), t_file);
fclose(t_file);
sc_sess->CLR_ERRNO;
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR("query does not have result");
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyPutFileToDBAction, ',', true);
EXEC_ACTION_START(SCMyPutFileToDBAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
string fname = resolveVars(par2, sess, sc_sess, event_params);
size_t fpos = qstr.find("__FILE__");
if (fpos == string::npos) {
ERROR("missing __FILE__ in query string '%s'\n",
par1.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
sc_sess->SET_STRERROR("missing __FILE__ in query string '"+par1+"'\n");
return false;
}
try {
std::ifstream data_file(fname.c_str(), std::ios::in | std::ios::binary);
if (!data_file) {
DBG("could not read file '%s'\n", fname.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("could not read file '"+fname+"'\n");
return false;
}
// that one is clever...
// (see http://www.gamedev.net/community/forums/topic.asp?topic_id=353162 )
string file_data((std::istreambuf_iterator<char>(data_file)),
std::istreambuf_iterator<char>());
if (file_data.empty()) {
DBG("could not read file '%s'\n", fname.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("could not read file '"+fname+"'\n");
return false;
}
mysqlpp::Query query = conn->query();
query << qstr.substr(0, fpos) <<
mysqlpp::escape << file_data << qstr.substr(fpos+8);
mysqlpp::SimpleResult res = query.execute();
if (res) {
sc_sess->CLR_ERRNO;
sc_sess->var["db.rows"] = int2str((int)res.rows());
sc_sess->var["db.info"] = res.info();
sc_sess->var["db.insert_id"] = int2str((int)res.insert_id());
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(res.info());
sc_sess->var["db.info"] = res.info();
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
par1.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCMyEscapeAction, '=', false);
EXEC_ACTION_START(SCMyEscapeAction) {
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
mysqlpp::Query query = conn->query();
string val = resolveVars(par2, sess, sc_sess, event_params);
string dstvar = par1;
if (dstvar.size() && dstvar[0] == '$') {
dstvar = dstvar.substr(1);
}
string res;
query.escape_string(&res, val.c_str(), val.length());
sc_sess->var[dstvar] = res;
DBG("escaped: $%s = escape(%s) = %s\n",
dstvar.c_str(), val.c_str(), res.c_str());
} EXEC_ACTION_END;