Browse code

Merge branch 'master' into bpintea/prack

Resolved conflicts:

core/AmSessionEventHandler.h
core/plug-in/uac_auth/UACAuth.cpp

b/f: missing 'old_dlg_status' param in some onReply's:

apps/auth_b2b/AuthB2B.cpp
apps/registrar_client/SIPRegistrarClient.cpp
apps/sst_b2b/SSTB2B.cpp

bpintea authored on 01/08/2010 14:44:27
Showing 117 changed files
... ...
@@ -9,3 +9,4 @@ getarch
9 9
 getos
10 10
 sems
11 11
 sems-stats
12
+core/etc/*.conf
12 13
\ No newline at end of file
... ...
@@ -7,7 +7,9 @@ GIT = $(shell which git)
7 7
 SVNVERSION = $(shell which svnversion)
8 8
 
9 9
 ifneq ($(GIT),)
10
-   SCM_REV = $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; then git describe --always --dirty; else git describe --always ; fi) 
10
+   SCM_REV = $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; \
11
+        then git describe --always --dirty 2>/dev/null; \
12
+        else git describe --always 2>/dev/null; fi)
11 13
 endif
12 14
 
13 15
 ifeq ($(SCM_REV),)
... ...
@@ -112,7 +112,7 @@ void AnnounceTransferDialog::onSessionStart(const AmSipRequest& req)
112 112
     status = Announcing;
113 113
     callee_uri = get_session_param(req.hdrs, "Refer-To");
114 114
     if (!callee_uri.length()) {
115
-      callee_uri = getHeader(req.hdrs, "P-Refer-To");
115
+      callee_uri = getHeader(req.hdrs, "P-Refer-To", true);
116 116
       if (callee_uri.length()) {
117 117
 	INFO("Use of P-Refer-To header is deprecated. "
118 118
 	     "Use '%s: Refer-To=<uri>' instead.\n",PARAM_HDR);
... ...
@@ -140,7 +140,7 @@ void AnnounceTransferDialog::onSipRequest(const AmSipRequest& req)
140 140
      (req.method == "NOTIFY")) {
141 141
     try {
142 142
 
143
-      if (strip_header_params(getHeader(req.hdrs,"Event", "o")) != "refer") 
143
+      if (strip_header_params(getHeader(req.hdrs,"Event", "o", true)) != "refer") 
144 144
 	throw AmSession::Exception(481, "Subscription does not exist");
145 145
 
146 146
       if ((strip_header_params(req.content_type) != "message/sipfrag"))
... ...
@@ -142,7 +142,7 @@ void AnnRecorderFactory::getAppParams(const AmSipRequest& req, map<string, strin
142 142
     typ = DEFAULT_TYPE;
143 143
   }
144 144
   else {
145
-    string iptel_app_param = getHeader(req.hdrs, PARAM_HDR);
145
+    string iptel_app_param = getHeader(req.hdrs, PARAM_HDR, true);
146 146
     
147 147
     if (!iptel_app_param.length()) {
148 148
       throw AmSession::Exception(500, MOD_NAME ": parameters not found");
... ...
@@ -113,7 +113,7 @@ void AuthB2BDialog::onInvite(const AmSipRequest& req)
113 113
   setInOut(NULL,NULL); 
114 114
 
115 115
   if (AuthB2BFactory::user.empty()) {
116
-    string app_param = getHeader(req.hdrs, PARAM_HDR);
116
+    string app_param = getHeader(req.hdrs, PARAM_HDR, true);
117 117
 
118 118
     if (!app_param.length()) {
119 119
       AmSession::Exception(500, "auth_b2b: parameters not found");
... ...
@@ -321,7 +321,7 @@ void AuthB2BCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_statu
321 321
   }
322 322
   
323 323
   unsigned int cseq_before = dlg.cseq;
324
-  if (!auth->onSipReply(reply)) {
324
+  if (!auth->onSipReply(reply, old_dlg_status)) {
325 325
       AmB2BCalleeSession::onSipReply(reply,old_dlg_status);
326 326
   } else {
327 327
     if (cseq_before != dlg.cseq) {
... ...
@@ -88,7 +88,7 @@ AmSession* CallTimerFactory::onInvite(const AmSipRequest& req)
88 88
      throw AmSession::Exception(500,"could not get a user timer reference");
89 89
   }
90 90
 
91
-  string app_param = getHeader(req.hdrs, PARAM_HDR);
91
+  string app_param = getHeader(req.hdrs, PARAM_HDR, true);
92 92
 
93 93
   unsigned int call_time = CallTimerFactory::DefaultCallTimer;
94 94
 
... ...
@@ -379,22 +379,22 @@ void ConferenceDialog::onSessionStart(const AmSipRequest& req)
379 379
   int i, len;
380 380
   string lonely_user_file;
381 381
 
382
-  string app_param_hdr = getHeader(req.hdrs, PARAM_HDR);
382
+  string app_param_hdr = getHeader(req.hdrs, PARAM_HDR, true);
383 383
   if (app_param_hdr.length()) {
384 384
     from_header = get_header_keyvalue(app_param_hdr, "Dialout-From");
385 385
     extra_headers = get_header_keyvalue(app_param_hdr, "Dialout-Extra");
386 386
     dialout_suffix = get_header_keyvalue(app_param_hdr, "Dialout-Suffix");      
387 387
     language = get_header_keyvalue(app_param_hdr, "Language");      
388 388
   } else {
389
-    from_header = getHeader(req.hdrs, "P-Dialout-From");
390
-    extra_headers = getHeader(req.hdrs, "P-Dialout-Extra");
391
-    dialout_suffix = getHeader(req.hdrs, "P-Dialout-Suffix");
389
+    from_header = getHeader(req.hdrs, "P-Dialout-From", true);
390
+    extra_headers = getHeader(req.hdrs, "P-Dialout-Extra", true);
391
+    dialout_suffix = getHeader(req.hdrs, "P-Dialout-Suffix", true);
392 392
     if (from_header.length() || extra_headers.length() 
393 393
 	|| dialout_suffix.length()) {
394 394
       DBG("Warning: P-Dialout- style headers are deprecated."
395 395
 	  " Please use P-App-Param header instead.\n");
396 396
     }
397
-    language = getHeader(req.hdrs, "P-Language");
397
+    language = getHeader(req.hdrs, "P-Language", true);
398 398
     if (language.length()) {
399 399
       DBG("Warning: P-Language header is deprecated."
400 400
 	  " Please use P-App-Param header instead.\n");
... ...
@@ -837,14 +837,14 @@ void ConferenceDialog::onSipRequest(const AmSipRequest& req)
837 837
   dlg.remote_tag = "";
838 838
 
839 839
   // get route set and next hop
840
-  string iptel_app_param = getHeader(req.hdrs, PARAM_HDR);
840
+  string iptel_app_param = getHeader(req.hdrs, PARAM_HDR, true);
841 841
   if (iptel_app_param.length()) {
842 842
     dlg.route = get_header_keyvalue(iptel_app_param,"Transfer-RR");
843 843
   } else {
844 844
     INFO("Use of P-Transfer-RR/P-Transfer-NH is deprecated. "
845 845
 	 "Use '%s: Transfer-RR=<rr>;Transfer-NH=<nh>' instead.\n",PARAM_HDR);
846 846
 
847
-    dlg.route = getHeader(req.hdrs,"P-Transfer-RR");
847
+    dlg.route = getHeader(req.hdrs,"P-Transfer-RR", true);
848 848
   }
849 849
 
850 850
   DBG("ConferenceDialog::onSipRequest: local_party = %s\n",dlg.local_party.c_str());
... ...
@@ -37,6 +37,7 @@
37 37
 #include "DSMChartReader.h"
38 38
 #include "AmSipHeaders.h"
39 39
 #include "AmEventDispatcher.h"
40
+#include "SystemDSM.h"
40 41
 
41 42
 #include <string>
42 43
 #include <fstream>
... ...
@@ -63,6 +64,7 @@ DSMFactory* DSMFactory::instance()
63 64
 bool DSMFactory::DebugDSM;
64 65
 string DSMFactory::InboundStartDiag;
65 66
 string DSMFactory::OutboundStartDiag;
67
+bool DSMFactory::CheckDSM;
66 68
 
67 69
 #ifdef USE_MONITORING
68 70
 bool DSMFactory::MonitoringFullCallgraph;
... ...
@@ -70,8 +72,10 @@ bool DSMFactory::MonitoringFullTransitions;
70 72
 
71 73
 MonSelectType DSMFactory::MonSelectCaller;
72 74
 MonSelectType DSMFactory::MonSelectCallee;
75
+vector<string> DSMFactory::MonSelectFilters;
73 76
 
74 77
 string DSMFactory::MonSelectFallback;
78
+
75 79
 #endif // USE_MONITORING
76 80
 
77 81
 DSMFactory::DSMFactory(const string& _app_name)
... ...
@@ -122,6 +126,7 @@ int DSMFactory::onLoad()
122 126
   configureModule(cfg);
123 127
 
124 128
   DebugDSM = cfg.getParameter("debug_raw_dsm") == "yes";
129
+  CheckDSM = cfg.getParameter("dsm_consistency_check", "yes") == "yes";
125 130
  
126 131
   if (!loadPrompts(cfg))
127 132
     return -1;
... ...
@@ -161,6 +166,17 @@ int DSMFactory::onLoad()
161 166
 
162 167
   MainScriptConfig.SetParamVariables = cfg.getParameter("set_param_variables")=="yes";
163 168
 
169
+  vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
170
+  for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
171
+    string status;
172
+    if (createSystemDSM("main", *it, false /* reload */, status)) {
173
+      DBG("created SystemDSM '%s'\n", it->c_str());
174
+    } else {
175
+      ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
176
+      return -1;
177
+    }
178
+  }
179
+
164 180
 #ifdef USE_MONITORING
165 181
   string monitoring_full_callgraph = cfg.getParameter("monitoring_full_stategraph");
166 182
   MonitoringFullCallgraph = monitoring_full_callgraph == "yes";
... ...
@@ -196,6 +212,20 @@ int DSMFactory::onLoad()
196 212
 	  cfg_usecallee.c_str());
197 213
   }
198 214
 
215
+  MonSelectFilters  = explode(cfg.getParameter("monitor_select_filters"), ",");
216
+  string filters;
217
+  for (vector<string>::iterator it = 
218
+	 MonSelectFilters.begin(); it != MonSelectFilters.end(); it++) {
219
+    if (it != MonSelectFilters.begin()) 
220
+      filters += ", ";
221
+    filters+=*it;
222
+  }
223
+  if (MonSelectFilters.size()) {
224
+    DBG("using additional monitor app select filters: %s\n", 
225
+	filters.c_str());
226
+  } else {
227
+    DBG("not using additional monitor app select filters\n");
228
+  }
199 229
   MonSelectFallback = cfg.getParameter("monitor_select_fallback");
200 230
 #endif
201 231
 
... ...
@@ -327,7 +357,7 @@ bool DSMFactory::loadDiags(AmConfigReader& cfg, DSMStateDiagramCollection* m_dia
327 357
   vector<string> diags_names = explode(LoadDiags, ",");
328 358
   for (vector<string>::iterator it=
329 359
 	 diags_names.begin(); it != diags_names.end(); it++) {
330
-    if (!m_diags->loadFile(DiagPath+*it+".dsm", *it, ModPath, DebugDSM)) {
360
+    if (!m_diags->loadFile(DiagPath+*it+".dsm", *it, ModPath, DebugDSM, CheckDSM)) {
331 361
       ERROR("loading %s from %s\n", 
332 362
 	    it->c_str(), (DiagPath+*it+".dsm").c_str());
333 363
       return false;
... ...
@@ -417,6 +447,7 @@ bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_nam
417 447
 
418 448
   ScriptConfigs_mut.lock();
419 449
   try {
450
+    Name2ScriptConfig[script_name] = script_config;
420 451
     // set ScriptConfig to this for all registered apps' names
421 452
     for (vector<string>::iterator reg_app_it=
422 453
 	   registered_apps.begin(); reg_app_it != registered_apps.end(); reg_app_it++) {
... ...
@@ -439,7 +470,19 @@ bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_nam
439 470
   }
440 471
   ScriptConfigs_mut.unlock();
441 472
 
442
-  return true;
473
+  bool res = true;
474
+
475
+  vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
476
+  for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
477
+    string status;
478
+    if (createSystemDSM(script_name, *it, live_reload, status)) {
479
+    } else {
480
+      ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
481
+      res = false;
482
+    }
483
+  }
484
+
485
+  return res;
443 486
 }
444 487
 
445 488
 
... ...
@@ -457,7 +500,7 @@ void DSMFactory::addVariables(DSMCall* s, const string& prefix,
457 500
 void DSMFactory::addParams(DSMCall* s, const string& hdrs) {
458 501
   // TODO: use real parser with quoting and optimize
459 502
   map<string, string> params;
460
-  vector<string> items = explode(getHeader(hdrs, PARAM_HDR), ";");
503
+  vector<string> items = explode(getHeader(hdrs, PARAM_HDR, true), ";");
461 504
   for (vector<string>::iterator it=items.begin(); 
462 505
        it != items.end(); it++) {
463 506
     vector<string> kv = explode(*it, "=");
... ...
@@ -487,12 +530,13 @@ void AmArg2DSMStrMap(const AmArg& arg,
487 530
   }
488 531
 }
489 532
 
490
-void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag, map<string, string>& vars) {
533
+void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag, 
534
+				     map<string, string>& vars) {
491 535
 #define FALLBACK_OR_EXCEPTION(code, reason)				\
492 536
   if (MonSelectFallback.empty()) {					\
493 537
     throw AmSession::Exception(code, reason);				\
494 538
   } else {								\
495
-    INFO("falling back to '%s'\n", MonSelectFallback.c_str());		\
539
+    DBG("falling back to '%s'\n", MonSelectFallback.c_str());		\
496 540
     start_diag = MonSelectFallback;					\
497 541
     return;								\
498 542
   }
... ...
@@ -510,7 +554,7 @@ void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag
510 554
 	  from_parser.uri = req.from_uri;
511 555
 	else {
512 556
 	  size_t end;
513
-	  string pai = getHeader(req.hdrs, SIP_HDR_P_ASSERTED_IDENTITY);
557
+	  string pai = getHeader(req.hdrs, SIP_HDR_P_ASSERTED_IDENTITY, true);
514 558
 	  if (!from_parser.parse_contact(pai, 0, end)) {
515 559
 	    WARN("Failed to parse " SIP_HDR_P_ASSERTED_IDENTITY " '%s'\n",
516 560
 		  pai.c_str());
... ...
@@ -551,13 +595,27 @@ void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag
551 595
 	}
552 596
 	  
553 597
 	DBG(" && looking for callee=='%s'\n", req.user.c_str());
554
-	di_args.push(callee_filter);	
598
+	di_args.push(callee_filter);
599
+      }
600
+      // apply additional filters
601
+      if (MonSelectFilters.size()) {
602
+	string app_params = getHeader(req.hdrs, PARAM_HDR);
603
+	for (vector<string>::iterator it = 
604
+	       MonSelectFilters.begin(); it != MonSelectFilters.end(); it++) {
605
+	  AmArg filter;
606
+	  filter.push(*it); // avp name
607
+	  string app_param_val = get_header_keyvalue(app_params, *it);
608
+	  filter.push(app_param_val);
609
+	  di_args.push(filter);
610
+	  DBG(" && looking for %s=='%s'\n", it->c_str(), app_param_val.c_str());
611
+	}
555 612
       }
613
+
556 614
       MONITORING_GLOBAL_INTERFACE->invoke("listByFilter",di_args,ret);
557 615
       
558 616
       if ((ret.getType()!=AmArg::Array)||
559 617
 	  !ret.size()) {
560
-	INFO("call info not found. caller uri %s, r-uri %s\n", 
618
+	DBG("call info not found. caller uri %s, r-uri %s\n", 
561 619
 	     req.from_uri.c_str(), req.r_uri.c_str());
562 620
 	FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
563 621
       }
... ...
@@ -666,7 +724,7 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req,
666 724
     if (cred_obj)
667 725
       cred = dynamic_cast<UACAuthCred*>(cred_obj);
668 726
   } else if (session_params.getType() == AmArg::Array) {
669
-    DBG("session params is array - size %d\n", session_params.size());
727
+    DBG("session params is array - size %zd\n", session_params.size());
670 728
     // Creds
671 729
     if (session_params.get(0).getType() == AmArg::AObject) {
672 730
       ArgObject* cred_obj = session_params.get(0).asObject();
... ...
@@ -705,7 +763,7 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req,
705 763
     addParams(s, req.hdrs); 
706 764
 
707 765
   if (NULL == cred) {
708
-    WARN("discarding unknown session parameters.\n");
766
+    DBG("outgoing DSM call will not be authenticated.\n");
709 767
   } else {
710 768
     AmSessionEventHandlerFactory* uac_auth_f = 
711 769
       AmPlugIn::instance()->getFactory4Seh("uac_auth");
... ...
@@ -723,6 +781,39 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req,
723 781
   return s;
724 782
 }
725 783
 
784
+bool DSMFactory::createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status) {
785
+  bool res = true;
786
+
787
+  DSMScriptConfig* script_config = NULL;
788
+  ScriptConfigs_mut.lock();
789
+  if (config_name == "main")
790
+    script_config = &MainScriptConfig;
791
+  else {
792
+    map<string, DSMScriptConfig>::iterator it = Name2ScriptConfig.find(config_name);
793
+    if (it != Name2ScriptConfig.end()) 
794
+      script_config = &it->second;
795
+  }
796
+  if (script_config==NULL) {
797
+    status = "Error: Script config '"+config_name+"' not found, in [";
798
+    for (map<string, DSMScriptConfig>::iterator it = 
799
+	   Name2ScriptConfig.begin(); it != Name2ScriptConfig.end(); it++) {
800
+      if (it != Name2ScriptConfig.begin()) 
801
+	status+=", ";
802
+      status += it->first; 
803
+    }
804
+    status += "]";
805
+    res = false;
806
+  } else {
807
+    SystemDSM* s = new SystemDSM(*script_config, start_diag, reload);
808
+    s->start();
809
+    // add to garbage collector
810
+    AmThreadWatcher::instance()->add(s);
811
+    status = "OK";
812
+  }
813
+  ScriptConfigs_mut.unlock();
814
+  return res;
815
+}
816
+
726 817
 void DSMFactory::reloadDSMs(const AmArg& args, AmArg& ret) {
727 818
   DSMStateDiagramCollection* new_diags = new DSMStateDiagramCollection();
728 819
 
... ...
@@ -743,7 +834,7 @@ void DSMFactory::reloadDSMs(const AmArg& args, AmArg& ret) {
743 834
   vector<string> diags_names = explode(LoadDiags, ",");
744 835
   for (vector<string>::iterator it=
745 836
 	 diags_names.begin(); it != diags_names.end(); it++) {
746
-    if (!new_diags->loadFile(DiagPath+*it+".dsm", *it, ModPath, DebugDSM)) {
837
+    if (!new_diags->loadFile(DiagPath+*it+".dsm", *it, ModPath, DebugDSM, CheckDSM)) {
747 838
       ERROR("loading %s from %s\n", 
748 839
 	    it->c_str(), (DiagPath+*it+".dsm").c_str());
749 840
       ret.push(500);
... ...
@@ -949,7 +1040,7 @@ void DSMFactory::loadDSM(const AmArg& args, AmArg& ret) {
949 1040
       ret.push(400);
950 1041
       ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
951 1042
     } else {
952
-      if (!MainScriptConfig.diags->loadFile(dsm_file_name, dsm_name, ModPath, DebugDSM)) {
1043
+      if (!MainScriptConfig.diags->loadFile(dsm_file_name, dsm_name, ModPath, DebugDSM, CheckDSM)) {
953 1044
 	ret.push(500);
954 1045
 	ret.push("error loading "+dsm_name+" from "+ dsm_file_name);
955 1046
       } else {
... ...
@@ -976,7 +1067,7 @@ void DSMFactory::loadDSMWithPaths(const AmArg& args, AmArg& ret) {
976 1067
       ret.push(400);
977 1068
       ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
978 1069
     } else {
979
-      if (!MainScriptConfig.diags->loadFile(diag_path+dsm_name+".dsm", dsm_name, mod_path, DebugDSM)) {
1070
+      if (!MainScriptConfig.diags->loadFile(diag_path+dsm_name+".dsm", dsm_name, mod_path, DebugDSM, CheckDSM)) {
980 1071
 	ret.push(500);
981 1072
 	ret.push("error loading "+dsm_name+" from "+ diag_path+dsm_name+".dsm");
982 1073
       } else {
... ...
@@ -1032,6 +1123,16 @@ void DSMFactory::invoke(const string& method, const AmArg& args,
1032 1123
   } else if (method == "loadConfig"){
1033 1124
     args.assertArrayFmt("ss");
1034 1125
     loadConfig(args,ret);
1126
+  } else if (method == "createSystemDSM"){
1127
+    args.assertArrayFmt("ss");
1128
+    string status;
1129
+    if (createSystemDSM(args.get(0).asCStr(), args.get(1).asCStr(), false, status)) {
1130
+      ret.push(200);
1131
+      ret.push(status);
1132
+    } else {
1133
+      ret.push(500);
1134
+      ret.push(status);
1135
+    }
1035 1136
   } else if(method == "_list"){ 
1036 1137
     ret.push(AmArg("postDSMEvent"));
1037 1138
     ret.push(AmArg("reloadDSMs"));
... ...
@@ -1043,6 +1144,8 @@ void DSMFactory::invoke(const string& method, const AmArg& args,
1043 1144
     ret.push(AmArg("hasDSM"));
1044 1145
     ret.push(AmArg("listDSMs"));
1045 1146
     ret.push(AmArg("registerApplication"));
1147
+    ret.push(AmArg("createSystemDSM"));
1046 1148
   }  else
1047 1149
     throw AmDynInvoke::NotImplemented(method);
1048 1150
 }
1151
+
... ...
@@ -67,18 +67,23 @@ class DSMFactory
67 67
   std::set<DSMStateDiagramCollection*> old_diags;
68 68
 
69 69
   static bool DebugDSM;
70
+  static bool CheckDSM;
70 71
 
71 72
   static string InboundStartDiag;
72 73
   static string OutboundStartDiag;
73 74
 
74 75
   DSMScriptConfig MainScriptConfig;
76
+  // script name -> config
75 77
   map<string, DSMScriptConfig> ScriptConfigs;
78
+  // config name -> config
79
+  map<string, DSMScriptConfig> Name2ScriptConfig;
76 80
   AmMutex ScriptConfigs_mut;
77 81
 
78 82
 #ifdef USE_MONITORING
79 83
   static MonSelectType MonSelectCaller;
80 84
   static MonSelectType MonSelectCallee;
81 85
   static string MonSelectFallback;
86
+  static vector<string> MonSelectFilters;
82 87
 
83 88
 #endif // USE_MONITORING
84 89
 
... ...
@@ -139,6 +144,8 @@ public:
139 144
 
140 145
   void postEvent(AmEvent* e);
141 146
 
147
+  bool createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status);
148
+
142 149
 };
143 150
 
144 151
 #endif
... ...
@@ -93,7 +93,7 @@ void DSMCall::onInvite(const AmSipRequest& req) {
93 93
   bool run_session_invite = engine.onInvite(req, this);
94 94
 
95 95
   if (run_invite_event) {
96
-    if (!engine.init(this, startDiagName, DSMCondition::Invite))
96
+    if (!engine.init(this, this, startDiagName, DSMCondition::Invite))
97 97
       run_session_invite =false;
98 98
 
99 99
     if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) {
... ...
@@ -120,7 +120,7 @@ void DSMCall::onOutgoingInvite(const string& headers) {
120 120
   bool run_session_invite = engine.onInvite(req, this);
121 121
 
122 122
   if (run_invite_event) {
123
-    if (!engine.init(this, startDiagName, DSMCondition::Invite))
123
+    if (!engine.init(this, this, startDiagName, DSMCondition::Invite))
124 124
       run_session_invite =false;
125 125
 
126 126
     if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) {
... ...
@@ -146,7 +146,7 @@ void DSMCall::onOutgoingInvite(const string& headers) {
146 146
   params["reason"] = reply.reason;
147 147
   params["has_body"] = reply.body.empty() ?
148 148
     "false" : "true";
149
-  engine.runEvent(this, DSMCondition::Ringing, &params);
149
+  engine.runEvent(this, this, DSMCondition::Ringing, &params);
150 150
   // todo: local ringbacktone
151 151
 }
152 152
 
... ...
@@ -156,7 +156,7 @@ void DSMCall::onEarlySessionStart(const AmSipReply& reply) {
156 156
   params["reason"] = reply.reason;
157 157
   params["has_body"] = reply.body.empty() ?
158 158
     "false" : "true";
159
-  engine.runEvent(this, DSMCondition::EarlySession, &params);
159
+  engine.runEvent(this, this, DSMCondition::EarlySession, &params);
160 160
 
161 161
   if (checkVar(DSM_CONNECT_EARLY_SESSION, DSM_CONNECT_EARLY_SESSION_FALSE)) {
162 162
     DBG("call does not connect early session\n");
... ...
@@ -192,7 +192,7 @@ void DSMCall::onSessionStart(const AmSipReply& rep)
192 192
 }
193 193
 
194 194
 void DSMCall::startSession(){
195
-  engine.init(this, startDiagName, DSMCondition::SessionStart);
195
+  engine.init(this, this, startDiagName, DSMCondition::SessionStart);
196 196
 
197 197
   setReceiving(true);
198 198
 
... ...
@@ -232,7 +232,7 @@ void DSMCall::onDtmf(int event, int duration_msec) {
232 232
   map<string, string> params;
233 233
   params["key"] = int2str(event);
234 234
   params["duration"] = int2str(duration_msec);
235
-  engine.runEvent(this, DSMCondition::Key, &params);
235
+  engine.runEvent(this, this, DSMCondition::Key, &params);
236 236
 }
237 237
 
238 238
 void DSMCall::onBye(const AmSipRequest& req)
... ...
@@ -241,13 +241,13 @@ void DSMCall::onBye(const AmSipRequest& req)
241 241
   map<string, string> params;
242 242
   params["headers"] = req.hdrs;
243 243
  
244
-  engine.runEvent(this, DSMCondition::Hangup, &params);
244
+  engine.runEvent(this, this, DSMCondition::Hangup, &params);
245 245
 }
246 246
 
247 247
 void DSMCall::onCancel() {
248 248
   DBG("onCancel\n");
249 249
   if (dlg.getStatus() < AmSipDialog::Connected) 
250
-    engine.runEvent(this, DSMCondition::Hangup, NULL);
250
+    engine.runEvent(this, this, DSMCondition::Hangup, NULL);
251 251
   else {
252 252
     DBG("ignoring onCancel event in established dialog\n");
253 253
   }
... ...
@@ -272,7 +272,7 @@ void DSMCall::onSipRequest(const AmSipRequest& req) {
272 272
     DSMSipRequest* sip_req = new DSMSipRequest(&req);
273 273
     avar[DSM_AVAR_REQUEST] = AmArg(sip_req);
274 274
     
275
-    engine.runEvent(this, DSMCondition::SipRequest, &params);
275
+    engine.runEvent(this, this, DSMCondition::SipRequest, &params);
276 276
 
277 277
     delete sip_req;
278 278
     avar.erase(DSM_AVAR_REQUEST);
... ...
@@ -306,7 +306,7 @@ void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
306 306
     DSMSipReply* dsm_reply = new DSMSipReply(&reply);
307 307
     avar[DSM_AVAR_REPLY] = AmArg(dsm_reply);
308 308
     
309
-    engine.runEvent(this, DSMCondition::SipReply, &params);
309
+    engine.runEvent(this, this, DSMCondition::SipReply, &params);
310 310
 
311 311
     delete dsm_reply;
312 312
     avar.erase(DSM_AVAR_REPLY);
... ...
@@ -327,7 +327,7 @@ void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status) {
327 327
     map<string, string> params;
328 328
     params["code"] = int2str(reply.code);
329 329
     params["reason"] = reply.reason;
330
-    engine.runEvent(this, DSMCondition::FailedCall, &params);    
330
+    engine.runEvent(this, this, DSMCondition::FailedCall, &params);
331 331
     setStopped();
332 332
   }
333 333
 }
... ...
@@ -338,10 +338,9 @@ void DSMCall::process(AmEvent* event)
338 338
   if (event->event_id == DSM_EVENT_ID) {
339 339
     DSMEvent* dsm_event = dynamic_cast<DSMEvent*>(event);
340 340
     if (dsm_event) {      
341
-      engine.runEvent(this, DSMCondition::DSMEvent, &dsm_event->params);
341
+      engine.runEvent(this, this, DSMCondition::DSMEvent, &dsm_event->params);
342 342
       return;
343
-    }
344
-  
343
+    }  
345 344
   }
346 345
 
347 346
   AmAudioEvent* audio_event = dynamic_cast<AmAudioEvent*>(event);
... ...
@@ -350,7 +349,7 @@ void DSMCall::process(AmEvent* event)
350 349
       (audio_event->event_id == AmAudioEvent::noAudio))){
351 350
     map<string, string> params;
352 351
     params["type"] = audio_event->event_id == AmAudioEvent::cleared?"cleared":"noAudio";
353
-    engine.runEvent(this, DSMCondition::NoAudio, &params);
352
+    engine.runEvent(this, this, DSMCondition::NoAudio, &params);
354 353
     return;
355 354
   }
356 355
 
... ...
@@ -359,14 +358,14 @@ void DSMCall::process(AmEvent* event)
359 358
     int timer_id = plugin_event->data.get(0).asInt();
360 359
     map<string, string> params;
361 360
     params["id"] = int2str(timer_id);
362
-    engine.runEvent(this, DSMCondition::Timer, &params);
361
+    engine.runEvent(this, this, DSMCondition::Timer, &params);
363 362
   }
364 363
 
365 364
   AmPlaylistSeparatorEvent* sep_ev = dynamic_cast<AmPlaylistSeparatorEvent*>(event);
366 365
   if (sep_ev) {
367 366
     map<string, string> params;
368 367
     params["id"] = int2str(sep_ev->event_id);
369
-    engine.runEvent(this, DSMCondition::PlaylistSeparator, &params);
368
+    engine.runEvent(this, this, DSMCondition::PlaylistSeparator, &params);
370 369
   }
371 370
 
372 371
   // todo: give modules the possibility to define/process events
... ...
@@ -386,12 +385,17 @@ void DSMCall::process(AmEvent* event)
386 385
       // decode result for easy use from script
387 386
       varPrintArg(resp_ev->response.data, params, resp_ev->response.is_error ? "error": "result");
388 387
 
389
-      // save reference to full parameters
390
-      avar[DSM_AVAR_JSONRPCRESPONEDATA] = AmArg(&resp_ev->response.data);
388
+      // decode udata for easy use from script
389
+      varPrintArg(resp_ev->udata, params, "udata");
390
+
391
+      // save reference to full parameters as avar
392
+      avar[DSM_AVAR_JSONRPCRESPONSEDATA] = AmArg(&resp_ev->response.data);
393
+      avar[DSM_AVAR_JSONRPCRESPONSEUDATA] = AmArg(&resp_ev->udata);
391 394
 
392
-      engine.runEvent(this, DSMCondition::JsonRpcResponse, &params);
395
+      engine.runEvent(this, this, DSMCondition::JsonRpcResponse, &params);
393 396
 
394
-      avar.erase(DSM_AVAR_JSONRPCRESPONEDATA);
397
+      avar.erase(DSM_AVAR_JSONRPCRESPONSEUDATA);
398
+      avar.erase(DSM_AVAR_JSONRPCRESPONSEDATA);
395 399
       return;
396 400
     }
397 401
 
... ...
@@ -412,7 +416,7 @@ void DSMCall::process(AmEvent* event)
412 416
       // save reference to full parameters
413 417
       avar[DSM_AVAR_JSONRPCREQUESTDATA] = AmArg(&req_ev->params);
414 418
 
415
-      engine.runEvent(this, DSMCondition::JsonRpcRequest, &params);
419
+      engine.runEvent(this, this, DSMCondition::JsonRpcRequest, &params);
416 420
 
417 421
       avar.erase(DSM_AVAR_JSONRPCREQUESTDATA);
418 422
       return;
... ...
@@ -613,7 +617,7 @@ void DSMCall::onOtherBye(const AmSipRequest& req) {
613 617
 
614 618
   map<string, string> params;
615 619
   params["hdrs"] = req.hdrs; // todo: optimization - make this configurable
616
-  engine.runEvent(this, DSMCondition::B2BOtherBye, &params);
620
+  engine.runEvent(this, this, DSMCondition::B2BOtherBye, &params);
617 621
 }
618 622
 
619 623
 bool DSMCall::onOtherReply(const AmSipReply& reply) {
... ...
@@ -625,7 +629,7 @@ bool DSMCall::onOtherReply(const AmSipReply& reply) {
625 629
   params["reason"] = reply.reason;
626 630
   params["hdrs"] = reply.hdrs; // todo: optimization - make this configurable
627 631
 
628
-  engine.runEvent(this, DSMCondition::B2BOtherReply, &params);
632
+  engine.runEvent(this, this, DSMCondition::B2BOtherReply, &params);
629 633
 
630 634
   return false;
631 635
 }
... ...
@@ -31,6 +31,8 @@
31 31
 #include "AmSession.h"
32 32
 #include "AmSessionContainer.h"
33 33
 #include "AmUtils.h"
34
+#include "AmEventDispatcher.h"
35
+#include "DSM.h"
34 36
 
35 37
 #include "jsonArg.h"
36 38
 
... ...
@@ -69,6 +71,8 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
69 71
   DEF_CMD("unmute", SCUnmuteAction);
70 72
   DEF_CMD("enableDTMFDetection", SCEnableDTMFDetection);
71 73
   DEF_CMD("disableDTMFDetection", SCDisableDTMFDetection);
74
+  DEF_CMD("sendDTMF", SCSendDTMFAction);
75
+  DEF_CMD("sendDTMFSequence", SCSendDTMFSequenceAction);
72 76
 
73 77
   DEF_CMD("set", SCSetAction);
74 78
   DEF_CMD("sets", SCSetSAction);
... ...
@@ -80,6 +84,7 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
80 84
   DEF_CMD("inc", SCIncAction);
81 85
   DEF_CMD("log", SCLogAction);
82 86
   DEF_CMD("clear", SCClearAction);
87
+  DEF_CMD("clearArray", SCClearArrayAction);
83 88
   DEF_CMD("logVars", SCLogVarsAction);
84 89
   DEF_CMD("logParams", SCLogParamsAction);
85 90
   DEF_CMD("logSelects", SCLogSelectsAction);
... ...
@@ -93,6 +98,10 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
93 98
 
94 99
   DEF_CMD("postEvent", SCPostEventAction);
95 100
 
101
+  DEF_CMD("registerEventQueue", SCRegisterEventQueueAction);
102
+  DEF_CMD("unregisterEventQueue", SCUnregisterEventQueueAction);
103
+  DEF_CMD("createSystemDSM", SCCreateSystemDSMAction);
104
+
96 105
   if (cmd == "DI") {
97 106
     SCDIAction * a = new SCDIAction(params, false);
98 107
     a->name = from_str;
... ...
@@ -131,22 +140,22 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
131 140
   if (cmd == "test")
132 141
     return new TestDSMCondition(params, DSMCondition::Any);
133 142
 
134
-  if (cmd == "keyTest") 
143
+  if ((cmd == "keyTest") || (cmd == "key"))
135 144
     return new TestDSMCondition(params, DSMCondition::Key);
136 145
 
137
-  if (cmd == "timerTest") 
146
+  if ((cmd == "timerTest") || (cmd == "timer"))
138 147
     return new TestDSMCondition(params, DSMCondition::Timer);
139 148
 
140
-  if (cmd == "noAudioTest") 
149
+  if ((cmd == "noAudioTest") || (cmd == "noAudio"))
141 150
     return new TestDSMCondition(params, DSMCondition::NoAudio);
142 151
 
143
-  if (cmd == "separatorTest") 
152
+  if ((cmd == "separatorTest") || (cmd == "separator"))
144 153
     return new TestDSMCondition(params, DSMCondition::PlaylistSeparator);
145 154
 
146 155
   if (cmd == "hangup") 
147 156
     return new TestDSMCondition(params, DSMCondition::Hangup);  
148 157
 
149
-  if (cmd == "eventTest") 
158
+  if ((cmd == "eventTest") || (cmd == "event"))
150 159
     return new TestDSMCondition(params, DSMCondition::DSMEvent);  
151 160
 
152 161
   if (cmd == "invite") 
... ...
@@ -182,6 +191,15 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) {
182 191
   if (cmd == "jsonRpcResponse") 
183 192
     return new TestDSMCondition(params, DSMCondition::JsonRpcResponse);  
184 193
 
194
+  if (cmd == "startup")
195
+    return new TestDSMCondition(params, DSMCondition::Startup);
196
+
197
+  if (cmd == "reload")
198
+    return new TestDSMCondition(params, DSMCondition::Reload);
199
+
200
+  if (cmd == "system")
201
+    return new TestDSMCondition(params, DSMCondition::System);
202
+
185 203
   return NULL;
186 204
 }
187 205
 
... ...
@@ -211,8 +229,12 @@ EXEC_ACTION_START(SCPostEventAction){
211 229
   if (!var.empty()) {
212 230
     if (var == "var")
213 231
       ev->params = sc_sess->var;
214
-    else 
215
-      ev->params[var] = sc_sess->var[var];
232
+    else {
233
+      vector<string> vars = explode(var, ";");
234
+      for (vector<string>::iterator it =
235
+	     vars.begin(); it != vars.end(); it++)
236
+	ev->params[*it] = sc_sess->var[*it];
237
+    }
216 238
   }
217 239
 
218 240
   DBG("posting event to session '%s'\n", sess_id.c_str());
... ...
@@ -347,7 +369,7 @@ EXEC_ACTION_START(SCStopAction) {
347 369
 
348 370
 #define DEF_SCModActionExec(clsname)				\
349 371
 								\
350
-  bool clsname::execute(AmSession* sess,			\
372
+  bool clsname::execute(AmSession* sess, DSMSession* sc_sess,	\
351 373
 			DSMCondition::EventType event,		\
352 374
 			map<string,string>* event_params) {	\
353 375
     return true;						\
... ...
@@ -613,6 +635,25 @@ EXEC_ACTION_START(SCClearAction) {
613 635
   sc_sess->var.erase(var_name);
614 636
 } EXEC_ACTION_END;
615 637
 
638
+EXEC_ACTION_START(SCClearArrayAction) {
639
+  string varprefix = (arg.length() && arg[0] == '$')?
640
+    arg.substr(1) : arg;
641
+  DBG("clear variable array '%s.*'\n", varprefix.c_str());
642
+
643
+  varprefix+=".";
644
+
645
+  map<string, string>::iterator lb = sc_sess->var.lower_bound(varprefix);
646
+  while (lb != sc_sess->var.end()) {
647
+    if ((lb->first.length() < varprefix.length()) ||
648
+	strncmp(lb->first.c_str(), varprefix.c_str(),varprefix.length()))
649
+      break;
650
+    map<string, string>::iterator lb_d = lb;
651
+    lb++;
652
+    sc_sess->var.erase(lb_d);    
653
+  }
654
+
655
+} EXEC_ACTION_END;
656
+
616 657
 
617 658
 CONST_ACTION_2P(SCAppendAction,',', false);
618 659
 EXEC_ACTION_START(SCAppendAction) {
... ...
@@ -838,7 +879,7 @@ TestDSMCondition::TestDSMCondition(const string& expr, DSMCondition::EventType e
838 879
   name = expr;
839 880
 }
840 881
 
841
-bool TestDSMCondition::match(AmSession* sess, DSMCondition::EventType event,
882
+bool TestDSMCondition::match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
842 883
 			  map<string,string>* event_params) {
843 884
   if (ttype == None || (type != DSMCondition::Any && type != event))
844 885
     return false;
... ...
@@ -846,7 +887,6 @@ bool TestDSMCondition::match(AmSession* sess, DSMCondition::EventType event,
846 887
   if (ttype == Always)
847 888
     return true;
848 889
 
849
-  DSMSession* sc_sess = dynamic_cast<DSMSession*>(sess);
850 890
   if (!sc_sess) {
851 891
     ERROR("wrong session type\n");
852 892
     return false;
... ...
@@ -1160,3 +1200,100 @@ EXEC_ACTION_START(SCB2BClearHeadersAction) {
1160 1200
   DBG("clearing B2B headers\n");
1161 1201
   sc_sess->B2BclearHeaders();
1162 1202
 } EXEC_ACTION_END;
1203
+
1204
+CONST_ACTION_2P(SCSendDTMFAction,',', true);
1205
+EXEC_ACTION_START(SCSendDTMFAction) {
1206
+  string event = resolveVars(par1, sess, sc_sess, event_params);
1207
+  string duration = resolveVars(par2, sess, sc_sess, event_params);  
1208
+  
1209
+  unsigned int event_i;
1210
+  if (str2i(event, event_i)) {
1211
+    ERROR("event '%s' not a valid DTMF event\n", event.c_str());
1212
+    throw DSMException("core", "cause", "invalid DTMF:"+ event);
1213
+  }
1214
+
1215
+  unsigned int duration_i;
1216
+  if (duration.empty()) {
1217
+    duration_i = 500; // default
1218
+  } else {
1219
+    if (str2i(duration, duration_i)) {
1220
+      ERROR("event duration '%s' not a valid DTMF duration\n", duration.c_str());
1221
+      throw DSMException("core", "cause", "invalid DTMF duration:"+ duration);
1222
+    }
1223
+  }
1224
+
1225
+  sess->sendDtmf(event_i, duration_i);
1226
+} EXEC_ACTION_END;
1227
+
1228
+CONST_ACTION_2P(SCSendDTMFSequenceAction,',', true);
1229
+EXEC_ACTION_START(SCSendDTMFSequenceAction) {
1230
+  string events = resolveVars(par1, sess, sc_sess, event_params);
1231
+  string duration = resolveVars(par2, sess, sc_sess, event_params);
1232
+
1233
+  unsigned int duration_i;
1234
+  if (duration.empty()) {
1235
+    duration_i = 500; // default
1236
+  } else {
1237
+    if (str2i(duration, duration_i)) {
1238
+      ERROR("event duration '%s' not a valid DTMF duration\n", duration.c_str());
1239
+      throw DSMException("core", "cause", "invalid DTMF duration:"+ duration);
1240
+    }
1241
+  }
1242
+
1243
+  for (size_t i=0;i<events.length();i++) {
1244
+    if ((events[i]<'0' || events[i]>'9')
1245
+	&& (events[i] != '#') && (events[i] != '*')
1246
+	&& (events[i] <'A' || events[i] >'F')) {
1247
+	DBG("skipping non-DTMF event char '%c'\n", events[i]);
1248
+	continue;
1249
+    }
1250
+    int event = events[i] - '0';
1251
+    if (events[i] == '*')
1252
+      event = 10;
1253
+    else if (events[i] == '#')
1254
+      event = 11;
1255
+    else if (events[i] >= 'A' && events[i] <= 'F' )
1256
+      event = 12 + (events[i] - 'A');
1257
+    DBG("sending event %d duration %u\n", event, duration_i);
1258
+    sess->sendDtmf(event, duration_i);
1259
+  }
1260
+} EXEC_ACTION_END;
1261
+
1262
+EXEC_ACTION_START(SCRegisterEventQueueAction) {
1263
+  string q_name = resolveVars(arg, sess, sc_sess, event_params);
1264
+  DBG("Registering event queue '%s'\n", q_name.c_str());
1265
+  if (q_name.empty()) {
1266
+    WARN("Registering empty event queue name!\n");
1267
+  }
1268
+  AmEventDispatcher::instance()->addEventQueue(q_name, sess);
1269
+} EXEC_ACTION_END;
1270
+
1271
+EXEC_ACTION_START(SCUnregisterEventQueueAction) {
1272
+  string q_name = resolveVars(arg, sess, sc_sess, event_params);
1273
+  DBG("Unregistering event queue '%s'\n", q_name.c_str());
1274
+  if (q_name.empty()) {
1275
+    WARN("Unregistering empty event queue name!\n");
1276
+  }
1277
+  AmEventDispatcher::instance()->delEventQueue(q_name);
1278
+} EXEC_ACTION_END;
1279
+
1280
+CONST_ACTION_2P(SCCreateSystemDSMAction,',', false);
1281
+EXEC_ACTION_START(SCCreateSystemDSMAction) {
1282
+  string conf_name = resolveVars(par1, sess, sc_sess, event_params);
1283
+  string script_name = resolveVars(par2, sess, sc_sess, event_params);
1284
+
1285
+  if (conf_name.empty() || script_name.empty()) {
1286
+    throw DSMException("core", "cause", "parameters missing - "
1287
+		       "need both conf_name and script_name for createSystemDSM");
1288
+  }
1289
+
1290
+  DBG("creating system DSM conf_name %s, script_name %s\n", 
1291
+      conf_name.c_str(), script_name.c_str());
1292
+  string status;
1293
+  if (!DSMFactory::instance()->createSystemDSM(conf_name, script_name, false, status)) {
1294
+    ERROR("creating system DSM: %s\n", status.c_str());
1295
+    throw DSMException("core", "cause", status);
1296
+  }
1297
+  
1298
+} EXEC_ACTION_END;
1299
+
... ...
@@ -62,6 +62,8 @@ DEF_ACTION_1P(SCMuteAction);
62 62
 DEF_ACTION_1P(SCUnmuteAction);
63 63
 DEF_ACTION_1P(SCEnableDTMFDetection);
64 64
 DEF_ACTION_1P(SCDisableDTMFDetection);
65
+DEF_ACTION_2P(SCSendDTMFAction);
66
+DEF_ACTION_2P(SCSendDTMFSequenceAction);
65 67
 
66 68
 DEF_ACTION_1P(SCSetPromptsAction);
67 69
 DEF_ACTION_2P(SCAddSeparatorAction);
... ...
@@ -81,6 +83,7 @@ DEF_ACTION_2P(SCAppendAction);
81 83
 DEF_ACTION_2P(SCSubStrAction);
82 84
 DEF_ACTION_1P(SCIncAction);
83 85
 DEF_ACTION_1P(SCClearAction);
86
+DEF_ACTION_1P(SCClearArrayAction);
84 87
 DEF_ACTION_2P(SCSetTimerAction);
85 88
 DEF_ACTION_1P(SCRemoveTimerAction);
86 89
 DEF_ACTION_1P(SCRemoveTimersAction);
... ...
@@ -103,13 +106,18 @@ DEF_ACTION_1P(SCB2BAddHeaderAction);
103 106
 DEF_ACTION_1P(SCB2BClearHeadersAction);
104 107
 DEF_ACTION_2P(SCB2BSetHeadersAction);
105 108
 
109
+DEF_ACTION_1P(SCRegisterEventQueueAction);
110
+DEF_ACTION_1P(SCUnregisterEventQueueAction);
111
+
112
+DEF_ACTION_2P(SCCreateSystemDSMAction);
113
+
106 114
 class SCDIAction					
107 115
 : public DSMAction {
108 116
   vector<string> params;
109 117
   bool get_res;
110 118
  public:
111 119
   SCDIAction(const string& arg, bool get_res);
112
-  bool execute(AmSession* sess,
120
+  bool execute(AmSession* sess, DSMSession* sc_sess,
113 121
 	       DSMCondition::EventType event,
114 122
 	       map<string,string>* event_params);
115 123
 };									
... ...
@@ -131,7 +139,7 @@ class TestDSMCondition
131 139
 
132 140
  public:
133 141
   TestDSMCondition(const string& expr, DSMCondition::EventType e);
134
-  bool match(AmSession* sess, DSMCondition::EventType event,
142
+  bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
135 143
 	     map<string,string>* event_params);
136 144
 };
137 145
 
... ...
@@ -105,9 +105,12 @@ string resolveVars(const string ts, AmSession* sess,
105 105
       return "";
106 106
     }
107 107
     case '#': 
108
-      if (event_params) 
109
-	return  (*event_params)[s.substr(1)];
110
-      else 
108
+      if (event_params) {
109
+	map<string, string>::iterator it = event_params->find(s.substr(1));
110
+	if (it != event_params->end())
111
+	  return it->second;
112
+	return  "";
113
+      }else 
111 114
 	return string();
112 115
     case '@': {
113 116
       string s1 = s.substr(1); 
... ...
@@ -96,7 +96,7 @@ class SCStrArgAction
96 96
   : public SCStrArgAction {						\
97 97
   public:								\
98 98
   CL_Name(const string& arg) : SCStrArgAction(arg) { }			\
99
-    bool execute(AmSession* sess,					\
99
+    bool execute(AmSession* sess, DSMSession* sc_sess,			\
100 100
 		 DSMCondition::EventType event,				\
101 101
 		 map<string,string>* event_params);			\
102 102
   };									\
... ...
@@ -107,7 +107,7 @@ class SCStrArgAction
107 107
   : public SCStrArgAction {						\
108 108
   public:								\
109 109
   CL_Name(const string& arg) : SCStrArgAction(arg) { }			\
110
-    bool execute(AmSession* sess,					\
110
+    bool execute(AmSession* sess, DSMSession* sc_sess,			\
111 111
 		 DSMCondition::EventType event,				\
112 112
 		 map<string,string>* event_params);			\
113 113
     SEAction getSEAction(std::string&);					\
... ...
@@ -120,7 +120,7 @@ class SCStrArgAction
120 120
     string par2;							\
121 121
   public:								\
122 122
     CL_Name(const string& arg);						\
123
-    bool execute(AmSession* sess,					\
123
+    bool execute(AmSession* sess, DSMSession* sc_sess,			\
124 124
 		 DSMCondition::EventType event,				\
125 125
 		 map<string,string>* event_params);			\
126 126
   };									\
... ...
@@ -195,25 +195,17 @@ class SCStrArgAction
195 195
   }									\
196 196
   
197 197
 
198
-#define GET_SCSESSION()					       \
199
-  DSMSession* sc_sess = dynamic_cast<DSMSession*>(sess);       \
200
-  if (!sc_sess) {					       \
201
-    ERROR("wrong session type\n");			       \
202
-    return false;					       \
203
-  }
204
-
205
-
206 198
 #define EXEC_ACTION_START(act_name)					\
207
-  bool act_name::execute(AmSession* sess,				\
199
+  bool act_name::execute(AmSession* sess, DSMSession* sc_sess,		\
208 200
 			 DSMCondition::EventType event,			\
209
-			 map<string,string>* event_params) {		\
210
-  GET_SCSESSION();							
201
+			 map<string,string>* event_params) {
202
+
211 203
 
212 204
 #define EXEC_ACTION_END				\
213 205
   return false;					\
214 206
   }
215 207
 
216
-#define EXEC_ACTION_STOP				\
208
+#define EXEC_ACTION_STOP			\
217 209
   return false;
218 210
 
219 211
 string resolveVars(const string s, AmSession* sess,
... ...
@@ -241,39 +233,19 @@ void splitCmd(const string& from_str,
241 233
     						\
242 234
   public:					\
243 235
     						\
244
-  cond_name(const string& arg, bool inv)			\
245
-    : arg(arg), inv(inv) { }					\
246
-    bool match(AmSession* sess, DSMCondition::EventType event,	\
247
-	       map<string,string>* event_params);		\
248
-  };								\
236
+  cond_name(const string& arg, bool inv)				\
237
+    : arg(arg), inv(inv) { }						\
238
+    bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \
239
+	       map<string,string>* event_params);			\
240
+  };
249 241
   
250 242
 
251 243
 #define MATCH_CONDITION_START(cond_clsname)				\
252
-  bool cond_clsname::match(AmSession* sess, DSMCondition::EventType event, \
253
-			   map<string,string>* event_params) {		\
254
-  GET_SCSESSION();
255
-
256
-#define MATCH_CONDITION_END }			
257
-
258
-
259
-#define GET_SCSESSION()					       \
260
-  DSMSession* sc_sess = dynamic_cast<DSMSession*>(sess);       \
261
-  if (!sc_sess) {					       \
262
-    ERROR("wrong session type\n");			       \
263
-    return false;					       \
264
-  }
265
-
266
-
267
-#define EXEC_ACTION_START(act_name)					\
268
-  bool act_name::execute(AmSession* sess,				\
269
-			 DSMCondition::EventType event,			\
270
-			 map<string,string>* event_params) {		\
271
-    GET_SCSESSION();							
272
-
273
-#define EXEC_ACTION_END				\
274
-  return false;					\
275
-  }
244
+  bool cond_clsname::match(AmSession* sess, DSMSession* sc_sess,	\
245
+			   DSMCondition::EventType event,		\
246
+			   map<string,string>* event_params) {
276 247
 
248
+#define MATCH_CONDITION_END }
277 249
 
278 250
 #define DECLARE_MODULE(mod_cls_name)			\
279 251
   class mod_cls_name					\
... ...
@@ -63,7 +63,8 @@ using std::map;
63 63
 #define DSM_AVAR_REPLY   "reply"
64 64
 
65 65
 #define DSM_AVAR_JSONRPCREQUESTDATA "JsonRpcRequestParameters"
66
-#define DSM_AVAR_JSONRPCRESPONEDATA "JsonRpcResponseParameters"
66
+#define DSM_AVAR_JSONRPCRESPONSEDATA "JsonRpcResponseParameters"
67
+#define DSM_AVAR_JSONRPCRESPONSEUDATA "JsonRpcResponseUData"
67 68
 
68 69
 #define DSM_ERRNO_FILE        "file"
69 70
 #define DSM_ERRNO_UNKNOWN_ARG "arg"
... ...
@@ -37,7 +37,7 @@ DSMStateDiagramCollection::~DSMStateDiagramCollection() {
37 37
 }
38 38
 
39 39
 bool DSMStateDiagramCollection::loadFile(const string& filename, const string& name, 
40
-					 const string& mod_path, bool debug_dsm) {
40
+					 const string& mod_path, bool debug_dsm, bool check_dsm) {
41 41
   DBG("loading DSM '%s' from '%s'\n", name.c_str(), filename.c_str());
42 42
 
43 43
   DSMChartReader cr;
... ...
@@ -70,6 +70,19 @@ bool DSMStateDiagramCollection::loadFile(const string& filename, const string& n
70 70
     ERROR("DonkeySM decode script error!\n");
71 71
     return false;
72 72
   }
73
+  if (check_dsm) {
74
+    string report;
75
+    if (!diags.back().checkConsistency(report)) {
76
+      WARN("consistency check failed on '%s' from file '%s':\n", 
77
+	   name.c_str(), filename.c_str());
78
+      WARN("------------------------------------------\n"
79
+	   "%s\n"
80
+	   "------------------------------------------\n", report.c_str());
81
+    } else {
82
+      DBG("DSM '%s' passed consistency check\n", name.c_str());
83
+    }
84
+  }
85
+
73 86
   return true;
74 87
 }
75 88
 
... ...
@@ -44,7 +44,7 @@ class DSMStateDiagramCollection
44 44
   ~DSMStateDiagramCollection();
45 45
 
46 46
   bool loadFile(const string& filename, const string& name, 
47
-		const string& mod_path, bool debug_dsm);
47
+		const string& mod_path, bool debug_dsm, bool check_dsm);
48 48
   void addToEngine(DSMStateEngine* e);
49 49
   bool hasDiagram(const string& name);
50 50
   vector<string> getDiagramNames();
... ...
@@ -139,6 +139,70 @@ State* DSMStateDiagram::getInitialState() {
139 139
 }
140 140
 
141 141
 
142
+bool DSMStateDiagram::checkConsistency(string& report) {
143
+  bool res = true;
144
+  DBG("checking consistency of '%s'\n", name.c_str());
145
+  res &= checkInitialState(report);
146
+  res &= checkDestinationStates(report);
147
+  res &= checkHangupHandled(report);
148
+  return res;
149
+}
150
+
151
+bool DSMStateDiagram::checkInitialState(string& report) {
152
+  DBG("checking for initial state...\n");
153
+  if (NULL == getInitialState()) {
154
+    report+=name+": " "No initial state defined!\n";
155
+    return false;
156
+  }
157
+  return true;
158
+}
159
+bool DSMStateDiagram::checkDestinationStates(string& report) {
160
+  DBG("checking for existence of destination states...\n");
161
+  bool res = true;
162
+  for (vector<State>::iterator it=
163
+	 states.begin(); it != states.end(); it++) {
164
+    for (vector<DSMTransition>::iterator t_it=
165
+	   it->transitions.begin(); t_it != it->transitions.end(); t_it++) {
166
+      if (NULL == getState(t_it->to_state)) {
167
+	report += name+": State '"+it->name+"' Transition '"+t_it->name+
168
+	  "' : Destination state '"+ t_it->to_state +"' is not defined\n";
169
+	res = false;
170
+      }
171
+    }
172
+  }
173
+  return res;
174
+}
175
+
176
+bool DSMStateDiagram::checkHangupHandled(string& report) {
177
+  DBG("checking for hangup handled in all states...\n");
178
+  bool res = true;
179
+  for (vector<State>::iterator it=
180
+	 states.begin(); it != states.end(); it++) {
181
+    bool have_hangup_trans = false;
182
+    for (vector<DSMTransition>::iterator t_it=
183
+	   it->transitions.begin(); t_it != it->transitions.end(); t_it++) {
184
+      for (vector<DSMCondition*>::iterator c_it=
185
+	     t_it->precond.begin(); c_it!=t_it->precond.end(); c_it++) {
186
+	if ((*c_it)->type == DSMCondition::Hangup) {
187
+	  // todo: what if other conditions unmet?
188
+	  have_hangup_trans = true;
189
+	  break;
190
+	}
191
+      }
192
+      if (have_hangup_trans)
193
+	break;
194
+
195
+    }
196
+    if (!have_hangup_trans) {
197
+      report += name+": State '"+it->name+"': hangup is not handled\n";
198
+      res = false;
199
+    }
200
+  }
201
+
202
+  return res;
203
+}
204
+
205
+
142 206
 DSMStateEngine::DSMStateEngine() 
143 207
   : current(NULL) {
144 208
 }
... ...
@@ -157,12 +221,12 @@ bool DSMStateEngine::onInvite(const AmSipRequest& req, DSMSession* sess) {
157 221
 
158 222
 bool DSMStateEngine::runactions(vector<DSMAction*>::iterator from, 
159 223
 				vector<DSMAction*>::iterator to, 
160
-				AmSession* sess,  DSMCondition::EventType event,
224
+				AmSession* sess,  DSMSession* sc_sess, DSMCondition::EventType event,
161 225
 				map<string,string>* event_params,  bool& is_consumed) {
162 226
 //   DBG("running %zd actions\n", to - from);
163 227
   for (vector<DSMAction*>::iterator it=from; it != to; it++) {
164 228
     DBG("executing '%s'\n", (*it)->name.c_str()); 
165
-    if ((*it)->execute(sess, event, event_params)) {
229
+    if ((*it)->execute(sess, sc_sess, event, event_params)) {
166 230
       string se_modifier;
167 231
       switch ((*it)->getSEAction(se_modifier)) {
168 232
       case DSMAction::Repost: 
... ...
@@ -170,17 +234,17 @@ bool DSMStateEngine::runactions(vector<DSMAction*>::iterator from,
170 234
 	break;
171 235
       case DSMAction::Jump: 
172 236
 	DBG("jumping %s\n", se_modifier.c_str());
173
-	if (jumpDiag(se_modifier, sess, event, event_params)) {
237
+	if (jumpDiag(se_modifier, sess, sc_sess, event, event_params)) {
174 238
 	  // is_consumed = false; 
175 239
 	  return true;  
176 240
 	} break;
177 241
       case DSMAction::Call: 
178
-	if (callDiag(se_modifier, sess, event, event_params))  {
242
+	if (callDiag(se_modifier, sess, sc_sess, event, event_params))  {
179 243
 	  // is_consumed = false; 
180 244
 	  return true;   
181 245
 	} break;
182 246
       case DSMAction::Return: 
183
-	if (returnDiag(sess)) {  
247
+	if (returnDiag(sess, sc_sess)) {
184 248
 	  //is_consumed = false;
185 249
 	  return true; 
186 250
 	} break;
... ...
@@ -202,11 +266,12 @@ void DSMStateEngine::addModules(vector<DSMModule*> modules) {
202 266
     mods.push_back(*it);
203 267
 }
204 268
 
205
-bool DSMStateEngine::init(AmSession* sess, const string& startDiagram, 
269
+bool DSMStateEngine::init(AmSession* sess, DSMSession* sc_sess,
270
+			  const string& startDiagram,
206 271
 			  DSMCondition::EventType init_event) {
207 272
 
208 273
   try {
209
-    if (!jumpDiag(startDiagram, sess, init_event, NULL)) {
274
+    if (!jumpDiag(startDiagram, sess, sc_sess, init_event, NULL)) {
210 275
       ERROR("initializing with start diag '%s'\n",
211 276
 	    startDiagram.c_str());
212 277
       return false;
... ...
@@ -214,26 +279,26 @@ bool DSMStateEngine::init(AmSession* sess, const string& startDiagram,
214 279
   } catch (DSMException& e) {
215 280
     DBG("Exception type '%s' occured while initializing! Run init event as exception...\n", 
216 281
 	e.params["type"].c_str());
217
-    runEvent(sess, init_event, &e.params, true);
282
+    runEvent(sess, sc_sess, init_event, &e.params, true);
218 283
     return true;
219 284
     
220 285
   }
221 286
 
222 287
   DBG("run init event...\n");
223
-  runEvent(sess, init_event, NULL);
288
+  runEvent(sess, sc_sess, init_event, NULL);
224 289
   return true;
225 290
 }
226 291
 
227
-bool DSMCondition::_match(AmSession* sess, 
228
-		      DSMCondition::EventType event,
229
-		      map<string,string>* event_params) {
292
+bool DSMCondition::_match(AmSession* sess, DSMSession* sc_sess,
293
+			  DSMCondition::EventType event,
294
+			  map<string,string>* event_params) {
230 295
   // or xor
231
-  return invert?(!match(sess,event,event_params)):match(sess,event,event_params);
296
+  return invert? (!match(sess,sc_sess,event,event_params)) : match(sess, sc_sess, event, event_params);
232 297
 }
233 298
 
234
-bool DSMCondition::match(AmSession* sess, 
235
-		      DSMCondition::EventType event,
236
-		      map<string,string>* event_params) {
299
+bool DSMCondition::match(AmSession* sess, DSMSession* sc_sess,
300
+			 DSMCondition::EventType event,
301
+			 map<string,string>* event_params) {
237 302
 
238 303
   if ((type != Any) && (event != type))
239 304
     return false;
... ...
@@ -252,7 +317,7 @@ bool DSMCondition::match(AmSession* sess,
252 317
   return true;
253 318
 }
254 319
 
255
-void DSMStateEngine::runEvent(AmSession* sess,
320
+void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess,
256 321
 			      DSMCondition::EventType event,
257 322
 			      map<string,string>* event_params,
258 323
 			      bool run_exception) {
... ...
@@ -278,7 +343,7 @@ void DSMStateEngine::runEvent(AmSession* sess,
278 343
 	
279 344
 	vector<DSMCondition*>::iterator con=tr->precond.begin();
280 345
 	while (con!=tr->precond.end()) {
281
-	  if (!(*con)->_match(sess, active_event, active_params))
346
+	  if (!(*con)->_match(sess, sc_sess, active_event, active_params))
282 347
 	    break;
283 348
 	  con++;
284 349
 	}
... ...
@@ -302,7 +367,7 @@ void DSMStateEngine::runEvent(AmSession* sess,
302 367
 		current->post_actions.size(), current->name.c_str());
303 368
 	    if (runactions(current->post_actions.begin(), 
304 369
 			   current->post_actions.end(), 
305
-			   sess, active_event, active_params, is_consumed)) {
370
+			   sess, sc_sess, active_event, active_params, is_consumed)) {
306 371
 	      break;
307 372
 	    }
308 373
 	  }
... ...
@@ -313,7 +378,7 @@ void DSMStateEngine::runEvent(AmSession* sess,
313 378
 		tr->actions.size(), tr->name.c_str());
314 379
 	    if (runactions(tr->actions.begin(), 
315 380
 			   tr->actions.end(), 
316
-			   sess, active_event, active_params, is_consumed)) {
381
+			   sess, sc_sess, active_event, active_params, is_consumed)) {
317 382
 	      break;
318 383
 	    }
319 384
 	  }
... ...
@@ -348,7 +413,7 @@ void DSMStateEngine::runEvent(AmSession* sess,
348 413
 		current->pre_actions.size(), current->name.c_str());
349 414
 	    if (runactions(current->pre_actions.begin(), 
350 415
 			   current->pre_actions.end(), 
351
-			   sess, active_event, active_params, is_consumed)) {
416
+			   sess, sc_sess, active_event, active_params, is_consumed)) {
352 417
 	      break;
353 418
 	    }
354 419
 	  }
... ...
@@ -369,7 +434,7 @@ void DSMStateEngine::runEvent(AmSession* sess,
369 434
   } while (!is_consumed);
370 435
 }
371 436
 
372
-bool DSMStateEngine::callDiag(const string& diag_name, AmSession* sess, 
437
+bool DSMStateEngine::callDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess,
373 438
 			   DSMCondition::EventType event,
374 439
 			   map<string,string>* event_params) {
375 440
   if (!current || !current_diag) {
... ...
@@ -377,10 +442,10 @@ bool DSMStateEngine::callDiag(const string& diag_name, AmSession* sess,
377 442
     return false;
378 443
   }
379 444
   stack.push_back(std::make_pair(current_diag, current));
380
-  return jumpDiag(diag_name, sess, event, event_params);
445
+  return jumpDiag(diag_name, sess, sc_sess, event, event_params);
381 446
 }
382 447
 
383
-bool DSMStateEngine::jumpDiag(const string& diag_name, AmSession* sess,
448
+bool DSMStateEngine::jumpDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess,
384 449
 			   DSMCondition::EventType event,
385 450
 			   map<string,string>* event_params) {
386 451
   for (vector<DSMStateDiagram*>::iterator it=
... ...
@@ -414,7 +479,7 @@ bool DSMStateEngine::jumpDiag(const string& diag_name, AmSession* sess,
414 479
       is_finished = true;
415 480
       runactions(current->pre_actions.begin(), 
416 481
 		 current->pre_actions.end(), 
417
-		 sess, event, event_params, is_finished); 
482
+		 sess, sc_sess, event, event_params, is_finished); 
418 483
 
419 484
       return true;
420 485
     }
... ...
@@ -423,7 +488,7 @@ bool DSMStateEngine::jumpDiag(const string& diag_name, AmSession* sess,
423 488
   return false;
424 489
 }
425 490
 
426
-bool DSMStateEngine::returnDiag(AmSession* sess) {
491
+bool DSMStateEngine::returnDiag(AmSession* sess, DSMSession* sc_sess) {
427 492
   if (stack.empty()) {
428 493
     ERROR("returning from empty stack\n");
429 494
     return false;
... ...
@@ -492,3 +557,4 @@ void varPrintArg(const AmArg& a, map<string, string>& dst, const string& name) {
492 557
   default: dst[name] = "<UNKONWN TYPE>"; return;
493 558
   }
494 559
 }
560
+
... ...
@@ -86,7 +86,11 @@ class DSMCondition
86 86
     XmlrpcResponse,
87 87
 
88 88
     JsonRpcResponse,
89
-    JsonRpcRequest
89
+    JsonRpcRequest,
90
+
91
+    Startup,
92
+    Reload,
93
+    System
90 94
   };
91 95
 
92 96
   bool invert; 
... ...
@@ -97,10 +101,10 @@ class DSMCondition
97 101
   EventType type;
98 102
   map<string, string> params;
99 103
 
100
-  bool _match(AmSession* sess, DSMCondition::EventType event,
104
+  bool _match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
101 105
 	      map<string,string>* event_params);
102 106
 
103
-  virtual bool match(AmSession* sess, DSMCondition::EventType event,
107
+  virtual bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
104 108
 		     map<string,string>* event_params);
105 109
 };
106 110
 
... ...
@@ -120,7 +124,7 @@ class DSMAction
120 124
   virtual ~DSMAction() { /* DBG("dest action\n"); */ }
121 125
 
122 126
   /** @return whether state engine is to be modified (via getSEAction) */
123
-  virtual bool execute(AmSession* sess, DSMCondition::EventType event,	\
127
+  virtual bool execute(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \
124 128
 		       map<string,string>* event_params) = 0;
125 129
 
126 130
   /** @return state engine modification */
... ...
@@ -161,6 +165,10 @@ class DSMStateDiagram  {
161 165
   string name;
162 166
   string initial_state;
163 167
 
168
+  bool checkInitialState(string& report);
169
+  bool checkDestinationStates(string& report);
170
+  bool checkHangupHandled(string& report);
171
+
164 172
  public:
165 173
   DSMStateDiagram(const string& name);
166 174
   ~DSMStateDiagram();
... ...
@@ -171,6 +179,7 @@ class DSMStateDiagram  {
171 179
   void addState(const State& state, bool is_initial = false);
172 180
   bool addTransition(const DSMTransition& trans);
173 181
   const string& getName() { return name; }
182
+  bool checkConsistency(string& report);
174 183
 };
175 184
 
176 185
 class DSMException {
... ...
@@ -205,14 +214,16 @@ class DSMStateEngine {
205 214
 
206 215
   vector<pair<DSMStateDiagram*, State*> > stack;
207 216
 
208
-  bool callDiag(const string& diag_name, AmSession* sess, DSMCondition::EventType event,
209
-			   map<string,string>* event_params);
210
-  bool jumpDiag(const string& diag_name, AmSession* sess, DSMCondition::EventType event,
211
-			   map<string,string>* event_params);
212
-  bool returnDiag(AmSession* sess);
217
+  bool callDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess, 
218
+		DSMCondition::EventType event,