Browse code

- 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

Stefan Sayer authored on 25/05/2010 08:37:56
Showing 10 changed files
... ...
@@ -67,10 +67,16 @@ DSMCall::~DSMCall()
67 67
 /** returns whether var exists && var==value*/
68 68
 bool DSMCall::checkVar(const string& var_name, const string& var_val) {
69 69
   map<string, string>::iterator it = var.find(var_name);
70
-  if ((it != var.end()) && (it->second == var_val)) 
71
-    return true;
70
+  return (it != var.end()) && (it->second == var_val);
71
+}
72 72
 
73
-  return false;
73
+/** returns whether params, param exists && param==value*/
74
+bool checkParam(const string& par_name, const string& par_val, map<string, string>* params) {
75
+  if (NULL == params)
76
+    return false;
77
+
78
+  map<string, string>::iterator it = params->find(par_name);
79
+  return (it != params->end()) && (it->second == par_val);
74 80
 }
75 81
 
76 82
 void DSMCall::onInvite(const AmSipRequest& req) {
... ...
@@ -134,7 +140,7 @@ void DSMCall::onOutgoingInvite(const string& headers) {
134 140
   }
135 141
 }
136 142
 
137
-void DSMCall::onRinging(const AmSipReply& reply) {
143
+ void DSMCall::onRinging(const AmSipReply& reply) {
138 144
   map<string, string> params;
139 145
   params["code"] = int2str(reply.code);
140 146
   params["reason"] = reply.reason;
... ...
@@ -247,7 +253,71 @@ void DSMCall::onCancel() {
247 253
   }
248 254
 }
249 255
 
256
+void DSMCall::onSipRequest(const AmSipRequest& req) {
257
+
258
+  if (checkVar(DSM_ENABLE_REQUEST_EVENTS, DSM_TRUE)) {
259
+    map<string, string> params;
260
+    params["method"] = req.method;
261
+    params["r_uri"] = req.r_uri;
262
+    params["from"] = req.from;
263
+    params["to"] = req.to;
264
+    params["hdrs"] = req.hdrs;
265
+
266
+    params["content_type"] = req.content_type;
267
+    params["body"] = req.body;
268
+
269
+    params["cseq"] = int2str(req.cseq);
270
+
271
+    // pass AmSipRequest for use by mod_dlg
272
+    DSMSipRequest* sip_req = new DSMSipRequest(&req);
273
+    avar[DSM_AVAR_REQUEST] = AmArg(sip_req);
274
+    
275
+    engine.runEvent(this, DSMCondition::SipRequest, &params);
276
+
277
+    delete sip_req;
278
+    avar.erase(DSM_AVAR_REQUEST);
279
+
280
+    if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
281
+      DBG("DSM script processed SIP request '%s', returning\n", 
282
+	  req.method.c_str());
283
+      return;
284
+    }
285
+  }
286
+
287
+  AmB2BCallerSession::onSipRequest(req);  
288
+}
289
+
250 290
 void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
291
+
292
+  if (checkVar(DSM_ENABLE_REPLY_EVENTS, DSM_TRUE)) {
293
+    map<string, string> params;
294
+    params["code"] = int2str(reply.code);
295
+    params["reason"] = reply.reason;
296
+    params["hdrs"] = reply.hdrs;
297
+    params["content_type"] = reply.content_type;
298
+    params["body"] = reply.body;
299
+
300
+    params["cseq"] = int2str(reply.cseq);
301
+
302
+    params["dlg_status"] = int2str(dlg.getStatus());
303
+    params["old_dlg_status"] = int2str(old_dlg_status);
304
+
305
+    // pass AmSipReply for use by mod_dlg (? sending ACK?)
306
+    DSMSipReply* dsm_reply = new DSMSipReply(&reply);
307
+    avar[DSM_AVAR_REPLY] = AmArg(dsm_reply);
308
+    
309
+    engine.runEvent(this, DSMCondition::SipReply, &params);
310
+
311
+    delete dsm_reply;
312
+    avar.erase(DSM_AVAR_REPLY);
313
+
314
+    if (checkParam(DSM_PROCESSED, DSM_TRUE, &params)) {
315
+      DBG("DSM script processed SIP reply '%u %s', returning\n", 
316
+	  reply.code, reply.reason.c_str());
317
+      return;
318
+    }
319
+  }
320
+
251 321
   AmB2BCallerSession::onSipReply(reply,old_dlg_status);
252 322
 
253 323
   if ((old_dlg_status < AmSipDialog::Connected) && 
... ...
@@ -82,7 +82,9 @@ public:
82 82
   void onBye(const AmSipRequest& req);
83 83
   void onDtmf(int event, int duration_msec);
84 84
 
85
+  void onSipRequest(const AmSipRequest& req);
85 86
   void onSipReply(const AmSipReply& reply, int old_dlg_status);
87
+
86 88
   void process(AmEvent* event);
87 89
 
88 90
   UACAuthCred* getCredentials();
... ...
@@ -2,6 +2,7 @@
2 2
  * $Id$
3 3
  *
4 4
  * Copyright (C) 2008 iptego GmbH
5
+ * Copyright (C) 2010 Stefan Sayer
5 6
  *
6 7
  * This file is part of SEMS, a free SIP media server.
7 8
  *
... ...
@@ -169,6 +170,12 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
169 170
   if (cmd == "B2B.otherBye") 
170 171
     return new TestDSMCondition(params, DSMCondition::B2BOtherBye);  
171 172
 
173
+  if (cmd == "sipRequest") 
174
+    return new TestDSMCondition(params, DSMCondition::SipRequest);  
175
+
176
+  if (cmd == "sipReply") 
177
+    return new TestDSMCondition(params, DSMCondition::SipReply);  
178
+
172 179
   return NULL;
173 180
 }
174 181
 
... ...
@@ -459,12 +466,25 @@ EXEC_ACTION_START(SCLogAllAction) {
459 466
 
460 467
 CONST_ACTION_2P(SCSetAction,'=', false);
461 468
 EXEC_ACTION_START(SCSetAction) {
462
-  string var_name = (par1.length() && par1[0] == '$')?
463
-    par1.substr(1) : par1;
469
+  if (par1.length() && par1[0] == '#') {
470
+    // set param
471
+    if (NULL != event_params) {
472
+      string res = resolveVars(par2, sess, sc_sess, event_params);
473
+      (*event_params)[par1.substr(1)] = res;
474
+      DBG("set #%s='%s'\n", par1.substr(1).c_str(), res.c_str());
475
+    } else {
476
+      DBG("not setting %s (no param set)\n", par1.c_str());
477
+    }
478
+  } else {
479
+    // set variable
480
+    string var_name = (par1.length() && par1[0] == '$')?
481
+      par1.substr(1) : par1;
464 482
 
465
-  sc_sess->var[var_name] = resolveVars(par2, sess, sc_sess, event_params);
466
-  DBG("set $%s='%s'\n", 
467
-      var_name.c_str(), sc_sess->var[var_name].c_str());
483
+    sc_sess->var[var_name] = resolveVars(par2, sess, sc_sess, event_params);
484
+    
485
+    DBG("set $%s='%s'\n", 
486
+	var_name.c_str(), sc_sess->var[var_name].c_str());
487
+  }
468 488
 } EXEC_ACTION_END;
469 489
 
470 490
 string replaceParams(const string& q, AmSession* sess, DSMSession* sc_sess, 
... ...
@@ -511,13 +531,25 @@ string replaceParams(const string& q, AmSession* sess, DSMSession* sc_sess,
511 531
 }
512 532
 CONST_ACTION_2P(SCSetSAction,'=', false);
513 533
 EXEC_ACTION_START(SCSetSAction) {
514
-  string var_name = (par1.length() && par1[0] == '$')?
515
-    par1.substr(1) : par1;
534
+  if (par1.length() && par1[0] == '#') {
535
+    // set param
536
+    if (NULL != event_params) {
537
+      string res = replaceParams(par2, sess, sc_sess, event_params);
538
+      (*event_params)[par1.substr(1)] = res;
539
+      DBG("set #%s='%s'\n", par1.substr(1).c_str(), res.c_str());
540
+    } else {
541
+      DBG("not set %s (no param set)\n", par1.c_str());
542
+    }
543
+  } else {
544
+    // set variable
545
+    string var_name = (par1.length() && par1[0] == '$')?
546
+      par1.substr(1) : par1;
516 547
 
517
-  sc_sess->var[var_name] = replaceParams(par2, sess, sc_sess, event_params);
518
-					 
519
-  DBG("set $%s='%s'\n", 
520
-      var_name.c_str(), sc_sess->var[var_name].c_str());
548
+    sc_sess->var[var_name] = replaceParams(par2, sess, sc_sess, event_params);
549
+    
550
+    DBG("set $%s='%s'\n", 
551
+	var_name.c_str(), sc_sess->var[var_name].c_str());
552
+  }
521 553
 } EXEC_ACTION_END;
522 554
 
523 555
 CONST_ACTION_2P(SCEvalAction,'=', false);
... ...
@@ -53,6 +53,8 @@ class DSMModule {
53 53
   virtual bool onInvite(const AmSipRequest& req, DSMSession* sess) { return true; }
54 54
 };
55 55
 
56
+typedef map<string,string> EventParamT;
57
+
56 58
 typedef void* (*SCFactoryCreate)();
57 59
 
58 60
 #define SCSTR(x) #x
... ...
@@ -40,8 +40,11 @@ using std::vector;
40 40
 using std::map;
41 41
 #include <memory>
42 42
 
43
-#define DSM_REPLY_REQUEST      "reply_request" // todo: rethink these names
44
-#define DSM_REPLY_REQUEST_FALSE "0"
43
+#define DSM_TRUE "true"
44
+#define DSM_FALSE "false"
45
+
46
+#define DSM_PROCESSED "processed"
47
+#define DSM_CONNECT   "connect"
45 48
 
46 49
 #define DSM_CONNECT_SESSION    "connect_session" // todo: rethink these names
47 50
 #define DSM_CONNECT_SESSION_FALSE    "0"
... ...
@@ -52,6 +55,13 @@ using std::map;
52 55
 #define DSM_CONNECT_EARLY_SESSION        "connect_early_session" // todo: rethink these names
53 56
 #define DSM_CONNECT_EARLY_SESSION_FALSE    "0"
54 57
 
58
+#define DSM_ENABLE_REQUEST_EVENTS  "enable_request_events"
59
+#define DSM_ENABLE_REPLY_EVENTS    "enable_reply_events"
60
+
61
+
62
+#define DSM_AVAR_REQUEST "request"
63
+#define DSM_AVAR_REPLY   "reply"
64
+
55 65
 #define DSM_ERRNO_FILE        "file"
56 66
 #define DSM_ERRNO_UNKNOWN_ARG "arg"
57 67
 #define DSM_ERRNO_SCRIPT      "script"
... ...
@@ -74,6 +84,9 @@ using std::map;
74 84
 #define CLR_STRERROR				\
75 85
   var["strerror"] = "";
76 86
 
87
+typedef map<string, string> VarMapT;
88
+typedef map<string, AmArg>  AVarMapT;
89
+
77 90
 class DSMDisposable;
78 91
 class AmPlaylistItem;
79 92
 
... ...
@@ -127,10 +140,10 @@ class DSMSession {
127 140
   virtual void releaseOwnership(DSMDisposable* d) = 0;
128 141
 
129 142
   /* holds variables which are accessed by $varname */
130
-  map<string, string> var;
143
+  VarMapT var;
131 144
 
132
-  /* holds AmArg variables. todo: merge var with these */
133
-  map<string, AmArg> avar;
145
+  /* holds AmArg variables. todo(?): merge var with these */
146
+  AVarMapT avar;
134 147
 
135 148
   /* result of the last DI call */
136 149
   AmArg di_res;
... ...
@@ -164,6 +177,27 @@ class DSMDisposableAudioFile
164 177
   ~DSMDisposableAudioFile() { }
165 178
 };
166 179
 
180
+class DSMSipRequest
181
+: public ArgObject {
182
+ public: 
183
+  const AmSipRequest* req;
184
+
185
+  DSMSipRequest(const AmSipRequest* req)
186
+    : req(req)  { }
187
+  ~DSMSipRequest() { }
188
+};
189
+
190
+class DSMSipReply
191
+: public ArgObject {
192
+ public: 
193
+  const AmSipReply* reply;
194
+
195
+  DSMSipReply(const AmSipReply* reply)
196
+    : reply(reply)  { }
197
+  ~DSMSipReply() { }
198
+};
199
+
200
+
167 201
 #define DSM_EVENT_ID -10
168 202
 /**  generic event for passing events between DSM sessions */
169 203
 struct DSMEvent : public AmEvent {
... ...
@@ -57,29 +57,33 @@ class DSMCondition
57 57
  public:
58 58
   enum EventType {
59 59
     Any,
60
+
60 61
     Invite,
61 62
     SessionStart,
62 63
     Ringing,
63 64
     EarlySession,
64 65
     FailedCall,
65
-    Key,
66
-    Timer,
67
-
68
-    NoAudio,
66
+    SipRequest,
67
+    SipReply,
69 68
 
70 69
     Hangup,
71 70
     Hold,
72 71
     UnHold,
73 72
 
74
-    XmlrpcResponse,
75
-    DSMEvent,
76
-    PlaylistSeparator,
77
-    
78 73
     B2BOtherReply,
79 74
     B2BOtherBye,
80 75
 
76
+    Key,
77
+    Timer,
78
+
79
+    NoAudio,
80
+    PlaylistSeparator,
81
+
82
+    DSMEvent,    
81 83
     DSMException,
82 84
 
85
+    XmlrpcResponse,
86
+
83 87
     JsonRpcResponse,
84 88
     JsonRpcRequest
85 89
   };
... ...
@@ -43,6 +43,7 @@ SC_EXPORT(MOD_CLS_NAME);
43 43
 MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
44 44
 
45 45
   DEF_CMD("dlg.reply", DLGReplyAction);
46
+  DEF_CMD("dlg.replyRequest", DLGReplyRequestAction);
46 47
   DEF_CMD("dlg.acceptInvite", DLGAcceptInviteAction);
47 48
   DEF_CMD("dlg.bye", DLGByeAction);
48 49
   DEF_CMD("dlg.connectCalleeRelayed", DLGConnectCalleeRelayedAction);
... ...
@@ -66,29 +67,52 @@ bool DLGModule::onInvite(const AmSipRequest& req, DSMSession* sess) {
66 67
     return false;					 \
67 68
   }
68 69
 
69
-CONST_ACTION_2P(DLGReplyAction, ',', true);
70
-EXEC_ACTION_START(DLGReplyAction) {
70
+// todo: convert errors to exceptions
71
+void replyRequest(DSMSession* sc_sess, AmSession* sess, 
72
+		  EventParamT* event_params,
73
+		  const string& par1, const string& par2,
74
+		  const AmSipRequest& req) {
71 75
   string code = resolveVars(par1, sess, sc_sess, event_params);
72 76
   string reason = resolveVars(par2, sess, sc_sess, event_params);
73 77
   unsigned int code_i;
74 78
   if (str2i(code, code_i)) {
75 79
     ERROR("decoding reply code '%s'\n", code.c_str());
76 80
     sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
77
-    return false;
81
+    return;
78 82
   }
79 83
 
80 84
   if (!sc_sess->last_req.get()) {
81 85
     ERROR("no last request to reply\n");
82 86
     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
83 87
     sc_sess->SET_STRERROR("no last request to reply");
84
-    return false;
88
+    return;
85 89
   }
86 90
 
87
-  if (sess->dlg.reply(*sc_sess->last_req.get(), code_i, reason)) {
91
+  if (sess->dlg.reply(req, code_i, reason)) {
88 92
     sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL);
89 93
     sc_sess->SET_STRERROR("error sending reply");
90 94
   } else
91 95
     sc_sess->CLR_ERRNO;
96
+}
97
+
98
+CONST_ACTION_2P(DLGReplyAction, ',', true);
99
+EXEC_ACTION_START(DLGReplyAction) {
100
+  replyRequest(sc_sess, sess, event_params, par1, par2, *sc_sess->last_req.get());
101
+} EXEC_ACTION_END;
102
+
103
+// todo (?) move replyRequest to core module (?)
104
+CONST_ACTION_2P(DLGReplyRequestAction, ',', true);
105
+EXEC_ACTION_START(DLGReplyRequestAction) {
106
+  DSMSipRequest* sip_req;
107
+
108
+  AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST);
109
+  if (it == sc_sess->avar.end() ||
110
+      !isArgAObject(it->second) || 
111
+      !(sip_req = dynamic_cast<DSMSipRequest*>(it->second.asObject()))) {
112
+    throw DSMException("dlg", "cause", "no request");
113
+  }
114
+    
115
+  replyRequest(sc_sess, sess, event_params, par1, par2, *sip_req->req);
92 116
 } EXEC_ACTION_END;
93 117
 
94 118
 CONST_ACTION_2P(DLGAcceptInviteAction, ',', true);
... ...
@@ -35,6 +35,7 @@ bool onInvite(const AmSipRequest& req, DSMSession* sess);
35 35
 DECLARE_MODULE_END;
36 36
 
37 37
 DEF_ACTION_2P(DLGReplyAction);
38
+DEF_ACTION_2P(DLGReplyRequestAction);
38 39
 DEF_ACTION_2P(DLGAcceptInviteAction);
39 40
 DEF_ACTION_2P(DLGConnectCalleeRelayedAction);
40 41
 DEF_ACTION_1P(DLGByeAction);
... ...
@@ -1,13 +1,22 @@
1
-* mod_dlg saves the initial INVITE to DSMSession::last_req
1
+* mod_dlg saves the initial INVITE to DSMSession::last_req,
2
+  this can be processed with dlg.reply(...)/dlg.acceptInvite(...)
2 3
 * set connect_session to 0 with set(connect_session=0)
3 4
   if you want to reply with other than the standard 200 OK 
4 5
   to initial INVITE received.
6
+* for processing of other requests, use enable_request_events and replyRequest
5 7
 
6 8
 dlg.reply(code,reason);
7 9
  reply to the request in DSMSession::last_req 
8 10
  (usually INVITE, if not yet replied) with code and reason
9 11
  * sets $errno (arg,general)
10 12
 
13
+dlg.replyRequest(code,reason);
14
+ request processing in script; use with set($enable_request_events="true");
15
+ reply to any request (in avar[DSM_AVAR_REQUEST]) with code and reason
16
+ * sets $errno (arg,general)
17
+ * throws exception if request not found (i.e. called from other event than
18
+   sipRequest)
19
+
11 20
 dlg.acceptInvite([code, reason]);
12 21
  e.g. dlg.acceptInvite(183, progress);
13 22
  * sets $errno (arg,general)
14 23
new file mode 100644
... ...
@@ -0,0 +1,24 @@
1
+-- example for SIP request processing from script
2
+
3
+import(mod_dlg);
4
+
5
+initial state in_call
6
+  enter { 
7
+    -- from now on, get events for SIP requests
8
+    set($enable_request_events="true");
9
+  };
10
+
11
+transition "SIP BYE request" in_call - sipRequest(#method==BYE) / {
12
+  dlg.replyRequest(200, "okey bye bye");
13
+  -- set event param "processed" - BYE will not be processed by normal app logic
14
+  set(#processed=true);
15
+  logAll(1);
16
+  stop(false);
17
+ } -> end;
18
+
19
+transition "SIP reply" in_call - sipReply / logAll(1) -> lobby;
20
+
21
+-- this is not executed - BYE gets processed above
22
+transition "bye in lobby recvd" in_call - hangup / stop(false) -> end;
23
+
24
+state end;
0 25
\ No newline at end of file