- event params made writable with set() and sets(); used to return value from event processing

- raw SIP request processing from within DSM script (use enable_request_events/enable_reply_events)


git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@1926 8eb893ce-cfd4-0310-b710-fb5ebe64c474
sayer/1.4-spce2.6
Stefan Sayer 16 years ago
parent eca4a88f55
commit b9176c56d0

@ -67,10 +67,16 @@ DSMCall::~DSMCall()
/** returns whether var exists && var==value*/
bool DSMCall::checkVar(const string& var_name, const string& var_val) {
map<string, string>::iterator it = var.find(var_name);
if ((it != var.end()) && (it->second == var_val))
return true;
return (it != var.end()) && (it->second == var_val);
}
return false;
/** returns whether params, param exists && param==value*/
bool checkParam(const string& par_name, const string& par_val, map<string, string>* params) {
if (NULL == params)
return false;
map<string, string>::iterator it = params->find(par_name);
return (it != params->end()) && (it->second == par_val);
}
void DSMCall::onInvite(const AmSipRequest& req) {
@ -134,7 +140,7 @@ void DSMCall::onOutgoingInvite(const string& headers) {
}
}
void DSMCall::onRinging(const AmSipReply& reply) {
void DSMCall::onRinging(const AmSipReply& reply) {
map<string, string> params;
params["code"] = int2str(reply.code);
params["reason"] = reply.reason;
@ -247,7 +253,71 @@ void DSMCall::onCancel() {
}
}
void DSMCall::onSipRequest(const AmSipRequest& req) {
if (checkVar(DSM_ENABLE_REQUEST_EVENTS, DSM_TRUE)) {
map<string, string> params;
params["method"] = req.method;
params["r_uri"] = req.r_uri;
params["from"] = req.from;
params["to"] = req.to;
params["hdrs"] = req.hdrs;
params["content_type"] = req.content_type;
params["body"] = req.body;
params["cseq"] = int2str(req.cseq);
// pass AmSipRequest for use by mod_dlg
DSMSipRequest* sip_req = new DSMSipRequest(&req);
avar[DSM_AVAR_REQUEST] = AmArg(sip_req);
engine.runEvent(this, DSMCondition::SipRequest, &params);
delete sip_req;
avar.erase(DSM_AVAR_REQUEST);
if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
DBG("DSM script processed SIP request '%s', returning\n",
req.method.c_str());
return;
}
}
AmB2BCallerSession::onSipRequest(req);
}
void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
if (checkVar(DSM_ENABLE_REPLY_EVENTS, DSM_TRUE)) {
map<string, string> params;
params["code"] = int2str(reply.code);
params["reason"] = reply.reason;
params["hdrs"] = reply.hdrs;
params["content_type"] = reply.content_type;
params["body"] = reply.body;
params["cseq"] = int2str(reply.cseq);
params["dlg_status"] = int2str(dlg.getStatus());
params["old_dlg_status"] = int2str(old_dlg_status);
// pass AmSipReply for use by mod_dlg (? sending ACK?)
DSMSipReply* dsm_reply = new DSMSipReply(&reply);
avar[DSM_AVAR_REPLY] = AmArg(dsm_reply);
engine.runEvent(this, DSMCondition::SipReply, &params);
delete dsm_reply;
avar.erase(DSM_AVAR_REPLY);
if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
DBG("DSM script processed SIP reply '%u %s', returning\n",
reply.code, reply.reason.c_str());
return;
}
}
AmB2BCallerSession::onSipReply(reply,old_dlg_status);
if ((old_dlg_status < AmSipDialog::Connected) &&

@ -82,7 +82,9 @@ public:
void onBye(const AmSipRequest& req);
void onDtmf(int event, int duration_msec);
void onSipRequest(const AmSipRequest& req);
void onSipReply(const AmSipReply& reply, int old_dlg_status);
void process(AmEvent* event);
UACAuthCred* getCredentials();

@ -2,6 +2,7 @@
* $Id$
*
* Copyright (C) 2008 iptego GmbH
* Copyright (C) 2010 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
@ -169,6 +170,12 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
if (cmd == "B2B.otherBye")
return new TestDSMCondition(params, DSMCondition::B2BOtherBye);
if (cmd == "sipRequest")
return new TestDSMCondition(params, DSMCondition::SipRequest);
if (cmd == "sipReply")
return new TestDSMCondition(params, DSMCondition::SipReply);
return NULL;
}
@ -459,12 +466,25 @@ EXEC_ACTION_START(SCLogAllAction) {
CONST_ACTION_2P(SCSetAction,'=', false);
EXEC_ACTION_START(SCSetAction) {
string var_name = (par1.length() && par1[0] == '$')?
par1.substr(1) : par1;
if (par1.length() && par1[0] == '#') {
// set param
if (NULL != event_params) {
string res = resolveVars(par2, sess, sc_sess, event_params);
(*event_params)[par1.substr(1)] = res;
DBG("set #%s='%s'\n", par1.substr(1).c_str(), res.c_str());
} else {
DBG("not setting %s (no param set)\n", par1.c_str());
}
} else {
// set variable
string var_name = (par1.length() && par1[0] == '$')?
par1.substr(1) : par1;
sc_sess->var[var_name] = resolveVars(par2, sess, sc_sess, event_params);
DBG("set $%s='%s'\n",
var_name.c_str(), sc_sess->var[var_name].c_str());
sc_sess->var[var_name] = resolveVars(par2, sess, sc_sess, event_params);
DBG("set $%s='%s'\n",
var_name.c_str(), sc_sess->var[var_name].c_str());
}
} EXEC_ACTION_END;
string replaceParams(const string& q, AmSession* sess, DSMSession* sc_sess,
@ -511,13 +531,25 @@ string replaceParams(const string& q, AmSession* sess, DSMSession* sc_sess,
}
CONST_ACTION_2P(SCSetSAction,'=', false);
EXEC_ACTION_START(SCSetSAction) {
string var_name = (par1.length() && par1[0] == '$')?
par1.substr(1) : par1;
if (par1.length() && par1[0] == '#') {
// set param
if (NULL != event_params) {
string res = replaceParams(par2, sess, sc_sess, event_params);
(*event_params)[par1.substr(1)] = res;
DBG("set #%s='%s'\n", par1.substr(1).c_str(), res.c_str());
} else {
DBG("not set %s (no param set)\n", par1.c_str());
}
} else {
// set variable
string var_name = (par1.length() && par1[0] == '$')?
par1.substr(1) : par1;
sc_sess->var[var_name] = replaceParams(par2, sess, sc_sess, event_params);
DBG("set $%s='%s'\n",
var_name.c_str(), sc_sess->var[var_name].c_str());
sc_sess->var[var_name] = replaceParams(par2, sess, sc_sess, event_params);
DBG("set $%s='%s'\n",
var_name.c_str(), sc_sess->var[var_name].c_str());
}
} EXEC_ACTION_END;
CONST_ACTION_2P(SCEvalAction,'=', false);

@ -53,6 +53,8 @@ class DSMModule {
virtual bool onInvite(const AmSipRequest& req, DSMSession* sess) { return true; }
};
typedef map<string,string> EventParamT;
typedef void* (*SCFactoryCreate)();
#define SCSTR(x) #x

@ -40,8 +40,11 @@ using std::vector;
using std::map;
#include <memory>
#define DSM_REPLY_REQUEST "reply_request" // todo: rethink these names
#define DSM_REPLY_REQUEST_FALSE "0"
#define DSM_TRUE "true"
#define DSM_FALSE "false"
#define DSM_PROCESSED "processed"
#define DSM_CONNECT "connect"
#define DSM_CONNECT_SESSION "connect_session" // todo: rethink these names
#define DSM_CONNECT_SESSION_FALSE "0"
@ -52,6 +55,13 @@ using std::map;
#define DSM_CONNECT_EARLY_SESSION "connect_early_session" // todo: rethink these names
#define DSM_CONNECT_EARLY_SESSION_FALSE "0"
#define DSM_ENABLE_REQUEST_EVENTS "enable_request_events"
#define DSM_ENABLE_REPLY_EVENTS "enable_reply_events"
#define DSM_AVAR_REQUEST "request"
#define DSM_AVAR_REPLY "reply"
#define DSM_ERRNO_FILE "file"
#define DSM_ERRNO_UNKNOWN_ARG "arg"
#define DSM_ERRNO_SCRIPT "script"
@ -74,6 +84,9 @@ using std::map;
#define CLR_STRERROR \
var["strerror"] = "";
typedef map<string, string> VarMapT;
typedef map<string, AmArg> AVarMapT;
class DSMDisposable;
class AmPlaylistItem;
@ -127,10 +140,10 @@ class DSMSession {
virtual void releaseOwnership(DSMDisposable* d) = 0;
/* holds variables which are accessed by $varname */
map<string, string> var;
VarMapT var;
/* holds AmArg variables. todo: merge var with these */
map<string, AmArg> avar;
/* holds AmArg variables. todo(?): merge var with these */
AVarMapT avar;
/* result of the last DI call */
AmArg di_res;
@ -164,6 +177,27 @@ class DSMDisposableAudioFile
~DSMDisposableAudioFile() { }
};
class DSMSipRequest
: public ArgObject {
public:
const AmSipRequest* req;
DSMSipRequest(const AmSipRequest* req)
: req(req) { }
~DSMSipRequest() { }
};
class DSMSipReply
: public ArgObject {
public:
const AmSipReply* reply;
DSMSipReply(const AmSipReply* reply)
: reply(reply) { }
~DSMSipReply() { }
};
#define DSM_EVENT_ID -10
/** generic event for passing events between DSM sessions */
struct DSMEvent : public AmEvent {

@ -57,29 +57,33 @@ class DSMCondition
public:
enum EventType {
Any,
Invite,
SessionStart,
Ringing,
EarlySession,
FailedCall,
Key,
Timer,
NoAudio,
SipRequest,
SipReply,
Hangup,
Hold,
UnHold,
XmlrpcResponse,
DSMEvent,
PlaylistSeparator,
B2BOtherReply,
B2BOtherBye,
Key,
Timer,
NoAudio,
PlaylistSeparator,
DSMEvent,
DSMException,
XmlrpcResponse,
JsonRpcResponse,
JsonRpcRequest
};

@ -43,6 +43,7 @@ SC_EXPORT(MOD_CLS_NAME);
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
DEF_CMD("dlg.reply", DLGReplyAction);
DEF_CMD("dlg.replyRequest", DLGReplyRequestAction);
DEF_CMD("dlg.acceptInvite", DLGAcceptInviteAction);
DEF_CMD("dlg.bye", DLGByeAction);
DEF_CMD("dlg.connectCalleeRelayed", DLGConnectCalleeRelayedAction);
@ -66,29 +67,52 @@ bool DLGModule::onInvite(const AmSipRequest& req, DSMSession* sess) {
return false; \
}
CONST_ACTION_2P(DLGReplyAction, ',', true);
EXEC_ACTION_START(DLGReplyAction) {
// todo: convert errors to exceptions
void replyRequest(DSMSession* sc_sess, AmSession* sess,
EventParamT* event_params,
const string& par1, const string& par2,
const AmSipRequest& req) {
string code = resolveVars(par1, sess, sc_sess, event_params);
string reason = resolveVars(par2, sess, sc_sess, event_params);
unsigned int code_i;
if (str2i(code, code_i)) {
ERROR("decoding reply code '%s'\n", code.c_str());
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
return false;
return;
}
if (!sc_sess->last_req.get()) {
ERROR("no last request to reply\n");
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("no last request to reply");
return false;
return;
}
if (sess->dlg.reply(*sc_sess->last_req.get(), code_i, reason)) {
if (sess->dlg.reply(req, code_i, reason)) {
sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
sc_sess->SET_STRERROR("error sending reply");
} else
sc_sess->CLR_ERRNO;
}
CONST_ACTION_2P(DLGReplyAction, ',', true);
EXEC_ACTION_START(DLGReplyAction) {
replyRequest(sc_sess, sess, event_params, par1, par2, *sc_sess->last_req.get());
} EXEC_ACTION_END;
// todo (?) move replyRequest to core module (?)
CONST_ACTION_2P(DLGReplyRequestAction, ',', true);
EXEC_ACTION_START(DLGReplyRequestAction) {
DSMSipRequest* sip_req;
AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
if (it == sc_sess->avar.end() ||
!isArgAObject(it->second) ||
!(sip_req = dynamic_cast<DSMSipRequest*>(it->second.asObject()))) {
throw DSMException("dlg", "cause", "no request");
}
replyRequest(sc_sess, sess, event_params, par1, par2, *sip_req->req);
} EXEC_ACTION_END;
CONST_ACTION_2P(DLGAcceptInviteAction, ',', true);

@ -35,6 +35,7 @@ bool onInvite(const AmSipRequest& req, DSMSession* sess);
DECLARE_MODULE_END;
DEF_ACTION_2P(DLGReplyAction);
DEF_ACTION_2P(DLGReplyRequestAction);
DEF_ACTION_2P(DLGAcceptInviteAction);
DEF_ACTION_2P(DLGConnectCalleeRelayedAction);
DEF_ACTION_1P(DLGByeAction);

@ -1,13 +1,22 @@
* mod_dlg saves the initial INVITE to DSMSession::last_req
* mod_dlg saves the initial INVITE to DSMSession::last_req,
this can be processed with dlg.reply(...)/dlg.acceptInvite(...)
* set connect_session to 0 with set(connect_session=0)
if you want to reply with other than the standard 200 OK
to initial INVITE received.
* for processing of other requests, use enable_request_events and replyRequest
dlg.reply(code,reason);
reply to the request in DSMSession::last_req
(usually INVITE, if not yet replied) with code and reason
* sets $errno (arg,general)
dlg.replyRequest(code,reason);
request processing in script; use with set($enable_request_events="true");
reply to any request (in avar[DSM_AVAR_REQUEST]) with code and reason
* sets $errno (arg,general)
* throws exception if request not found (i.e. called from other event than
sipRequest)
dlg.acceptInvite([code, reason]);
e.g. dlg.acceptInvite(183, progress);
* sets $errno (arg,general)

@ -0,0 +1,24 @@
-- example for SIP request processing from script
import(mod_dlg);
initial state in_call
enter {
-- from now on, get events for SIP requests
set($enable_request_events="true");
};
transition "SIP BYE request" in_call - sipRequest(#method==BYE) / {
dlg.replyRequest(200, "okey bye bye");
-- set event param "processed" - BYE will not be processed by normal app logic
set(#processed=true);
logAll(1);
stop(false);
} -> end;
transition "SIP reply" in_call - sipReply / logAll(1) -> lobby;
-- this is not executed - BYE gets processed above
transition "bye in lobby recvd" in_call - hangup / stop(false) -> end;
state end;
Loading…
Cancel
Save