1 /*
2  * Copyright (C) 2008 iptego GmbH
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 #include "DSM.h"
28 #include "AmConfig.h"
29 #include "AmUtils.h"
30 #include "AmPlugIn.h"
31 #include "log.h"
32 #include "AmConfigReader.h"
33 #include "AmSessionContainer.h"
34 #include "ampi/MonitoringAPI.h"
35 #include "AmUriParser.h"
36 #include "DSMCall.h"
37 #include "DSMChartReader.h"
38 #include "AmSipHeaders.h"
39 #include "AmEventDispatcher.h"
40 #include "SystemDSM.h"
41 
42 #include <string>
43 #include <fstream>
44 
45 #include <sys/types.h>
46 #include <dirent.h>
47 
48 #define MOD_NAME "dsm"
49 
plugin_class_create()50 extern "C" void* plugin_class_create()
51 {
52   return DSMFactory::instance();
53 }
54 
55 DSMFactory* DSMFactory::_instance=0;
56 
instance()57 DSMFactory* DSMFactory::instance()
58 {
59   if(_instance == NULL)
60     _instance = new DSMFactory(MOD_NAME);
61   return _instance;
62 }
63 
64 bool DSMFactory::DebugDSM;
65 string DSMFactory::InboundStartDiag;
66 string DSMFactory::OutboundStartDiag;
67 bool DSMFactory::CheckDSM;
68 
69 #ifdef USE_MONITORING
70 bool DSMFactory::MonitoringFullCallgraph;
71 bool DSMFactory::MonitoringFullTransitions;
72 
73 MonSelectType DSMFactory::MonSelectCaller;
74 MonSelectType DSMFactory::MonSelectCallee;
75 vector<string> DSMFactory::MonSelectFilters;
76 
77 string DSMFactory::MonSelectFallback;
78 
79 #endif // USE_MONITORING
80 
DSMFactory(const string & _app_name)81 DSMFactory::DSMFactory(const string& _app_name)
82   : AmSessionFactory(_app_name),AmDynInvokeFactory(_app_name),
83     loaded(false),
84     session_timer_f(NULL)
85 {
86   AmEventDispatcher::instance()->addEventQueue("dsm", this);
87 
88   MainScriptConfig.diags = new DSMStateDiagramCollection();
89 }
90 
postEvent(AmEvent * e)91 void DSMFactory::postEvent(AmEvent* e) {
92   AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(e);
93   if (sys_ev &&
94       sys_ev->sys_event == AmSystemEvent::ServerShutdown) {
95     DBG("stopping DSM...\n");
96     preload_reader.cleanup();
97     AmEventDispatcher::instance()->delEventQueue("dsm");
98     return;
99   }
100 
101   WARN("received unknown event\n");
102 }
103 
~DSMFactory()104 DSMFactory::~DSMFactory() {
105   for (map<string, AmPromptCollection*>::iterator it=
106 	 prompt_sets.begin(); it != prompt_sets.end(); it++)
107     delete it->second;
108 
109   for (std::set<DSMStateDiagramCollection*>::iterator it=
110 	 old_diags.begin(); it != old_diags.end(); it++)
111     delete *it;
112 
113   delete MainScriptConfig.diags;
114 }
115 
onLoad()116 int DSMFactory::onLoad()
117 {
118   if (loaded)
119     return 0;
120   loaded = true;
121 
122   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf")))
123     return -1;
124 
125   // get application specific global parameters
126   configureModule(cfg);
127 
128   DebugDSM = cfg.getParameter("debug_raw_dsm") == "yes";
129   CheckDSM = cfg.getParameter("dsm_consistency_check", "yes") == "yes";
130 
131   if (!loadPrompts(cfg))
132     return -1;
133 
134   if (!loadPromptSets(cfg))
135     return -1;
136 
137   if (!loadDiags(cfg, MainScriptConfig.diags))
138     return -1;
139 
140   vector<string> registered_apps;
141   if (!registerApps(cfg, MainScriptConfig.diags, registered_apps))
142     return -1;
143 
144   InboundStartDiag = cfg.getParameter("inbound_start_diag");
145   if (InboundStartDiag.empty()) {
146     INFO("no 'inbound_start_diag' set in config. "
147 	 "inbound calls with application 'dsm' disabled.\n");
148   }
149 
150   OutboundStartDiag = cfg.getParameter("outbound_start_diag");
151   if (OutboundStartDiag.empty()) {
152     INFO("no 'outbound_start_diag' set in config. "
153 	 "outbound calls with application 'dsm' disabled.\n");
154   }
155 
156   if (!InboundStartDiag.empty())
157     AmPlugIn::instance()->registerFactory4App("dsm",this);
158 
159   MainScriptConfig.config_vars.insert(cfg.begin(), cfg.end());
160 
161 //   for (std::map<string,string>::const_iterator it =
162 // 	 cfg.begin(); it != cfg.end(); it++)
163 //     MainScriptConfig.config_vars[it->first] = it->second;
164 
165   MainScriptConfig.RunInviteEvent = cfg.getParameter("run_invite_event")=="yes";
166 
167   MainScriptConfig.SetParamVariables = cfg.getParameter("set_param_variables")=="yes";
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 
180 #ifdef USE_MONITORING
181   string monitoring_full_callgraph = cfg.getParameter("monitoring_full_stategraph");
182   MonitoringFullCallgraph = monitoring_full_callgraph == "yes";
183   DBG("%sogging full call graph (states) to monitoring.\n",
184       MonitoringFullCallgraph?"L":"Not l");
185 
186   string monitoring_full_transitions = cfg.getParameter("monitoring_full_transitions");
187   MonitoringFullTransitions = monitoring_full_transitions == "yes";
188   DBG("%sogging full call graph (transitions) to monitoring.\n",
189       MonitoringFullTransitions?"L":"Not l");
190 
191   string cfg_usecaller = cfg.getParameter("monitor_select_use_caller");
192   if (cfg_usecaller.empty() || cfg_usecaller=="from")
193     MonSelectCaller = MonSelect_FROM;
194   else if (cfg_usecaller=="no")
195     MonSelectCaller = MonSelect_NONE;
196   else if (cfg_usecaller=="pai")
197     MonSelectCaller = MonSelect_PAI;
198   else {
199     ERROR("monitor_select_use_caller value '%s' not understood\n",
200 	  cfg_usecaller.c_str());
201   }
202 
203   string cfg_usecallee = cfg.getParameter("monitor_select_use_callee");
204   if (cfg_usecallee.empty() || cfg_usecallee=="ruri")
205     MonSelectCallee = MonSelect_RURI;
206   else if (cfg_usecallee=="no")
207     MonSelectCallee = MonSelect_NONE;
208   else if (cfg_usecallee=="from")
209     MonSelectCallee = MonSelect_FROM;
210   else {
211     ERROR("monitor_select_use_callee value '%s' not understood\n",
212 	  cfg_usecallee.c_str());
213   }
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   }
229   MonSelectFallback = cfg.getParameter("monitor_select_fallback");
230 #endif
231 
232   string conf_d_dir = cfg.getParameter("conf_dir");
233   if (!conf_d_dir.empty()) {
234     if (conf_d_dir[conf_d_dir.length()-1] != '/')
235       conf_d_dir += '/';
236 
237     DBG("processing configurations in '%s'...\n", conf_d_dir.c_str());
238     int err=0;
239     struct dirent* entry;
240     DIR* dir = opendir(conf_d_dir.c_str());
241 
242     if(!dir){
243       INFO("DSM config files loader (%s): %s\n",
244 	    conf_d_dir.c_str(), strerror(errno));
245     } else {
246       while( ((entry = readdir(dir)) != NULL) && (err == 0) ){
247 	string conf_name = string(entry->d_name);
248 
249 	if (conf_name.find(".conf",conf_name.length()-5) == string::npos){
250 	  continue;
251 	}
252 
253 	string conf_file_name = conf_d_dir + conf_name;
254 
255 	DBG("loading %s ...\n",conf_file_name.c_str());
256 	if (!loadConfig(conf_file_name, conf_name, false, NULL))
257 	  return -1;
258 
259       }
260       closedir(dir);
261     }
262   }
263 
264   if(cfg.hasParameter("enable_session_timer") &&
265      (cfg.getParameter("enable_session_timer") == string("yes")) ){
266     DBG("enabling session timers\n");
267     session_timer_f = AmPlugIn::instance()->getFactory4Seh("session_timer");
268     if(session_timer_f == NULL){
269       ERROR("Could not load the session_timer module: disabling session timers.\n");
270     }
271   }
272 
273   return 0;
274 }
275 
loadPrompts(AmConfigReader & cfg)276 bool DSMFactory::loadPrompts(AmConfigReader& cfg) {
277 
278   vector<string> prompts_files =
279     explode(cfg.getParameter("load_prompts"), ",");
280   for (vector<string>::iterator it=
281 	 prompts_files.begin(); it != prompts_files.end(); it++) {
282     DBG("loading prompts from '%s'\n", it->c_str());
283     std::ifstream ifs(it->c_str());
284     string s;
285     while (ifs.good() && !ifs.eof()) {
286       getline(ifs, s);
287       if (s.length() && s.find_first_not_of(" \t")!= string::npos &&
288 	  s[s.find_first_not_of(" \t")] != '#') {
289         vector<string> p=explode(s, "=");
290 	if (p.size()==2) {
291 	  prompts.setPrompt(p[0], p[1], MOD_NAME);
292 	  DBG("added prompt '%s' as '%s'\n",
293 	      p[0].c_str(), p[1].c_str());
294 	}
295       }
296     }
297   }
298 
299   bool has_all_prompts = true;
300   vector<string> required_prompts =
301     explode(cfg.getParameter("required_prompts"), ",");
302 
303   for (vector<string>::iterator it=required_prompts.begin();
304        it != required_prompts.end(); it++) {
305     if (!prompts.hasPrompt(*it)) {
306       ERROR("required prompt '%s' not loaded.\n",
307 	    it->c_str());
308       has_all_prompts = false;
309     }
310   }
311   if (!has_all_prompts)
312     return false;
313 
314   return true;
315 }
316 
loadPromptSets(AmConfigReader & cfg)317 bool DSMFactory::loadPromptSets(AmConfigReader& cfg) {
318   string prompt_sets_path = cfg.getParameter("prompts_sets_path");
319 
320   vector<string> prompt_sets_names =
321     explode(cfg.getParameter("load_prompts_sets"), ",");
322   for (vector<string>::iterator it=
323 	 prompt_sets_names.begin(); it != prompt_sets_names.end(); it++) {
324     string fname = prompt_sets_path.empty() ? "": prompt_sets_path + "/";
325     fname += *it;
326     DBG("loading prompts for '%s' (file '%s')\n", it->c_str(), fname.c_str());
327     std::ifstream ifs(fname.c_str());
328     string s;
329     if (!ifs.good()) {
330       WARN("prompts set file '%s' could not be read\n", fname.c_str());
331     }
332     AmPromptCollection* pc = new AmPromptCollection();
333     while (ifs.good() && !ifs.eof()) {
334       getline(ifs, s);
335       if (s.length() && s.find_first_not_of(" \t")!= string::npos &&
336 	  s[s.find_first_not_of(" \t")] != '#') {
337         vector<string> p=explode(s, "=");
338 	if (p.size()==2) {
339 	  pc->setPrompt(p[0], p[1], MOD_NAME);
340 	  DBG("set '%s' added prompt '%s' as '%s'\n",
341 	      it->c_str(), p[0].c_str(), p[1].c_str());
342 	}
343       }
344     }
345     prompt_sets[*it] = pc;
346   }
347   return true;
348 }
349 
loadDiags(AmConfigReader & cfg,DSMStateDiagramCollection * m_diags)350 bool DSMFactory::loadDiags(AmConfigReader& cfg, DSMStateDiagramCollection* m_diags) {
351   string DiagPath = cfg.getParameter("diag_path");
352   if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
353     DiagPath += '/';
354 
355   string ModPath = cfg.getParameter("mod_path");
356 
357   string err;
358   int res = preloadModules(cfg, err, ModPath);
359   if (res<0) {
360     ERROR("%s\n", err.c_str());
361     return false;
362   }
363 
364   // todo: pass preloaded mods to chart reader
365 
366   string LoadDiags = cfg.getParameter("load_diags");
367   vector<string> diags_names = explode(LoadDiags, ",");
368   for (vector<string>::iterator it=
369 	 diags_names.begin(); it != diags_names.end(); it++) {
370     if (!m_diags->loadFile(DiagPath+*it+".dsm", *it, DiagPath, ModPath, DebugDSM, CheckDSM)) {
371       ERROR("loading %s from %s\n",
372 	    it->c_str(), (DiagPath+*it+".dsm").c_str());
373       return false;
374     }
375   }
376 
377   return true;
378 }
379 
registerApps(AmConfigReader & cfg,DSMStateDiagramCollection * m_diags,vector<string> & register_names)380 bool DSMFactory::registerApps(AmConfigReader& cfg, DSMStateDiagramCollection* m_diags,
381 			      vector<string>& register_names) {
382   string RegisterDiags = cfg.getParameter("register_apps");
383   register_names = explode(RegisterDiags, ",");
384   for (vector<string>::iterator it=
385 	 register_names.begin(); it != register_names.end(); it++) {
386     if (m_diags->hasDiagram(*it)) {
387       bool res = AmPlugIn::instance()->registerFactory4App(*it,this);
388       if(res)
389 	INFO("DSM state machine registered: %s.\n",
390 	     it->c_str());
391     } else {
392       ERROR("trying to register application '%s' which is not loaded.\n",
393 	    it->c_str());
394       return false;
395     }
396   }
397   return true;
398 }
399 
400 // DI interface function
loadConfig(const AmArg & args,AmArg & ret)401 void DSMFactory::loadConfig(const AmArg& args, AmArg& ret) {
402   string file_name = args.get(0).asCStr();
403   string diag_name = args.get(1).asCStr();
404 
405   if (loadConfig(file_name, diag_name, true, NULL)) {
406     ret.push(200);
407     ret.push("OK");
408   } else {
409     ret.push(500);
410     ret.push("reload config failed");
411   }
412 }
413 
loadConfig(const string & conf_file_name,const string & conf_name,bool live_reload,DSMStateDiagramCollection * m_diags)414 bool DSMFactory::loadConfig(const string& conf_file_name, const string& conf_name,
415 			    bool live_reload, DSMStateDiagramCollection* m_diags) {
416 
417   string script_name = conf_name.substr(0, conf_name.length()-5); // - .conf
418   DBG("loading %s from %s ...\n", script_name.c_str(), conf_file_name.c_str());
419   AmConfigReader cfg;
420   if(cfg.loadFile(conf_file_name))
421     return false;
422 
423   DSMScriptConfig script_config;
424   script_config.RunInviteEvent =
425     cfg.getParameter("run_invite_event")=="yes";
426 
427   script_config.SetParamVariables =
428     cfg.getParameter("set_param_variables")=="yes";
429 
430   script_config.config_vars.insert(cfg.begin(), cfg.end());
431 
432   if (live_reload) {
433     INFO("live DSM config reload does NOT reload prompts and prompt sets!\n");
434   } else {
435     if (!loadPrompts(cfg))
436       return false;
437 
438     if (!loadPromptSets(cfg))
439       return false;
440   }
441 
442   DSMStateDiagramCollection* used_diags;
443   if (m_diags != NULL)
444     used_diags = m_diags;     // got this from caller (main diags)
445   else {
446     // create a new set of diags
447     used_diags = script_config.diags = new DSMStateDiagramCollection();
448   }
449 
450   if (!loadDiags(cfg, used_diags))
451     return false;
452 
453   vector<string> registered_apps;
454   if (!registerApps(cfg, used_diags, registered_apps))
455     return false;
456 
457   ScriptConfigs_mut.lock();
458   try {
459     Name2ScriptConfig[script_name] = script_config;
460     // set ScriptConfig to this for all registered apps' names
461     for (vector<string>::iterator reg_app_it=
462 	   registered_apps.begin(); reg_app_it != registered_apps.end(); reg_app_it++) {
463       string& app_name = *reg_app_it;
464       // dispose of the old one, if it exists
465       map<string, DSMScriptConfig>::iterator it=ScriptConfigs.find(app_name);
466       if (it != ScriptConfigs.end()) {
467 	// may be in use by active call - don't delete but save to
468 	// old_diags for garbage collection (destructor)
469 	if (it->second.diags != NULL)
470 	  old_diags.insert(it->second.diags);
471       }
472 
473       // overwrite with new config
474       ScriptConfigs[app_name] = script_config;
475     }
476   } catch(...) {
477     ScriptConfigs_mut.unlock();
478     throw;
479   }
480   ScriptConfigs_mut.unlock();
481 
482   bool res = true;
483 
484   vector<string> system_dsms = explode(cfg.getParameter("run_system_dsms"), ",");
485   for (vector<string>::iterator it=system_dsms.begin(); it != system_dsms.end(); it++) {
486     string status;
487     if (createSystemDSM(script_name, *it, live_reload, status)) {
488     } else {
489       ERROR("creating system DSM '%s': '%s'\n", it->c_str(), status.c_str());
490       res = false;
491     }
492   }
493 
494   return res;
495 }
496 
497 
prepareSession(DSMCall * s)498 void DSMFactory::prepareSession(DSMCall* s) {
499   s->setPromptSets(prompt_sets);
500   setupSessionTimer(s);
501 }
502 
setupSessionTimer(AmSession * s)503 void DSMFactory::setupSessionTimer(AmSession* s) {
504   if (NULL != session_timer_f) {
505 
506     AmSessionEventHandler* h = session_timer_f->getHandler(s);
507     if (NULL == h)
508       return;
509 
510     if(h->configure(cfg)){
511       ERROR("Could not configure the session timer: disabling session timers.\n");
512       delete h;
513     } else {
514       s->addHandler(h);
515     }
516   }
517 }
518 
addVariables(DSMCall * s,const string & prefix,map<string,string> & vars)519 void DSMFactory::addVariables(DSMCall* s, const string& prefix,
520 			      map<string, string>& vars) {
521   for (map<string, string>::iterator it =
522 	 vars.begin(); it != vars.end(); it++)
523     s->var[prefix+it->first] = it->second;
524 }
525 
addParams(DSMCall * s,const string & hdrs)526 void DSMFactory::addParams(DSMCall* s, const string& hdrs) {
527   // TODO: use real parser with quoting and optimize
528   map<string, string> params;
529   vector<string> items = explode(getHeader(hdrs, PARAM_HDR, true), ";");
530   for (vector<string>::iterator it=items.begin();
531        it != items.end(); it++) {
532     vector<string> kv = explode(*it, "=");
533     if (kv.size()==2)
534       params.insert(make_pair(kv[0], kv[1]));
535   }
536   addVariables(s, "", params);
537 }
538 
AmArg2DSMStrMap(const AmArg & arg,map<string,string> & vars)539 void AmArg2DSMStrMap(const AmArg& arg,
540 		     map<string, string>& vars) {
541   for (AmArg::ValueStruct::const_iterator it=arg.begin();
542        it != arg.end(); it++) {
543     if (it->second.getType() == AmArg::CStr)
544       vars[it->first] = it->second.asCStr();
545     else if (it->second.getType() == AmArg::Array) {
546       vars[it->first+"_size"] = int2str((unsigned int)it->second.size());
547       for (size_t i=0;i<it->second.size();i++) {
548 	if (it->second.get(i).getType() == AmArg::CStr)
549 	  vars[it->first+"_"+int2str((unsigned int)i)] = it->second.get(i).asCStr();
550 	else
551 	  vars[it->first+"_"+int2str((unsigned int)i)] = AmArg::print(it->second.get(i));
552       }
553     } else {
554       vars[it->first] = AmArg::print(it->second);
555     }
556   }
557 }
558 
runMonitorAppSelect(const AmSipRequest & req,string & start_diag,map<string,string> & vars)559 void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag,
560 				     map<string, string>& vars) {
561 #define FALLBACK_OR_EXCEPTION(code, reason)				\
562   if (MonSelectFallback.empty()) {					\
563     throw AmSession::Exception(code, reason);				\
564   } else {								\
565     DBG("falling back to '%s'\n", MonSelectFallback.c_str());		\
566     start_diag = MonSelectFallback;					\
567     return;								\
568   }
569 
570 #ifdef USE_MONITORING
571       if (NULL == MONITORING_GLOBAL_INTERFACE) {
572 	ERROR("using $(mon_select) but monitoring not loaded\n");
573 	FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
574       }
575 
576       AmArg di_args, ret;
577       if (MonSelectCaller != MonSelect_NONE) {
578 	AmUriParser from_parser;
579 	if (MonSelectCaller == MonSelect_FROM)
580 	  from_parser.uri = req.from_uri;
581 	else {
582 	  size_t end;
583 	  string pai = getHeader(req.hdrs, SIP_HDR_P_ASSERTED_IDENTITY, true);
584 	  if (!from_parser.parse_contact(pai, 0, end)) {
585 	    WARN("Failed to parse " SIP_HDR_P_ASSERTED_IDENTITY " '%s'\n",
586 		  pai.c_str());
587 	    FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
588 	  }
589 	}
590 
591 	if (!from_parser.parse_uri()) {
592 	  DBG("failed to parse caller uri '%s'\n", from_parser.uri.c_str());
593 	  FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
594 	}
595 
596 	AmArg caller_filter;
597 	caller_filter.push("caller");
598 	caller_filter.push(from_parser.uri_user);
599 	DBG(" && looking for caller=='%s'\n", from_parser.uri_user.c_str());
600 	di_args.push(caller_filter);
601       }
602 
603 
604       if (MonSelectCallee != MonSelect_NONE) {
605 	AmArg callee_filter;
606 	callee_filter.push("callee");
607 	if (MonSelectCallee == MonSelect_RURI)
608 	  callee_filter.push(req.user);
609 	else {
610 	  AmUriParser to_parser;
611 	  size_t end;
612 	  if (!to_parser.parse_contact(req.to, 0, end)) {
613 	    ERROR("Failed to parse To '%s'\n", req.to.c_str());
614 	    FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
615 	  }
616 	  if (!to_parser.parse_uri()) {
617 	    DBG("failed to parse callee uri '%s'\n", to_parser.uri.c_str());
618 	    FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
619 	  }
620 	  callee_filter.push(to_parser.uri_user);
621 	}
622 
623 	DBG(" && looking for callee=='%s'\n", req.user.c_str());
624 	di_args.push(callee_filter);
625       }
626       // apply additional filters
627       if (MonSelectFilters.size()) {
628 	string app_params = getHeader(req.hdrs, PARAM_HDR);
629 	for (vector<string>::iterator it =
630 	       MonSelectFilters.begin(); it != MonSelectFilters.end(); it++) {
631 	  AmArg filter;
632 	  filter.push(*it); // avp name
633 	  string app_param_val = get_header_keyvalue(app_params, *it);
634 	  filter.push(app_param_val);
635 	  di_args.push(filter);
636 	  DBG(" && looking for %s=='%s'\n", it->c_str(), app_param_val.c_str());
637 	}
638       }
639 
640       MONITORING_GLOBAL_INTERFACE->invoke("listByFilter",di_args,ret);
641 
642       if ((ret.getType()!=AmArg::Array)||
643 	  !ret.size()) {
644 	DBG("call info not found. caller uri %s, r-uri %s\n",
645 	     req.from_uri.c_str(), req.r_uri.c_str());
646 	FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
647       }
648 
649       AmArg sess_id, sess_params;
650       if (ret.size()>1) {
651 	DBG("multiple call info found - picking the first one\n");
652       }
653       const char* session_id = ret.get(0).asCStr();
654       sess_id.push(session_id);
655       MONITORING_GLOBAL_INTERFACE->invoke("get",sess_id,sess_params);
656 
657       if ((sess_params.getType()!=AmArg::Array)||
658 	  !sess_params.size() ||
659 	  sess_params.get(0).getType() != AmArg::Struct) {
660 	INFO("call parameters not found. caller uri %s, r-uri %s, id %s\n",
661 	     req.from_uri.c_str(), req.r_uri.c_str(), ret.get(0).asCStr());
662 	FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
663       }
664 
665       AmArg& sess_dict = sess_params.get(0);
666       if (sess_dict.hasMember("app")) {
667 	start_diag = sess_dict["app"].asCStr();
668 	DBG("selected application '%s' for session\n", start_diag.c_str());
669       } else {
670 	ERROR("selected session params don't contain 'app'\n");
671 	FALLBACK_OR_EXCEPTION(500, "Internal Server Error");
672       }
673       AmArg2DSMStrMap(sess_dict["appParams"], vars);
674       vars["mon_session_record"] = session_id;
675 
676 #else
677       ERROR("using $(mon_select) for dsm application, "
678 	    "but compiled without monitoring support!\n");
679       throw AmSession::Exception(500, "Internal Server Error");
680 #endif
681 
682 #undef FALLBACK_OR_EXCEPTION
683 }
684 
onInvite(const AmSipRequest & req,const string & app_name,const map<string,string> & app_params)685 AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name,
686 				const map<string,string>& app_params)
687 {
688   string start_diag;
689   map<string, string> vars;
690 
691   if (app_name == MOD_NAME) {
692     if (InboundStartDiag.empty()) {
693       ERROR("no inbound calls allowed\n");
694       throw AmSession::Exception(488, SIP_REPLY_NOT_ACCEPTABLE_HERE);
695     }
696     if (InboundStartDiag=="$(mon_select)") {
697       runMonitorAppSelect(req, start_diag, vars);
698     } else {
699       start_diag = InboundStartDiag;
700     }
701   } else {
702     start_diag = app_name;
703   }
704 
705   DBG("start_diag = %s\n",start_diag.c_str());
706 
707   // determine run configuration for script
708   DSMScriptConfig call_config;
709   ScriptConfigs_mut.lock();
710   map<string, DSMScriptConfig>::iterator sc=ScriptConfigs.find(start_diag);
711   if (sc == ScriptConfigs.end())
712     call_config = MainScriptConfig;
713   else
714     call_config = sc->second;
715 
716   DSMCall* s = new DSMCall(call_config, &prompts, *call_config.diags, start_diag, NULL);
717 
718   ScriptConfigs_mut.unlock();
719 
720   prepareSession(s);
721   addVariables(s, "config.", call_config.config_vars);
722 
723   if (call_config.SetParamVariables)
724     addParams(s, req.hdrs);
725 
726   if (!vars.empty())
727     addVariables(s, "", vars);
728 
729   return s;
730 }
731 
732 // outgoing call
onInvite(const AmSipRequest & req,const string & app_name,AmArg & session_params)733 AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name,
734 				AmArg& session_params)
735 {
736 
737   string start_diag;
738 
739   if (app_name == MOD_NAME) {
740     if (OutboundStartDiag.empty()) {
741       ERROR("no outbound calls allowed\n");
742       throw AmSession::Exception(488, SIP_REPLY_NOT_ACCEPTABLE_HERE);
743     }
744   } else {
745     start_diag = app_name;
746   }
747 
748   UACAuthCred* cred = NULL;
749   map<string, string> vars;
750   // Creds
751   if (session_params.getType() == AmArg::AObject) {
752     AmObject* cred_obj = session_params.asObject();
753     if (cred_obj)
754       cred = dynamic_cast<UACAuthCred*>(cred_obj);
755   } else if (session_params.getType() == AmArg::Array) {
756     DBG("session params is array - size %zd\n", session_params.size());
757     // Creds
758     cred = AmUACAuth::unpackCredentials(session_params.get(0));
759     // Creds + vars
760     if (session_params.size()>1 &&
761 	session_params.get(1).getType() == AmArg::Struct) {
762       AmArg2DSMStrMap(session_params.get(1), vars);
763     }
764   } else if (session_params.getType() == AmArg::Struct) {
765     // vars
766     AmArg2DSMStrMap(session_params, vars);
767   }
768 
769   DSMScriptConfig call_config;
770   ScriptConfigs_mut.lock();
771   map<string, DSMScriptConfig>::iterator sc=ScriptConfigs.find(start_diag);
772   if (sc == ScriptConfigs.end())
773     call_config = MainScriptConfig;
774   else
775     call_config = sc->second;
776 
777   DSMCall* s = new DSMCall(call_config, &prompts, *call_config.diags, start_diag, cred);
778 
779   ScriptConfigs_mut.unlock();
780 
781   prepareSession(s);
782 
783   addVariables(s, "config.", call_config.config_vars);
784   if (!vars.empty())
785     addVariables(s, "", vars);
786 
787   if (call_config.SetParamVariables)
788     addParams(s, req.hdrs);
789 
790   if (NULL == cred) {
791     DBG("outgoing DSM call will not be authenticated.\n");
792   } else {
793     AmUACAuth::enable(s);
794   }
795 
796   return s;
797 }
798 
createSystemDSM(const string & config_name,const string & start_diag,bool reload,string & status)799 bool DSMFactory::createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status) {
800   bool res = true;
801 
802   DSMScriptConfig* script_config = NULL;
803   ScriptConfigs_mut.lock();
804   if (config_name == "main")
805     script_config = &MainScriptConfig;
806   else {
807     map<string, DSMScriptConfig>::iterator it = Name2ScriptConfig.find(config_name);
808     if (it != Name2ScriptConfig.end())
809       script_config = &it->second;
810   }
811   if (script_config==NULL) {
812     status = "Error: Script config '"+config_name+"' not found, in [";
813     for (map<string, DSMScriptConfig>::iterator it =
814 	   Name2ScriptConfig.begin(); it != Name2ScriptConfig.end(); it++) {
815       if (it != Name2ScriptConfig.begin())
816 	status+=", ";
817       status += it->first;
818     }
819     status += "]";
820     res = false;
821   } else {
822     SystemDSM* s = new SystemDSM(*script_config, start_diag, reload);
823     s->start();
824     // add to garbage collector
825     AmThreadWatcher::instance()->add(s);
826     status = "OK";
827   }
828   ScriptConfigs_mut.unlock();
829   return res;
830 }
831 
reloadDSMs(const AmArg & args,AmArg & ret)832 void DSMFactory::reloadDSMs(const AmArg& args, AmArg& ret) {
833   AmConfigReader cfg;
834   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
835       ret.push(500);
836       ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
837       return ;
838   }
839 
840   DSMStateDiagramCollection* new_diags = new DSMStateDiagramCollection();
841 
842   string DiagPath = cfg.getParameter("diag_path");
843   if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
844     DiagPath += '/';
845 
846   string ModPath = cfg.getParameter("mod_path");
847 
848   string LoadDiags = cfg.getParameter("load_diags");
849   vector<string> diags_names = explode(LoadDiags, ",");
850   for (vector<string>::iterator it=
851 	 diags_names.begin(); it != diags_names.end(); it++) {
852     if (!new_diags->loadFile(DiagPath+*it+".dsm", *it, DiagPath, ModPath,
853 			     DebugDSM, CheckDSM)) {
854       ERROR("loading %s from %s\n",
855 	    it->c_str(), (DiagPath+*it+".dsm").c_str());
856       ret.push(500);
857       ret.push("loading " +*it+ " from "+ DiagPath+*it+".dsm");
858       return;
859     }
860   }
861   ScriptConfigs_mut.lock();
862   old_diags.insert(MainScriptConfig.diags);
863   MainScriptConfig.diags = new_diags;
864   ScriptConfigs_mut.unlock();
865 
866   ret.push(200);
867   ret.push("DSMs reloaded");
868 }
869 
870 
preloadModules(AmConfigReader & cfg,string & res,const string & ModPath)871 int DSMFactory::preloadModules(AmConfigReader& cfg, string& res, const string& ModPath) {
872   string preload_mods = cfg.getParameter("preload_mods");
873   vector<string> preload_names = explode(preload_mods, ",");
874   if (preload_names.size()) {
875     for (vector<string>::iterator it=
876 	   preload_names.begin(); it != preload_names.end(); it++) {
877       DBG("preloading '%s'...\n", it->c_str());
878       if (!preload_reader.importModule("import("+*it+")", ModPath)) {
879 	res = "importing module '"+*it+"' for preload\n";
880 	return -1;
881       }
882       DSMModule* last_loaded = preload_reader.mods.back();
883       if (last_loaded) {
884  	if (last_loaded->preload()) {
885 	  res = "Error while preloading '"+*it+"'\n";
886  	  return -1;
887  	}
888       }
889     }
890   }
891 
892   return 0;
893 }
894 
preloadModules(const AmArg & args,AmArg & ret)895 void DSMFactory::preloadModules(const AmArg& args, AmArg& ret) {
896   AmConfigReader cfg;
897   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
898       ret.push(500);
899       ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
900       return ;
901   }
902   string err;
903 
904   string ModPath = cfg.getParameter("mod_path");
905 
906   int res = preloadModules(cfg, err, ModPath);
907   if (res<0) {
908     ret.push(500);
909     ret.push(err);
910   } else {
911     ret.push(200);
912     ret.push("modules preloaded");
913   }
914 }
915 
preloadModule(const AmArg & args,AmArg & ret)916 void DSMFactory::preloadModule(const AmArg& args, AmArg& ret) {
917   string mod_name = args.get(0).asCStr();
918   string mod_path = args.get(1).asCStr();
919 
920   if (!preload_reader.importModule("import("+mod_name+")", mod_path)) {
921     ret.push(500);
922     ret.push("importing module '"+mod_name+"' for preload");
923     return;
924   }
925   DSMModule* last_loaded = preload_reader.mods.back();
926   if (last_loaded) {
927     if (last_loaded->preload()) {
928       ret.push(500);
929       ret.push("Error while preloading '"+mod_name+"'");
930       return;
931     }
932   }
933   ret.push(200);
934   ret.push("module preloaded.");
935   return;
936 }
937 
listDSMs(const AmArg & args,AmArg & ret)938 void DSMFactory::listDSMs(const AmArg& args, AmArg& ret) {
939   vector<string> names;
940   ScriptConfigs_mut.lock();
941 
942   try {
943     if (isArgUndef(args) || !args.size())
944       names = MainScriptConfig.diags->getDiagramNames();
945     else {
946       if (isArgCStr(args.get(0))) {
947 	map<string, DSMScriptConfig>::iterator i=
948 	  ScriptConfigs.find(args.get(0).asCStr());
949 	if (i!= ScriptConfigs.end())
950 	  names = i->second.diags->getDiagramNames();
951       }
952     }
953   } catch (...) {
954     ScriptConfigs_mut.unlock();
955     throw;
956   }
957 
958   ScriptConfigs_mut.unlock();
959 
960   for (vector<string>::iterator it=
961 	 names.begin(); it != names.end(); it++) {
962     ret.push(*it);
963   }
964 }
965 
hasDSM(const string & dsm_name,const string & conf_name)966 bool DSMFactory::hasDSM(const string& dsm_name, const string& conf_name) {
967   bool res = false;
968   if (conf_name.empty())
969     res = MainScriptConfig.diags->hasDiagram(dsm_name);
970   else {
971       map<string, DSMScriptConfig>::iterator i=
972 	ScriptConfigs.find(conf_name);
973 	if (i!= ScriptConfigs.end())
974 	  res = i->second.diags->hasDiagram(dsm_name);
975   }
976   return res;
977 }
978 
hasDSM(const AmArg & args,AmArg & ret)979 void DSMFactory::hasDSM(const AmArg& args, AmArg& ret) {
980   string conf_name;
981   if (args.size()>1 && isArgCStr(args.get(1)))
982     conf_name = args.get(1).asCStr();
983 
984   bool res;
985 
986   ScriptConfigs_mut.lock();
987   try {
988     res = hasDSM(args.get(0).asCStr(), conf_name);
989   } catch(...) {
990     ScriptConfigs_mut.unlock();
991     throw;
992   }
993   ScriptConfigs_mut.unlock();
994 
995   if (res)
996     ret.push("1");
997   else
998     ret.push("0");
999 }
1000 
registerApplication(const AmArg & args,AmArg & ret)1001 void DSMFactory::registerApplication(const AmArg& args, AmArg& ret) {
1002   string diag_name = args.get(0).asCStr();
1003   string conf_name;
1004   if (args.size()>1 && isArgCStr(args.get(1)))
1005     conf_name = args.get(1).asCStr();
1006   bool has_diag;
1007 
1008   ScriptConfigs_mut.lock();
1009   try {
1010     has_diag = hasDSM(diag_name, conf_name);
1011   } catch(...) {
1012     ScriptConfigs_mut.unlock();
1013     throw;
1014   }
1015   ScriptConfigs_mut.unlock();
1016 
1017   if (!has_diag) {
1018     ret.push(400);
1019     ret.push("unknown application (DSM)");
1020     return;
1021   }
1022 
1023   bool res = AmPlugIn::instance()->registerFactory4App(diag_name,this);
1024   if(res) {
1025     INFO("DSM state machine registered: %s.\n",diag_name.c_str());
1026     ret.push(200);
1027     ret.push("registered DSM application");
1028   } else {
1029     ret.push(500);
1030     ret.push("Error registering DSM application (already registered?)");
1031   }
1032 }
1033 
loadDSM(const AmArg & args,AmArg & ret)1034 void DSMFactory::loadDSM(const AmArg& args, AmArg& ret) {
1035   string dsm_name  = args.get(0).asCStr();
1036 
1037   AmConfigReader cfg;
1038   if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
1039       ret.push(500);
1040       ret.push("loading config file " +AmConfig::ModConfigPath + string(MOD_NAME ".conf"));
1041       return;
1042   }
1043 
1044   string DiagPath = cfg.getParameter("diag_path");
1045   if (DiagPath.length() && DiagPath[DiagPath.length()-1] != '/')
1046     DiagPath += '/';
1047 
1048   string ModPath = cfg.getParameter("mod_path");
1049 
1050   string dsm_file_name = DiagPath+dsm_name+".dsm";
1051   string res = "OK";
1052 
1053   ScriptConfigs_mut.lock();
1054   try {
1055     if (MainScriptConfig.diags->hasDiagram(dsm_name)) {
1056       ret.push(400);
1057       ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
1058     } else {
1059       if (!MainScriptConfig.diags->loadFile(dsm_file_name, dsm_name, DiagPath, ModPath, DebugDSM, CheckDSM)) {
1060 	ret.push(500);
1061 	ret.push("error loading "+dsm_name+" from "+ dsm_file_name);
1062       } else {
1063 	ret.push(200);
1064 	ret.push("loaded "+dsm_name+" from "+ dsm_file_name);
1065       }
1066     }
1067   } catch(...) {
1068     ScriptConfigs_mut.unlock();
1069     throw;
1070   }
1071   ScriptConfigs_mut.unlock();
1072 }
1073 
loadDSMWithPaths(const AmArg & args,AmArg & ret)1074 void DSMFactory::loadDSMWithPaths(const AmArg& args, AmArg& ret) {
1075   string dsm_name  = args.get(0).asCStr();
1076   string diag_path = args.get(1).asCStr();
1077   string mod_path  = args.get(2).asCStr();
1078 
1079   string res = "OK";
1080   ScriptConfigs_mut.lock();
1081   try {
1082     if (MainScriptConfig.diags->hasDiagram(dsm_name)) {
1083       ret.push(400);
1084       ret.push("DSM named '" + dsm_name + "' already loaded (use reloadDSMs to reload all)");
1085     } else {
1086       if (!MainScriptConfig.diags->loadFile(diag_path+dsm_name+".dsm", dsm_name, diag_path, mod_path, DebugDSM, CheckDSM)) {
1087 	ret.push(500);
1088 	ret.push("error loading "+dsm_name+" from "+ diag_path+dsm_name+".dsm");
1089       } else {
1090 	ret.push(200);
1091 	ret.push("loaded "+dsm_name+" from "+ diag_path+dsm_name+".dsm");
1092       }
1093     }
1094   } catch(...) {
1095     ScriptConfigs_mut.unlock();
1096     throw;
1097   }
1098   ScriptConfigs_mut.unlock();
1099 }
1100 
addScriptDiagsToEngine(const string & config_set,DSMStateEngine * engine,map<string,string> & config_vars,bool & SetParamVariables)1101 bool DSMFactory::addScriptDiagsToEngine(const string& config_set,
1102 					DSMStateEngine* engine,
1103 					map<string,string>& config_vars,
1104 					bool& SetParamVariables) {
1105   bool res = false;
1106   ScriptConfigs_mut.lock();
1107   try {
1108     map<string, DSMScriptConfig>::iterator it=Name2ScriptConfig.find(config_set);
1109     if (it!=Name2ScriptConfig.end()) {
1110       res = true;
1111       it->second.diags->addToEngine(engine);
1112       config_vars = it->second.config_vars;
1113       SetParamVariables = it->second.SetParamVariables;
1114     }
1115   } catch(...) {
1116     ScriptConfigs_mut.unlock();
1117     throw;
1118   }
1119   ScriptConfigs_mut.unlock();
1120   return res;
1121 }
1122 
invoke(const string & method,const AmArg & args,AmArg & ret)1123 void DSMFactory::invoke(const string& method, const AmArg& args,
1124 				AmArg& ret)
1125 {
1126   if (method == "postDSMEvent"){
1127     assertArgCStr(args.get(0))
1128 
1129     DSMEvent* ev = new DSMEvent();
1130     for (size_t i=0;i<args[1].size();i++)
1131       ev->params[args[1][i][0].asCStr()] = args[1][i][1].asCStr();
1132 
1133     if (AmSessionContainer::instance()->postEvent(args.get(0).asCStr(), ev)) {
1134       ret.push(AmArg(200));
1135       ret.push(AmArg("OK"));
1136     } else {
1137       ret.push(AmArg(404));
1138       ret.push(AmArg("Session not found"));
1139     }
1140   } else if (method == "reloadDSMs"){
1141     reloadDSMs(args,ret);
1142   } else if (method == "loadDSM"){
1143     args.assertArrayFmt("s");
1144     loadDSM(args,ret);
1145   } else if (method == "loadDSMWithPath"){
1146     args.assertArrayFmt("sss");
1147     loadDSMWithPaths(args,ret);
1148   } else if (method == "preloadModules"){
1149     preloadModules(args,ret);
1150   } else if (method == "preloadModule"){
1151     args.assertArrayFmt("ss");
1152     preloadModule(args,ret);
1153   } else if (method == "hasDSM"){
1154     args.assertArrayFmt("s");
1155     hasDSM(args,ret);
1156   } else if (method == "listDSMs"){
1157     listDSMs(args,ret);
1158   } else if (method == "registerApplication"){
1159     args.assertArrayFmt("s");
1160     registerApplication(args,ret);
1161   } else if (method == "loadConfig"){
1162     args.assertArrayFmt("ss");
1163     loadConfig(args,ret);
1164   } else if (method == "createSystemDSM"){
1165     args.assertArrayFmt("ss");
1166     string status;
1167     if (createSystemDSM(args.get(0).asCStr(), args.get(1).asCStr(), false, status)) {
1168       ret.push(200);
1169       ret.push(status);
1170     } else {
1171       ret.push(500);
1172       ret.push(status);
1173     }
1174   } else if(method == "_list"){
1175     ret.push(AmArg("postDSMEvent"));
1176     ret.push(AmArg("reloadDSMs"));
1177     ret.push(AmArg("loadDSM"));
1178     ret.push(AmArg("loadDSMWithPaths"));
1179     ret.push(AmArg("preloadModules"));
1180     ret.push(AmArg("preloadModule"));
1181     ret.push(AmArg("loadConfig"));
1182     ret.push(AmArg("hasDSM"));
1183     ret.push(AmArg("listDSMs"));
1184     ret.push(AmArg("registerApplication"));
1185     ret.push(AmArg("createSystemDSM"));
1186   }  else
1187     throw AmDynInvoke::NotImplemented(method);
1188 }
1189 
1190