/// <summary> /// Processes events received from FreeSWITCH and dispatches them to the /// application event handlers. This is called from <see cref="SwitchApp" /> /// immediately after <see cref="SwitchApp.Main" /> returns on the /// application thread. /// </summary> internal static void EventLoop() { // Enlist in low-level global events if the application requested. var options = (switch_xml_section_enum_t)0; if (dialPlanEvent != null) { options |= switch_xml_section_enum_t.SWITCH_XML_SECTION_DIALPLAN; } if (userDirectoryEvent != null) { options |= switch_xml_section_enum_t.SWITCH_XML_SECTION_DIRECTORY; } if (options != (switch_xml_section_enum_t)0) { eventBinding = SwitchXmlSearchBinding.Bind(OnSwitchXmlSearchEvent, options); } // Decided whether we're going to actually consume NeonSwitch events based // on whether the application enlisted in the [EventReceived] event within // its [Main()] method. Note that we we'll exit the event loop (and the // application's background thread) immediately if this is the case. if (eventReceived == null) { return; } eventConsumer = new EventConsumer("all", string.Empty, 0); // Loop, waiting for the application to terminate. while (!stopPending) { try { var fsEvent = eventConsumer.pop(1, 0); var switchEvent = new SwitchEvent(fsEvent.InternalEvent); RaiseEventReceived(new SwitchEventArgs(switchEvent)); if (switchEvent.EventType == SwitchEventCode.BackgroundJob) { RaiseJobCompletedEvent(new JobCompletedEventArgs(switchEvent.Headers.Get("Job-ID", Guid.Empty))); } } catch (Exception e) { SysLog.LogException(e); } } }
/// <summary> /// Called by FreeSWITCH when one of the bound events is raised. /// </summary> /// <param name="args">The event arguments.</param> /// <returns>The event results as an XML document.</returns> private static string OnSwitchXmlSearchEvent(SwitchXmlSearchBinding.XmlBindingArgs args) { string result = null; try { switch (args.Section.ToLower()) { case "directory": { if (userDirectoryEvent == null) { break; } var switchEvent = new SwitchEvent(args.Parameters); var action = switchEvent.Headers.Get("action", string.Empty).ToLower(); if (action != "sip_auth") { return(FsConfigNotFound.Xml); // Ignore non-user directory operations } var directoryArgs = new UserDirectoryEventArgs(new SwitchEvent(args.Parameters)); userDirectoryEvent(null, directoryArgs); if (directoryArgs.Handled) { if (directoryArgs.Password == null) { directoryArgs.Password = string.Empty; } if (directoryArgs.AccessDenied) { // $hack(jeff.lill): // // There's doesn't appear to be a clean way to tell the switch that the user // is valid but that access should be denied and no additional lookup should // be performed by other modules. I'm going to handle this by setting the // password to guid. directoryArgs.Password = Guid.NewGuid().ToString("N"); } // Validate and set the common parameters/variables. try { Dtmf.ValidateNumeric(directoryArgs.VoiceMailPassword, 0, int.MaxValue, true); } catch (Exception e) { SysLog.LogWarning("UserDirectoryEvent handler ignored [VoiceMailPassword]: {0}", e.Message); } try { Dtmf.ValidateNumeric(directoryArgs.EffectiveCallerIDNumber, 0, 11, true); } catch (Exception e) { SysLog.LogWarning("UserDirectoryEvent handler ignored [EffectiveCallerIDNumber]: {0}", e.Message); } try { Dtmf.ValidateNumeric(directoryArgs.OutboundCallerIDNumber, 0, 11, true); } catch (Exception e) { SysLog.LogWarning("UserDirectoryEvent handler ignored [OutboundCallerIDNumber]: {0}", e.Message); } if (directoryArgs.VoiceMailPassword != null) { directoryArgs.Parameters["vm-password"] = directoryArgs.VoiceMailPassword; } if (directoryArgs.CallingRights != CallingRight.None) { var sb = new StringBuilder(); if ((directoryArgs.CallingRights & CallingRight.Local) != 0) { sb.Append("local"); } if ((directoryArgs.CallingRights & CallingRight.Domestic) != 0) { if (sb.Length > 0) { sb.Append(','); } sb.Append("domestic"); } if ((directoryArgs.CallingRights & CallingRight.International) != 0) { if (sb.Length > 0) { sb.Append(','); } sb.Append("international"); } directoryArgs.Variables["toll_allow"] = sb.ToString(); } if (directoryArgs.AccountCode != null) { directoryArgs.Variables["accountcode"] = directoryArgs.AccountCode; } if (directoryArgs.CallerContext != null) { directoryArgs.Variables["user_context"] = directoryArgs.CallerContext; } if (directoryArgs.EffectiveCallerIDName != null) { directoryArgs.Variables["effective_caller_id_name"] = directoryArgs.EffectiveCallerIDName; } if (directoryArgs.EffectiveCallerIDNumber != null) { directoryArgs.Variables["effective_caller_id_number"] = directoryArgs.EffectiveCallerIDNumber; } if (directoryArgs.OutboundCallerIDName != null) { directoryArgs.Variables["outbound_caller_id_name"] = directoryArgs.OutboundCallerIDName; } if (directoryArgs.OutboundCallerIDNumber != null) { directoryArgs.Variables["outbound_caller_id_number"] = directoryArgs.OutboundCallerIDNumber; } if (directoryArgs.CallGroup != null) { directoryArgs.Variables["callgroup"] = directoryArgs.CallGroup; } result = new FsConfigDirectory(directoryArgs.Domain, directoryArgs.UserID, directoryArgs.Password, directoryArgs.Parameters, directoryArgs.Variables).ToXml(); Debug.WriteLine(result); // $todo(jeff.lill): Delete this } } break; case "dialplan": { if (dialPlanEvent == null) { break; } var dialPlanArgs = new DialPlanEventArgs(new SwitchEvent(args.Parameters)); dialPlanEvent(null, dialPlanArgs); if (dialPlanArgs.Handled) { if (dialPlanArgs.Context == null) { throw new ArgumentException("DialPlanEvent handler returned [Context=null]."); } var renderContext = new ActionRenderingContext(true); foreach (var action in dialPlanArgs.Actions) { action.Render(renderContext); } result = new FsConfigDialPlan(dialPlanArgs.Context, renderContext.Actions).ToXml(); } } break; default: result = null; break; } } catch (Exception e) { SysLog.LogException(e); } return(result ?? FsConfigNotFound.Xml); }