From 93c5659ab72fd14f73cbccb0542c0823fef4624f Mon Sep 17 00:00:00 2001 From: skrusty_cp Date: Wed, 21 Aug 2013 03:31:26 -0700 Subject: [PATCH] See change log for details --- .../Asterisk.NET.Test.csproj | 2 +- .../Asterisk.NET.Test/Program.cs | 23 +++ .../Asterisk.NET/Asterisk.NET.csproj | 3 + Asterisk.2013/Asterisk.NET/Common.cs | 2 +- .../Asterisk.NET/FastAGI/AGIChannel.cs | 28 ++- .../FastAGI/AGIConnectionHandler.cs | 10 +- .../Asterisk.NET/FastAGI/AGIReply.cs | 5 + .../Asterisk.NET/FastAGI/AsteriskFastAGI.cs | 57 +++++- .../Asterisk.NET/FastAGI/IMappingStrategy.cs | 8 + .../GeneralMappingStrategy.cs | 180 ++++++++++++++++++ .../ResourceMappingStrategy.cs | 128 +++++++++++++ .../Asterisk.NET/FastAGI/MappingStrategy.cs | 13 +- .../Asterisk.NET/Manager/ManagerConnection.cs | 20 ++ Asterisk.2013/ChangeLog.txt | 11 ++ 14 files changed, 462 insertions(+), 28 deletions(-) create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/GeneralMappingStrategy.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/ResourceMappingStrategy.cs diff --git a/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Asterisk.NET.Test.csproj b/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Asterisk.NET.Test.csproj index 7ec0b45..df2a506 100644 --- a/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Asterisk.NET.Test.csproj +++ b/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Asterisk.NET.Test.csproj @@ -90,7 +90,7 @@ - + {bc6e7dba-c05a-45fe-a2a3-b1637ce16274} Asterisk.NET diff --git a/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Program.cs b/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Program.cs index 1562e20..12fa6aa 100644 --- a/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Program.cs +++ b/Asterisk.2013/Asterisk.NET.Test/Asterisk.NET.Test/Program.cs @@ -5,6 +5,8 @@ using Asterisk.NET.Manager.Action; using Asterisk.NET.Manager.Response; using Asterisk.NET.FastAGI; using Asterisk.NET.Manager.Event; +using Asterisk.NET.FastAGI.MappingStrategies; +using System.Collections.Generic; namespace Asterisk.NET.Test { @@ -27,7 +29,10 @@ namespace Asterisk.NET.Test [STAThread] static void Main() { + // Comment me out if you don't want to run the AMI sample checkManagerAPI(); + + // Comment me out if you don't want to run the FastAGI sample checkFastAGI(); } @@ -44,6 +49,24 @@ See CustomIVR.cs and fastagi-mapping.resx to detail. Ctrl-C to exit"); AsteriskFastAGI agi = new AsteriskFastAGI(); + agi.BindPort = 8675; + // Remove the lines below to enable the default (resource based) MappingStrategy + // You can use an XML file with XmlMappingStrategy, or simply pass in a list of + // ScriptMapping. + // If you wish to save it to a file, use ScriptMapping.SaveMappings and pass in a path. + // This can then be used to load the mappings without having to change the source code! + + agi.MappingStrategy = new GeneralMappingStrategy(new List() + { + new ScriptMapping() { + ScriptClass = "Asterisk.NET.Test.CustomIVR", + ScriptName = "customivr" + } + }); + + //agi.SC511_CAUSES_EXCEPTION = true; + //agi.SCHANGUP_CAUSES_EXCEPTION = true; + agi.Start(); } #endregion diff --git a/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj index 5c39438..3603bcd 100644 --- a/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj +++ b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj @@ -115,6 +115,9 @@ + + + diff --git a/Asterisk.2013/Asterisk.NET/Common.cs b/Asterisk.2013/Asterisk.NET/Common.cs index 6df3c54..bef2ce0 100644 --- a/Asterisk.2013/Asterisk.NET/Common.cs +++ b/Asterisk.2013/Asterisk.NET/Common.cs @@ -15,7 +15,7 @@ namespace Asterisk.NET /// Line separator public const string LINE_SEPARATOR = "\r\n"; - public static Regex ASTERISK_VERSION = new Regex("^Asterisk\\s+([0-9].[0-9]+.[0-9]+).*", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public static Regex ASTERISK_VERSION = new Regex("^Asterisk\\s+([0-9]+.[0-9]+.[0-9]+).*", RegexOptions.Compiled | RegexOptions.IgnoreCase); public static Regex SHOW_VERSION_FILES_PATTERN = new Regex("^([\\S]+)\\s+Revision: ([0-9\\.]+)"); public static char[] RESPONSE_KEY_VALUE_SEPARATOR = new char[] { ':' }; public static char[] MINUS_SEPARATOR = new char[] { '-' }; diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs index 721ae6a..0db77a3 100644 --- a/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs @@ -5,20 +5,30 @@ namespace Asterisk.NET.FastAGI /// public class AGIChannel { - private AGIWriter agiWriter; + private AGIWriter agiWriter; private AGIReader agiReader; private AGIReply agiReply; - public AGIChannel(IO.SocketConnection socket) - { - this.agiWriter = new AGIWriter(socket); - this.agiReader = new AGIReader(socket); - } + private bool _SC511_CAUSES_EXCEPTION = false; + private bool _SCHANGUP_CAUSES_EXCEPTION = false; - public AGIChannel(AGIWriter agiWriter, AGIReader agiReader) + + public AGIChannel(IO.SocketConnection socket, bool SC511_CAUSES_EXCEPTION, bool SCHANGUP_CAUSES_EXCEPTION) + { + this.agiWriter = new AGIWriter(socket); + this.agiReader = new AGIReader(socket); + + this._SC511_CAUSES_EXCEPTION = SC511_CAUSES_EXCEPTION; + this._SCHANGUP_CAUSES_EXCEPTION = SCHANGUP_CAUSES_EXCEPTION; + } + + public AGIChannel(AGIWriter agiWriter, AGIReader agiReader, bool SC511_CAUSES_EXCEPTION, bool SCHANGUP_CAUSES_EXCEPTION) { this.agiWriter = agiWriter; this.agiReader = agiReader; + + this._SC511_CAUSES_EXCEPTION = SC511_CAUSES_EXCEPTION; + this._SCHANGUP_CAUSES_EXCEPTION = SCHANGUP_CAUSES_EXCEPTION; } /// @@ -38,6 +48,10 @@ namespace Asterisk.NET.FastAGI throw new InvalidOrUnknownCommandException(command.BuildCommand()); else if (status == (int)AGIReplyStatuses.SC_INVALID_COMMAND_SYNTAX) throw new InvalidCommandSyntaxException(agiReply.GetSynopsis(), agiReply.GetUsage()); + else if (status == (int)AGIReplyStatuses.SC_DEAD_CHANNEL && _SC511_CAUSES_EXCEPTION) + throw new AGIHangupException(); + else if ((status == 0) && agiReply.FirstLine == "HANGUP" && _SCHANGUP_CAUSES_EXCEPTION) + throw new AGIHangupException(); return agiReply; } } diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs index fb5532b..6e6f852 100644 --- a/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs @@ -17,7 +17,9 @@ namespace Asterisk.NET.FastAGI #endif private static readonly LocalDataStoreSlot channel = Thread.AllocateDataSlot(); private IO.SocketConnection socket; - private MappingStrategy mappingStrategy; + private IMappingStrategy mappingStrategy; + private bool _SC511_CAUSES_EXCEPTION = false; + private bool _SCHANGUP_CAUSES_EXCEPTION = false; #region Channel /// @@ -39,10 +41,12 @@ namespace Asterisk.NET.FastAGI /// /// the socket connection to handle. /// the strategy to use to determine which script to run. - public AGIConnectionHandler(IO.SocketConnection socket, MappingStrategy mappingStrategy) + public AGIConnectionHandler(IO.SocketConnection socket, IMappingStrategy mappingStrategy, bool SC511_CAUSES_EXCEPTION, bool SCHANGUP_CAUSES_EXCEPTION) { this.socket = socket; this.mappingStrategy = mappingStrategy; + this._SC511_CAUSES_EXCEPTION = SC511_CAUSES_EXCEPTION; + this._SCHANGUP_CAUSES_EXCEPTION = SCHANGUP_CAUSES_EXCEPTION; } #endregion @@ -53,7 +57,7 @@ namespace Asterisk.NET.FastAGI AGIReader reader = new AGIReader(socket); AGIWriter writer = new AGIWriter(socket); AGIRequest request = reader.ReadRequest(); - AGIChannel channel = new AGIChannel(writer, reader); + AGIChannel channel = new AGIChannel(writer, reader, this._SC511_CAUSES_EXCEPTION, this._SCHANGUP_CAUSES_EXCEPTION); AGIScript script = mappingStrategy.DetermineScript(request); Thread.SetData(AGIConnectionHandler.channel, channel); diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs index 2863394..1380992 100644 --- a/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs @@ -18,6 +18,11 @@ namespace Asterisk.NET.FastAGI /// AGICommand because there is no command with the given name available. /// SC_INVALID_OR_UNKNOWN_COMMAND = 510, + /// + /// Status code (511) indicating Asterisk was unable to process the + /// AGICommand because the channel is dead. + /// + SC_DEAD_CHANNEL = 511, /// /// Status code (520) indicating Asterisk was unable to process the /// AGICommand because the syntax used was not correct. This is most likely diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs index acc9f92..c0a7423 100644 --- a/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs @@ -1,3 +1,4 @@ +using Asterisk.NET.FastAGI.MappingStrategies; using System.IO; using System.Net; using System.Text; @@ -5,10 +6,25 @@ using System.Text; namespace Asterisk.NET.FastAGI { public class AsteriskFastAGI - { - #region Variables + { + + #region Flags + /// + /// If set to true, causes the AGIChannel to throw an exception when a status code of 511 (Channel Dead) is returned. + /// This is set to false by default to maintain backwards compatibility + /// + public bool SC511_CAUSES_EXCEPTION = false; + + /// + /// If set to true, causes the AGIChannel to throw an exception when return status is 0 and reply is HANGUP. + /// This is set to false by default to maintain backwards compatibility + /// + public bool SCHANGUP_CAUSES_EXCEPTION = false; + #endregion + + #region Variables #if LOGGER - private Logger logger = Logger.Instance(); + private Logger logger = Logger.Instance(); #endif private IO.ServerSocket serverSocket; @@ -25,7 +41,7 @@ namespace Asterisk.NET.FastAGI /// /// The strategy to use for bind AGIRequests to AGIScripts that serve them. /// - private MappingStrategy mappingStrategy; + private IMappingStrategy mappingStrategy; private Encoding socketEncoding = Encoding.ASCII; #endregion @@ -61,7 +77,7 @@ namespace Asterisk.NET.FastAGI /// The default mapping is a MappingStrategy. /// /// - public MappingStrategy MappingStrategy + public IMappingStrategy MappingStrategy { set { this.mappingStrategy = value; } } @@ -84,7 +100,7 @@ namespace Asterisk.NET.FastAGI this.address = Common.AGI_BIND_ADDRESS; this.port = Common.AGI_BIND_PORT; this.poolSize = Common.AGI_POOL_SIZE; - this.mappingStrategy = new MappingStrategy(); + this.mappingStrategy = new ResourceMappingStrategy(); } #endregion @@ -97,10 +113,31 @@ namespace Asterisk.NET.FastAGI this.address = Common.AGI_BIND_ADDRESS; this.port = Common.AGI_BIND_PORT; this.poolSize = Common.AGI_POOL_SIZE; - this.mappingStrategy = new MappingStrategy(mappingStrategy); + this.mappingStrategy = new ResourceMappingStrategy(mappingStrategy); } #endregion + #region Constructor - AsteriskFastAGI() + /// + /// Creates a new AsteriskFastAGI. + /// + public AsteriskFastAGI(IMappingStrategy mappingStrategy) + { + this.address = Common.AGI_BIND_ADDRESS; + this.port = Common.AGI_BIND_PORT; + this.poolSize = Common.AGI_POOL_SIZE; + this.mappingStrategy = mappingStrategy; + } + + public AsteriskFastAGI(IMappingStrategy mappingStrategy, string ipaddress, int port, int poolSize) + { + this.address = ipaddress; + this.port = port; + this.poolSize = poolSize; + this.mappingStrategy = mappingStrategy; + } + #endregion + #region Constructor - AsteriskFastAGI(int port, int poolSize) /// /// Creates a new AsteriskFastAGI. @@ -113,7 +150,7 @@ namespace Asterisk.NET.FastAGI this.address = Common.AGI_BIND_ADDRESS; this.port = port; this.poolSize = poolSize; - this.mappingStrategy = new MappingStrategy(); + this.mappingStrategy = new ResourceMappingStrategy(); } #endregion @@ -130,7 +167,7 @@ namespace Asterisk.NET.FastAGI this.address = ipaddress; this.port = port; this.poolSize = poolSize; - this.mappingStrategy = new MappingStrategy(); + this.mappingStrategy = new ResourceMappingStrategy(); } #endregion @@ -168,7 +205,7 @@ namespace Asterisk.NET.FastAGI #if LOGGER logger.Info("Received connection."); #endif - connectionHandler = new AGIConnectionHandler(socket, mappingStrategy); + connectionHandler = new AGIConnectionHandler(socket, mappingStrategy, this.SC511_CAUSES_EXCEPTION, this.SCHANGUP_CAUSES_EXCEPTION); pool.AddJob(connectionHandler); } } diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs b/Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs new file mode 100644 index 0000000..b7baa32 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs @@ -0,0 +1,8 @@ +namespace Asterisk.NET.FastAGI +{ + public interface IMappingStrategy + { + AGIScript DetermineScript(AGIRequest request); + void Load(); + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/GeneralMappingStrategy.cs b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/GeneralMappingStrategy.cs new file mode 100644 index 0000000..64f4c93 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/GeneralMappingStrategy.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections; +using System.Resources; +using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Xml.Serialization; + +namespace Asterisk.NET.FastAGI.MappingStrategies +{ + + internal class MappingAssembly + { + public string ClassName { get; set; } + public Assembly LoadedAssembly { get; set; } + + public AGIScript CreateInstance() + { + AGIScript rtn = null; + try + { + if (LoadedAssembly != null) + rtn = (AGIScript)LoadedAssembly.CreateInstance(ClassName); + else + rtn = (AGIScript)Assembly.GetEntryAssembly().CreateInstance(ClassName); + } + catch (Exception ex) + { + + } + return rtn; + } + } + + public class ScriptMapping + { + /// + /// The name of the script as called by FastAGI + /// + public string ScriptName { get; set; } + /// + /// The class containing the AGIScript to be run + /// + public string ScriptClass{ get; set; } + /// + /// The name of the assembly to load, that contains the ScriptClass. Optional, if not specified, the class will be loaded from the current assembly + /// + public string ScriptAssmebly { get; set; } + + public static List LoadMappings(string pathToXml) + { + // Load ScriptMappings XML File + XmlSerializer xs = new XmlSerializer(typeof(List)); + try + { + using (FileStream fs = File.OpenRead(pathToXml)) + { + return (List)xs.Deserialize(fs); + } + } + catch + { + return new List(); + } + } + + public static void SaveMappings(string pathToXml, List resources) + { + // Save ScriptMappings XML File + XmlSerializer xs = new XmlSerializer(typeof(List)); + using (FileStream fs = File.Open(pathToXml, FileMode.Create, FileAccess.Write, FileShare.None)) + { + lock (resources) + { + xs.Serialize(fs, resources); + } + } + } + } + + /// + /// A MappingStrategy that is configured via a an XML file + /// or used by passing in a single or list of SciptMapping + /// This is useful as a general mapping strategy, rather than + /// using the default Resource Reference method. + /// + public class GeneralMappingStrategy : IMappingStrategy + { +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + private List mappings; + private Dictionary mapAssemblies; + + public string AGIPath = string.Empty; + + /// + /// + /// + public GeneralMappingStrategy() + { + this.mappings = null; + this.mapAssemblies = null; + } + + /// + /// + /// + /// + public GeneralMappingStrategy(List resources) + { + this.mappings = resources; + this.mapAssemblies = null; + } + + /// + /// + /// + /// + public GeneralMappingStrategy(string xmlFilePath) + { + this.mappings = ScriptMapping.LoadMappings(xmlFilePath); + this.mapAssemblies = null; + } + + public AGIScript DetermineScript(AGIRequest request) + { + AGIScript script = null; + if (mapAssemblies != null) + lock (mapAssemblies) + { + if (mapAssemblies.ContainsKey(request.Script)) + script = mapAssemblies[request.Script].CreateInstance(); + } + return script; + } + + public void Load() + { + if (mapAssemblies == null) + mapAssemblies = new Dictionary(); + lock (mapAssemblies) + { + mapAssemblies.Clear(); + try + { + foreach (var de in this.mappings) + { + MappingAssembly ma; + + if (mapAssemblies.ContainsKey(de.ScriptName)) + throw new AGIException(String.Format("Duplicate mapping name '{0}'", de.ScriptName)); + if (!string.IsNullOrEmpty(de.ScriptAssmebly)) + { + ma = new MappingAssembly() + { + ClassName = (string)de.ScriptClass, + LoadedAssembly = Assembly.LoadFile(Path.Combine(this.AGIPath, de.ScriptAssmebly)) + }; + } + else + { + ma = new MappingAssembly() + { + ClassName = (string)de.ScriptClass + }; + } + + mapAssemblies.Add(de.ScriptName, ma); + } + } + catch (Exception ex) + { + throw new Exception("No mappings were added before 'Load' method called."); + } + } + } + + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/ResourceMappingStrategy.cs b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/ResourceMappingStrategy.cs new file mode 100644 index 0000000..51cf0d4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategies/ResourceMappingStrategy.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using System.Resources; +using System.Reflection; + +namespace Asterisk.NET.FastAGI.MappingStrategies +{ + /// + /// A MappingStrategy that is configured via a resource bundle.
+ /// The resource bundle contains the script part of the url as key and the fully + /// qualified class name of the corresponding AGIScript as value.
+ /// Example: + ///
+	/// noopcommand = Asterisk.NET.FastAGI.Command.NoopCommand
+	/// 
+ /// NoopCommand must implement the AGIScript interface and have a default constructor with no parameters.
+ ///
+ public class ResourceMappingStrategy : IMappingStrategy + { +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + private string resourceName; + private Hashtable mapping; + + public ResourceMappingStrategy() + { + this.resourceName = Common.AGI_DEFAULT_RESOURCE_BUNDLE_NAME; + this.mapping = null; + } + + public ResourceMappingStrategy(string resourceName) + { + this.resourceName = resourceName; + this.mapping = null; + } + + public AGIScript DetermineScript(AGIRequest request) + { + AGIScript script = null; + if (mapping != null) + lock (mapping.SyncRoot) + { + if (mapping.Contains(request.Script)) + script = (AGIScript)mapping[request.Script]; + } + return script; + } + + public string ResourceBundleName + { + set + { + if (value == null) + { + mapping = null; + resourceName = null; + } + else if (this.resourceName != value) + { + this.resourceName = value; + Load(); + } + } + } + + public void Load() + { + string scriptName; + string className; + AGIScript agiScript; + + if (mapping == null) + mapping = new Hashtable(); + lock (mapping) + { + mapping.Clear(); + try + { + ResourceReader rr = new ResourceReader(AppDomain.CurrentDomain.BaseDirectory + resourceName); + foreach (DictionaryEntry de in rr) + { + scriptName = (string)de.Key; + className = (string)de.Value; + agiScript = CreateAGIScriptInstance(className); + if(mapping.Contains(scriptName)) + throw new AGIException(String.Format("Duplicate mapping name '{0}' in file {1}", scriptName, resourceName)); + mapping.Add(scriptName, agiScript); +#if LOGGER + logger.Info("Added mapping for '" + scriptName + "' to class " + agiScript.GetType().FullName); +#endif + } + } + catch (Exception ex) + { +#if LOGGER + logger.Error("Resource bundle '" + resourceName + "' is missing."); +#endif + throw ex; + } + } + } + + private AGIScript CreateAGIScriptInstance(string className) + { + Type agiScriptClass; + ConstructorInfo constructor; + AGIScript agiScript; + + try + { + agiScriptClass = Type.GetType(className); + constructor = agiScriptClass.GetConstructor(new Type[]{}); + agiScript = (AGIScript) constructor.Invoke(new object[]{}); + } + catch(Exception ex) + { +#if LOGGER + logger.Error("Unable to create AGIScript instance of type " + className, ex); + return null; +#else + throw new AGIException("Unable to create AGIScript instance of type " + className, ex); +#endif + } + return agiScript; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs index 3687670..a192265 100644 --- a/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs +++ b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs @@ -15,7 +15,8 @@ namespace Asterisk.NET.FastAGI /// /// NoopCommand must implement the AGIScript interface and have a default constructor with no parameters.
///
- public class MappingStrategy + [Obsolete("This class has been depreciated in favour of MappingStrategies.ResourceMappingStrategy", false)] + public class MappingStrategy : IMappingStrategy { #if LOGGER private Logger logger = Logger.Instance(); @@ -29,13 +30,13 @@ namespace Asterisk.NET.FastAGI this.mapping = null; } - public MappingStrategy(string resourceName) + public MappingStrategy(string resourceName) { this.resourceName = resourceName; this.mapping = null; } - internal AGIScript DetermineScript(AGIRequest request) + public AGIScript DetermineScript(AGIRequest request) { AGIScript script = null; if (mapping != null) @@ -64,7 +65,7 @@ namespace Asterisk.NET.FastAGI } } - internal void Load() + public void Load() { string scriptName; string className; @@ -82,7 +83,7 @@ namespace Asterisk.NET.FastAGI { scriptName = (string)de.Key; className = (string)de.Value; - agiScript = createAGIScriptInstance(className); + agiScript = CreateAGIScriptInstance(className); if(mapping.Contains(scriptName)) throw new AGIException(String.Format("Duplicate mapping name '{0}' in file {1}", scriptName, resourceName)); mapping.Add(scriptName, agiScript); @@ -101,7 +102,7 @@ namespace Asterisk.NET.FastAGI } } - private AGIScript createAGIScriptInstance(string className) + private AGIScript CreateAGIScriptInstance(string className) { Type agiScriptClass; ConstructorInfo constructor; diff --git a/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs b/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs index eeac30c..11a1338 100644 --- a/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs +++ b/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs @@ -81,6 +81,7 @@ namespace Asterisk.NET.Manager public delegate void ZapShowChannelsCompleteEventHandler(object sender, Event.ZapShowChannelsCompleteEvent e); public delegate void ZapShowChannelsEventHandler(object sender, Event.ZapShowChannelsEvent e); public delegate void ConnectionStateEventHandler(object sender, Event.ConnectionStateEvent e); + public delegate void VarSetEventHandler(object sender, Event.VarSetEvent e); #endregion @@ -414,6 +415,11 @@ namespace Asterisk.NET.Manager /// public event ConnectionStateEventHandler ConnectionState; + /// + /// When a variable is set + /// + public event VarSetEventHandler VarSet; + #endregion #region Constructor - ManagerConnection() @@ -506,6 +512,8 @@ namespace Asterisk.NET.Manager Helper.RegisterEventHandler(registeredEventHandlers, 64, typeof(TransferEvent)); Helper.RegisterEventHandler(registeredEventHandlers, 65, typeof(DTMFEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 70, typeof(VarSetEvent)); + #endregion @@ -1050,6 +1058,12 @@ namespace Asterisk.NET.Manager { DTMF(this, (DTMFEvent)e); } + break; + case 70: + if (VarSet != null) + { + VarSet(this, (VarSetEvent)e); + } break; default: @@ -1352,6 +1366,12 @@ namespace Asterisk.NET.Manager return Manager.AsteriskVersion.ASTERISK_1_6; else if (version.StartsWith("1.8.")) return Manager.AsteriskVersion.ASTERISK_1_8; + else if (version.StartsWith("10.")) + return Manager.AsteriskVersion.ASTERISK_10; + else if (version.StartsWith("11.")) + return Manager.AsteriskVersion.ASTERISK_11; + else if (version.StartsWith("12.")) + return Manager.AsteriskVersion.ASTERISK_12; else throw new ManagerException("Unknown Asterisk version " + version); } diff --git a/Asterisk.2013/ChangeLog.txt b/Asterisk.2013/ChangeLog.txt index dc89651..ce871f0 100644 --- a/Asterisk.2013/ChangeLog.txt +++ b/Asterisk.2013/ChangeLog.txt @@ -1,3 +1,14 @@ +21.08.2013 (skrusty) + Added VarSetEventHandler (added as contribution by bacobart) + Added IMappingStrategy to allow different mapping strategies to be created (added as contribution by bacobart) (*note to get documentation added for this) + Added SC_DEAD_CHANNEL result code handling (added as contribution by nuronce, as per work item 1163) + Added SC511_CAUSES_EXCEPTION and SCHANGUP_CAUSES_EXCEPTION flags to enable new behaviour (as per item 1163) + Added new GeneralMappingStrategy class to handle simpler script mappings + Added Obsolete attribute to MappingStrategy and created a new ResourceMappingStrategy to replace it + Fixed Version Parsing for versions later than Asterisk 10 (added as contribution by bacobart) + Changed Test project to use new GeneralMappingStrategy for simpler code and readability, left the existing resource file in for backwards compatibility + + 28.05.2013 (skrusty) Fix Fixed issue with SendEventGeneratingAction, see work item: 1000 (https://asternet.codeplex.com/workitem/1000)