See change log for details
This commit is contained in:
parent
7cb7869462
commit
93c5659ab7
|
@ -90,7 +90,7 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Asterisk.NET.1.6.3.1\Asterisk.NET.1.6.3.1\Asterisk.NET\Asterisk.NET.csproj">
|
||||
<ProjectReference Include="..\..\Asterisk.NET\Asterisk.NET.csproj">
|
||||
<Project>{bc6e7dba-c05a-45fe-a2a3-b1637ce16274}</Project>
|
||||
<Name>Asterisk.NET</Name>
|
||||
</ProjectReference>
|
||||
|
|
|
@ -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<ScriptMapping>()
|
||||
{
|
||||
new ScriptMapping() {
|
||||
ScriptClass = "Asterisk.NET.Test.CustomIVR",
|
||||
ScriptName = "customivr"
|
||||
}
|
||||
});
|
||||
|
||||
//agi.SC511_CAUSES_EXCEPTION = true;
|
||||
//agi.SCHANGUP_CAUSES_EXCEPTION = true;
|
||||
|
||||
agi.Start();
|
||||
}
|
||||
#endregion
|
||||
|
|
|
@ -115,6 +115,9 @@
|
|||
<Compile Include="FastAGI\Command\WaitForDigitCommand.cs" />
|
||||
<Compile Include="FastAGI\Exceptions\InvalidCommandSyntaxException.cs" />
|
||||
<Compile Include="FastAGI\Exceptions\InvalidOrUnknownCommandException.cs" />
|
||||
<Compile Include="FastAGI\IMappingStrategy.cs" />
|
||||
<Compile Include="FastAGI\MappingStrategies\GeneralMappingStrategy.cs" />
|
||||
<Compile Include="FastAGI\MappingStrategies\ResourceMappingStrategy.cs" />
|
||||
<Compile Include="FastAGI\MappingStrategy.cs" />
|
||||
<Compile Include="FastAGI\Script\AGINoAction.cs" />
|
||||
<Compile Include="IO\ServerSocket.cs" />
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Asterisk.NET
|
|||
/// <summary>Line separator</summary>
|
||||
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[] { '-' };
|
||||
|
|
|
@ -9,16 +9,26 @@ namespace Asterisk.NET.FastAGI
|
|||
private AGIReader agiReader;
|
||||
private AGIReply agiReply;
|
||||
|
||||
public AGIChannel(IO.SocketConnection socket)
|
||||
private bool _SC511_CAUSES_EXCEPTION = false;
|
||||
private bool _SCHANGUP_CAUSES_EXCEPTION = false;
|
||||
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
/// <summary>
|
||||
|
@ -39,10 +41,12 @@ namespace Asterisk.NET.FastAGI
|
|||
/// </summary>
|
||||
/// <param name="socket">the socket connection to handle.</param>
|
||||
/// <param name="mappingStrategy">the strategy to use to determine which script to run.</param>
|
||||
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);
|
||||
|
||||
|
|
|
@ -19,6 +19,11 @@ namespace Asterisk.NET.FastAGI
|
|||
/// </summary>
|
||||
SC_INVALID_OR_UNKNOWN_COMMAND = 510,
|
||||
/// <summary>
|
||||
/// Status code (511) indicating Asterisk was unable to process the
|
||||
/// AGICommand because the channel is dead.
|
||||
/// </summary>
|
||||
SC_DEAD_CHANNEL = 511,
|
||||
/// <summary>
|
||||
/// Status code (520) indicating Asterisk was unable to process the
|
||||
/// AGICommand because the syntax used was not correct. This is most likely
|
||||
/// due to missing required parameters or additional parameters sent that are
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Asterisk.NET.FastAGI.MappingStrategies;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
@ -6,6 +7,21 @@ namespace Asterisk.NET.FastAGI
|
|||
{
|
||||
public class AsteriskFastAGI
|
||||
{
|
||||
|
||||
#region Flags
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool SC511_CAUSES_EXCEPTION = false;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool SCHANGUP_CAUSES_EXCEPTION = false;
|
||||
#endregion
|
||||
|
||||
#region Variables
|
||||
#if LOGGER
|
||||
private Logger logger = Logger.Instance();
|
||||
|
@ -25,7 +41,7 @@ namespace Asterisk.NET.FastAGI
|
|||
/// <summary>
|
||||
/// The strategy to use for bind AGIRequests to AGIScripts that serve them.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
/// <seealso cref="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,7 +113,28 @@ 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()
|
||||
/// <summary>
|
||||
/// Creates a new AsteriskFastAGI.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
8
Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs
Normal file
8
Asterisk.2013/Asterisk.NET/FastAGI/IMappingStrategy.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Asterisk.NET.FastAGI
|
||||
{
|
||||
public interface IMappingStrategy
|
||||
{
|
||||
AGIScript DetermineScript(AGIRequest request);
|
||||
void Load();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the script as called by FastAGI
|
||||
/// </summary>
|
||||
public string ScriptName { get; set; }
|
||||
/// <summary>
|
||||
/// The class containing the AGIScript to be run
|
||||
/// </summary>
|
||||
public string ScriptClass{ get; set; }
|
||||
/// <summary>
|
||||
/// The name of the assembly to load, that contains the ScriptClass. Optional, if not specified, the class will be loaded from the current assembly
|
||||
/// </summary>
|
||||
public string ScriptAssmebly { get; set; }
|
||||
|
||||
public static List<ScriptMapping> LoadMappings(string pathToXml)
|
||||
{
|
||||
// Load ScriptMappings XML File
|
||||
XmlSerializer xs = new XmlSerializer(typeof(List<ScriptMapping>));
|
||||
try
|
||||
{
|
||||
using (FileStream fs = File.OpenRead(pathToXml))
|
||||
{
|
||||
return (List<ScriptMapping>)xs.Deserialize(fs);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<ScriptMapping>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveMappings(string pathToXml, List<ScriptMapping> resources)
|
||||
{
|
||||
// Save ScriptMappings XML File
|
||||
XmlSerializer xs = new XmlSerializer(typeof(List<ScriptMapping>));
|
||||
using (FileStream fs = File.Open(pathToXml, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
lock (resources)
|
||||
{
|
||||
xs.Serialize(fs, resources);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class GeneralMappingStrategy : IMappingStrategy
|
||||
{
|
||||
#if LOGGER
|
||||
private Logger logger = Logger.Instance();
|
||||
#endif
|
||||
private List<ScriptMapping> mappings;
|
||||
private Dictionary<string, MappingAssembly> mapAssemblies;
|
||||
|
||||
public string AGIPath = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public GeneralMappingStrategy()
|
||||
{
|
||||
this.mappings = null;
|
||||
this.mapAssemblies = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="resources"></param>
|
||||
public GeneralMappingStrategy(List<ScriptMapping> resources)
|
||||
{
|
||||
this.mappings = resources;
|
||||
this.mapAssemblies = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="xmlFilePath"></param>
|
||||
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<string, MappingAssembly>();
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Asterisk.NET.FastAGI.MappingStrategies
|
||||
{
|
||||
/// <summary>
|
||||
/// A MappingStrategy that is configured via a resource bundle.<br/>
|
||||
/// The resource bundle contains the script part of the url as key and the fully
|
||||
/// qualified class name of the corresponding AGIScript as value.<br/>
|
||||
/// Example:
|
||||
/// <pre>
|
||||
/// noopcommand = Asterisk.NET.FastAGI.Command.NoopCommand
|
||||
/// </pre>
|
||||
/// NoopCommand must implement the AGIScript interface and have a default constructor with no parameters.<br/>
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ namespace Asterisk.NET.FastAGI
|
|||
/// </pre>
|
||||
/// NoopCommand must implement the AGIScript interface and have a default constructor with no parameters.<br/>
|
||||
/// </summary>
|
||||
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();
|
||||
|
@ -35,7 +36,7 @@ namespace Asterisk.NET.FastAGI
|
|||
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;
|
||||
|
|
|
@ -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
|
|||
/// </summary>
|
||||
public event ConnectionStateEventHandler ConnectionState;
|
||||
|
||||
/// <summary>
|
||||
/// When a variable is set
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue