Welcome to your new home Asterisk .NET. Thanks to dnz2005, x893 for their great effort. Visit https://sourceforge.net/projects/asterisk-dotnet/ for more information.

This commit is contained in:
ppumkin_cp 2013-01-18 07:55:50 -08:00
parent f27c35ca1a
commit f6d419aa39
248 changed files with 19520 additions and 0 deletions

View file

@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{80ADC18F-2BFC-4B79-B264-5244E4F2FEED}"
ProjectSection(SolutionItems) = preProject
ChangeLog.txt = ChangeLog.txt
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Asterisk.NET", "Asterisk.NET\Asterisk.NET.csproj", "{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Asterisk.NET.WinForm", "Asterisk.NET.WinForm\Asterisk.NET.WinForm.csproj", "{03687626-613A-4E41-8F60-7C7839D6DD5D}"
EndProject
Global
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 3
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs33
SccLocalPath0 = .
SccProjectUniqueName1 = Asterisk.NET.WinForm\\Asterisk.NET.WinForm.csproj
SccProjectName1 = Asterisk.NET.WinForm
SccLocalPath1 = Asterisk.NET.WinForm
SccProjectUniqueName2 = Asterisk.NET\\Asterisk.NET.csproj
SccProjectName2 = Asterisk.NET
SccLocalPath2 = Asterisk.NET
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}.Release|Any CPU.Build.0 = Release|Any CPU
{03687626-613A-4E41-8F60-7C7839D6DD5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03687626-613A-4E41-8F60-7C7839D6DD5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03687626-613A-4E41-8F60-7C7839D6DD5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03687626-613A-4E41-8F60-7C7839D6DD5D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
}

View file

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{03687626-613A-4E41-8F60-7C7839D6DD5D}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Asterisk.NET.WinForm</RootNamespace>
<AssemblyName>Asterisk.NET.WinForm</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>2.0</OldToolsVersion>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="FormMain.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="FormMain.Designer.cs">
<DependentUpon>FormMain.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="FormMain.resx">
<SubType>Designer</SubType>
<DependentUpon>FormMain.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="app.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Asterisk.NET\Asterisk.NET.csproj">
<Project>{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}</Project>
<Name>Asterisk.NET</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View file

@ -0,0 +1,188 @@
namespace Asterisk.NET.WinForm
{
partial class FormMain
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lable1 = new System.Windows.Forms.Label();
this.tbAddress = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.btnDisconnect = new System.Windows.Forms.Button();
this.btnConnect = new System.Windows.Forms.Button();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.tbUser = new System.Windows.Forms.TextBox();
this.tbPassword = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.tbPort = new System.Windows.Forms.TextBox();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// lable1
//
this.lable1.AutoSize = true;
this.lable1.Location = new System.Drawing.Point(6, 24);
this.lable1.Name = "lable1";
this.lable1.Size = new System.Drawing.Size(29, 13);
this.lable1.TabIndex = 0;
this.lable1.Text = "Host";
//
// tbAddress
//
this.tbAddress.Location = new System.Drawing.Point(94, 19);
this.tbAddress.Name = "tbAddress";
this.tbAddress.Size = new System.Drawing.Size(100, 20);
this.tbAddress.TabIndex = 1;
this.tbAddress.Text = "192.168.2.51";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.btnDisconnect);
this.groupBox1.Controls.Add(this.btnConnect);
this.groupBox1.Controls.Add(this.label4);
this.groupBox1.Controls.Add(this.label3);
this.groupBox1.Controls.Add(this.tbUser);
this.groupBox1.Controls.Add(this.tbPassword);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.tbPort);
this.groupBox1.Controls.Add(this.lable1);
this.groupBox1.Controls.Add(this.tbAddress);
this.groupBox1.Location = new System.Drawing.Point(1, 1);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(200, 150);
this.groupBox1.TabIndex = 2;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Parameters connection";
//
// btnDisconnect
//
this.btnDisconnect.Enabled = false;
this.btnDisconnect.Location = new System.Drawing.Point(94, 123);
this.btnDisconnect.Name = "btnDisconnect";
this.btnDisconnect.Size = new System.Drawing.Size(100, 23);
this.btnDisconnect.TabIndex = 9;
this.btnDisconnect.Text = "Disconnect";
this.btnDisconnect.UseVisualStyleBackColor = true;
this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
//
// btnConnect
//
this.btnConnect.Location = new System.Drawing.Point(9, 123);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new System.Drawing.Size(75, 23);
this.btnConnect.TabIndex = 8;
this.btnConnect.Text = "Connect";
this.btnConnect.UseVisualStyleBackColor = true;
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(6, 102);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(53, 13);
this.label4.TabIndex = 6;
this.label4.Text = "Password";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(6, 76);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 13);
this.label3.TabIndex = 4;
this.label3.Text = "User";
//
// tbUser
//
this.tbUser.Location = new System.Drawing.Point(94, 71);
this.tbUser.Name = "tbUser";
this.tbUser.Size = new System.Drawing.Size(100, 20);
this.tbUser.TabIndex = 5;
this.tbUser.Text = "admin";
//
// tbPassword
//
this.tbPassword.Location = new System.Drawing.Point(94, 97);
this.tbPassword.Name = "tbPassword";
this.tbPassword.Size = new System.Drawing.Size(100, 20);
this.tbPassword.TabIndex = 7;
this.tbPassword.Text = "123Test";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 50);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(26, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Port";
//
// tbPort
//
this.tbPort.Location = new System.Drawing.Point(94, 45);
this.tbPort.Name = "tbPort";
this.tbPort.Size = new System.Drawing.Size(100, 20);
this.tbPort.TabIndex = 3;
this.tbPort.Text = "5038";
//
// FormMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(203, 155);
this.Controls.Add(this.groupBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormMain";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Client for Asterisk";
this.TopMost = true;
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label lable1;
private System.Windows.Forms.TextBox tbAddress;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox tbUser;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox tbPort;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox tbPassword;
private System.Windows.Forms.Button btnConnect;
private System.Windows.Forms.Button btnDisconnect;
}
}

View file

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Asterisk.NET.Manager;
using Asterisk.NET.Manager.Event;
using System.Diagnostics;
namespace Asterisk.NET.WinForm
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private ManagerConnection manager = null;
private void btnConnect_Click(object sender, EventArgs e)
{
string address = this.tbAddress.Text;
int port = int.Parse(this.tbPort.Text);
string user = this.tbUser.Text;
string password = this.tbPassword.Text;
btnConnect.Enabled = false;
manager = new ManagerConnection(address, port, user, password);
manager.UnhandledEvent += new ManagerEventHandler(manager_Events);
try
{
// Uncomment next 2 line comments to Disable timeout (debug mode)
// manager.DefaultResponseTimeout = 0;
// manager.DefaultEventTimeout = 0;
manager.Login();
}
catch(Exception ex)
{
MessageBox.Show("Error connect\n" + ex.Message);
manager.Logoff();
this.Close();
}
btnDisconnect.Enabled = true;
}
void manager_Events(object sender, ManagerEvent e)
{
Debug.WriteLine("Event : " + e.GetType().Name);
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
btnConnect.Enabled = true;
if (this.manager != null)
{
manager.Logoff();
this.manager = null;
}
btnDisconnect.Enabled = false;
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Asterisk.NET.WinForm
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormMain());
}
}
}

View file

@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Asterisk.NET.WinForm")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("...")]
[assembly: AssemblyProduct("Asterisk.NET.WinForm")]
[assembly: AssemblyCopyright("Copyright © ... 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("21011cfa-cef9-4bcb-8e71-e9ab2a16f81b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Asterisk.NET.WinForm.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Asterisk.NET.WinForm.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.17929
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Asterisk.NET.WinForm.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View file

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View file

@ -0,0 +1,3 @@
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>

View file

@ -0,0 +1,324 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BC6E7DBA-C05A-45FE-A2A3-B1637CE16274}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Asterisk.NET</RootNamespace>
<AssemblyName>Asterisk.NET</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>2.0</OldToolsVersion>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOGGER</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Common.cs" />
<Compile Include="FastAGI\AGIChannel.cs" />
<Compile Include="FastAGI\AGIConnectionHandler.cs" />
<Compile Include="FastAGI\Exceptions\AGIException.cs" />
<Compile Include="FastAGI\Exceptions\AGIHangupException.cs" />
<Compile Include="FastAGI\Exceptions\AGINetworkException.cs" />
<Compile Include="FastAGI\AGIReader.cs" />
<Compile Include="FastAGI\AGIReply.cs" />
<Compile Include="FastAGI\AGIRequest.cs" />
<Compile Include="FastAGI\AsteriskFastAGI.cs" />
<Compile Include="FastAGI\Command\AGICommand.cs" />
<Compile Include="FastAGI\Command\GetFullVariableCommand.cs" />
<Compile Include="FastAGI\Command\ReceiveTextCommand.cs" />
<Compile Include="FastAGI\Command\SayDateTimeCommand.cs" />
<Compile Include="FastAGI\AGIWriter.cs" />
<Compile Include="FastAGI\AGIScript.cs" />
<Compile Include="FastAGI\Command\AnswerCommand.cs" />
<Compile Include="FastAGI\Command\ChannelStatusCommand.cs" />
<Compile Include="FastAGI\Command\ControlStreamFileCommand.cs" />
<Compile Include="FastAGI\Command\DatabaseDelCommand.cs" />
<Compile Include="FastAGI\Command\DatabaseDelTreeCommand.cs" />
<Compile Include="FastAGI\Command\DatabaseGetCommand.cs" />
<Compile Include="FastAGI\Command\DatabasePutCommand.cs" />
<Compile Include="FastAGI\Command\ExecCommand.cs" />
<Compile Include="FastAGI\Command\GetDataCommand.cs" />
<Compile Include="FastAGI\Command\GetOptionCommand.cs" />
<Compile Include="FastAGI\Command\GetVariableCommand.cs" />
<Compile Include="FastAGI\Command\HangupCommand.cs" />
<Compile Include="FastAGI\Command\NoopCommand.cs" />
<Compile Include="FastAGI\Command\ReceiveCharCommand.cs" />
<Compile Include="FastAGI\Command\RecordFileCommand.cs" />
<Compile Include="FastAGI\Command\SayAlphaCommand.cs" />
<Compile Include="FastAGI\Command\SayDigitsCommand.cs" />
<Compile Include="FastAGI\Command\SayNumberCommand.cs" />
<Compile Include="FastAGI\Command\SayPhoneticCommand.cs" />
<Compile Include="FastAGI\Command\SayTimeCommand.cs" />
<Compile Include="FastAGI\Command\SendImageCommand.cs" />
<Compile Include="FastAGI\Command\SendTextCommand.cs" />
<Compile Include="FastAGI\Command\SetAutoHangupCommand.cs" />
<Compile Include="FastAGI\Command\SetCallerIdCommand.cs" />
<Compile Include="FastAGI\Command\SetContextCommand.cs" />
<Compile Include="FastAGI\Command\SetExtensionCommand.cs" />
<Compile Include="FastAGI\Command\SetMusicOffCommand.cs" />
<Compile Include="FastAGI\Command\SetMusicOnCommand.cs" />
<Compile Include="FastAGI\Command\SetPriorityCommand.cs" />
<Compile Include="FastAGI\Command\SetVariableCommand.cs" />
<Compile Include="FastAGI\Command\StreamFileCommand.cs" />
<Compile Include="FastAGI\Command\TDDModeCommand.cs" />
<Compile Include="FastAGI\Command\VerboseCommand.cs" />
<Compile Include="FastAGI\Command\WaitForDigitCommand.cs" />
<Compile Include="FastAGI\Exceptions\InvalidCommandSyntaxException.cs" />
<Compile Include="FastAGI\Exceptions\InvalidOrUnknownCommandException.cs" />
<Compile Include="FastAGI\MappingStrategy.cs" />
<Compile Include="FastAGI\Script\AGINoAction.cs" />
<Compile Include="IO\ServerSocket.cs" />
<Compile Include="IO\SocketConnection.cs" />
<Compile Include="IParseSupport.cs" />
<Compile Include="Manager\Action\AbsoluteTimeoutAction.cs" />
<Compile Include="Manager\Action\AgentCallbackLoginAction.cs" />
<Compile Include="Manager\Action\AgentLogoffAction.cs" />
<Compile Include="Manager\Action\AgentsAction.cs" />
<Compile Include="Manager\Action\ChallengeAction.cs" />
<Compile Include="Manager\Action\ChangeMonitorAction.cs" />
<Compile Include="Manager\Action\CommandAction.cs" />
<Compile Include="Manager\Action\DBGetAction.cs" />
<Compile Include="Manager\Action\DBPutAction.cs" />
<Compile Include="Manager\Action\EventsAction.cs" />
<Compile Include="Manager\Action\ExtensionStateAction.cs" />
<Compile Include="Manager\Action\GetConfigAction.cs" />
<Compile Include="Manager\Action\GetVarAction.cs" />
<Compile Include="Manager\Action\HangupAction.cs" />
<Compile Include="Manager\Action\ManagerActionEvent.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Manager\Action\LoginAction.cs" />
<Compile Include="Manager\Action\LogoffAction.cs" />
<Compile Include="Manager\Action\MailboxCountAction.cs" />
<Compile Include="Manager\Action\MailboxStatusAction.cs" />
<Compile Include="Manager\Action\ManagerAction.cs" />
<Compile Include="Manager\Action\ManagerActionResponse.cs" />
<Compile Include="Manager\Action\MonitorAction.cs" />
<Compile Include="Manager\Action\OriginateAction.cs" />
<Compile Include="Manager\Action\ParkedCallsAction.cs" />
<Compile Include="Manager\Action\PingAction.cs" />
<Compile Include="Manager\Action\ProxyAction.cs" />
<Compile Include="Manager\Action\QueueAddAction.cs" />
<Compile Include="Manager\Action\QueuePauseAction.cs" />
<Compile Include="Manager\Action\QueueRemoveAction.cs" />
<Compile Include="Manager\Action\QueueStatusAction.cs" />
<Compile Include="Manager\Action\RedirectAction.cs" />
<Compile Include="Manager\Action\SetCDRUserFieldAction.cs" />
<Compile Include="Manager\Action\SetVarAction.cs" />
<Compile Include="Manager\Action\SIPPeersAction.cs" />
<Compile Include="Manager\Action\SIPShowPeerAction.cs" />
<Compile Include="Manager\Action\StatusAction.cs" />
<Compile Include="Manager\Action\StopMonitorAction.cs" />
<Compile Include="Manager\Action\UpdateConfigAction.cs" />
<Compile Include="Manager\Action\ZapDialOffhookAction.cs" />
<Compile Include="Manager\Action\ZapDNDOffAction.cs" />
<Compile Include="Manager\Action\ZapDNDOnAction.cs" />
<Compile Include="Manager\Action\ZapHangupAction.cs" />
<Compile Include="Manager\Action\ZapShowChannelsAction.cs" />
<Compile Include="Manager\Action\ZapTransferAction.cs" />
<Compile Include="Manager\AsteriskVersion.cs" />
<Compile Include="Manager\Event\AbstractAgentVariables.cs" />
<Compile Include="Manager\Event\ConnectionStateEvent.cs" />
<Compile Include="Manager\Event\AGIExecEvent.cs" />
<Compile Include="Manager\Event\AsyncAGIEvent.cs" />
<Compile Include="Manager\Event\BridgeEvent.cs" />
<Compile Include="Manager\Event\ChannelReloadEvent.cs" />
<Compile Include="Manager\Event\ChannelUpdateEvent.cs" />
<Compile Include="Manager\Event\MeetmeMuteEvent.cs" />
<Compile Include="Manager\Event\DTMFEvent.cs" />
<Compile Include="Manager\Event\FaxReceivedEvent.cs" />
<Compile Include="Manager\Event\JabberEvent.cs" />
<Compile Include="Manager\Event\JitterBufStatsEvent.cs" />
<Compile Include="Manager\Event\MeetmeEndEvent.cs" />
<Compile Include="Manager\Event\MeetmeTalkRequestEvent.cs" />
<Compile Include="Manager\Event\MobileStatusEvent.cs" />
<Compile Include="Manager\Event\ModuleLoadReportEvent.cs" />
<Compile Include="Manager\Event\MonitorStartEvent.cs" />
<Compile Include="Manager\Event\MonitorStopEvent.cs" />
<Compile Include="Manager\Event\NewAccountCodeEvent.cs" />
<Compile Include="Manager\Event\OriginateResponseEvent.cs" />
<Compile Include="Manager\Event\PRIEvent.cs" />
<Compile Include="Manager\Event\QueueMemberPenaltyEvent.cs" />
<Compile Include="Manager\Event\RTPReceiverStatEvent.cs" />
<Compile Include="Manager\Event\RTCPSentEvent.cs" />
<Compile Include="Manager\Event\RTCPReceivedEvent.cs" />
<Compile Include="Manager\Event\RTPSenderStatEvent.cs" />
<Compile Include="Manager\Event\ShowDialPlanCompleteEvent.cs" />
<Compile Include="Manager\Event\TransferEvent.cs" />
<Compile Include="Manager\Event\UnknownEvent.cs" />
<Compile Include="Manager\Event\VarSet.cs" />
<Compile Include="Manager\Event\QueueCallerAbandonEvent.cs" />
<Compile Include="Manager\Event\MasqueradeEvent.cs" />
<Compile Include="Manager\Exceptions\AuthenticationFailedException.cs" />
<Compile Include="Manager\Response\GetConfigResponse.cs" />
<Compile Include="Manager\Response\OriginateResponse.cs" />
<Compile Include="Manager\ManagerConnection.cs" />
<Compile Include="Manager\Exceptions\EventTimeoutException.cs" />
<Compile Include="Manager\Event\AbstractAgentEvent.cs" />
<Compile Include="Manager\Event\AbstractParkedCallEvent.cs" />
<Compile Include="Manager\Event\AbstractQueueMemberEvent.cs" />
<Compile Include="Manager\Event\AgentCallbackLoginEvent.cs" />
<Compile Include="Manager\Event\AgentCallbackLogoffEvent.cs" />
<Compile Include="Manager\Event\AgentCalledEvent.cs" />
<Compile Include="Manager\Event\AgentCompleteEvent.cs" />
<Compile Include="Manager\Event\AgentConnectEvent.cs" />
<Compile Include="Manager\Event\AgentDumpEvent.cs" />
<Compile Include="Manager\Event\AgentLoginEvent.cs" />
<Compile Include="Manager\Event\AgentLogoffEvent.cs" />
<Compile Include="Manager\Event\AgentsCompleteEvent.cs" />
<Compile Include="Manager\Event\AgentsEvent.cs" />
<Compile Include="Manager\Event\AlarmClearEvent.cs" />
<Compile Include="Manager\Event\AlarmEvent.cs" />
<Compile Include="Manager\Event\CdrEvent.cs" />
<Compile Include="Manager\Event\AbstractChannelEvent.cs" />
<Compile Include="Manager\Event\ConnectEvent.cs" />
<Compile Include="Manager\Event\DBGetResponseEvent.cs" />
<Compile Include="Manager\Event\DialEvent.cs" />
<Compile Include="Manager\Event\DisconnectEvent.cs" />
<Compile Include="Manager\Event\DNDStateEvent.cs" />
<Compile Include="Manager\Event\ExtensionStatusEvent.cs" />
<Compile Include="Manager\Event\HangupEvent.cs" />
<Compile Include="Manager\Event\HoldedCallEvent.cs" />
<Compile Include="Manager\Event\HoldEvent.cs" />
<Compile Include="Manager\Event\JoinEvent.cs" />
<Compile Include="Manager\Event\LeaveEvent.cs" />
<Compile Include="Manager\Event\LinkEvent.cs" />
<Compile Include="Manager\Event\LogChannelEvent.cs" />
<Compile Include="Manager\Event\ManagerEvent.cs" />
<Compile Include="Manager\Event\AbstractMeetmeEvent.cs" />
<Compile Include="Manager\Event\MeetmeJoinEvent.cs" />
<Compile Include="Manager\Event\MeetmeLeaveEvent.cs" />
<Compile Include="Manager\Event\MeetmeStopTalkingEvent.cs" />
<Compile Include="Manager\Event\MeetmeTalkingEvent.cs" />
<Compile Include="Manager\Event\MessageWaitingEvent.cs" />
<Compile Include="Manager\Event\NewCallerIdEvent.cs" />
<Compile Include="Manager\Event\NewChannelEvent.cs" />
<Compile Include="Manager\Event\NewExtenEvent.cs" />
<Compile Include="Manager\Event\NewStateEvent.cs" />
<Compile Include="Manager\Event\ParkedCallEvent.cs" />
<Compile Include="Manager\Event\ParkedCallGiveUpEvent.cs" />
<Compile Include="Manager\Event\ParkedCallsCompleteEvent.cs" />
<Compile Include="Manager\Event\ParkedCallTimeOutEvent.cs" />
<Compile Include="Manager\Event\PeerEntryEvent.cs" />
<Compile Include="Manager\Event\PeerlistCompleteEvent.cs" />
<Compile Include="Manager\Event\PeerStatusEvent.cs" />
<Compile Include="Manager\Event\QueueEntryEvent.cs" />
<Compile Include="Manager\Event\QueueEvent.cs" />
<Compile Include="Manager\Event\QueueMemberAddedEvent.cs" />
<Compile Include="Manager\Event\QueueMemberEvent.cs" />
<Compile Include="Manager\Event\QueueMemberPausedEvent.cs" />
<Compile Include="Manager\Event\QueueMemberRemovedEvent.cs" />
<Compile Include="Manager\Event\QueueMemberStatusEvent.cs" />
<Compile Include="Manager\Event\QueueParamsEvent.cs" />
<Compile Include="Manager\Event\QueueStatusCompleteEvent.cs" />
<Compile Include="Manager\Event\RegistryEvent.cs" />
<Compile Include="Manager\Event\ReloadEvent.cs" />
<Compile Include="Manager\Event\RenameEvent.cs" />
<Compile Include="Manager\Event\ResponseEvent.cs" />
<Compile Include="Manager\Event\ShutdownEvent.cs" />
<Compile Include="Manager\Event\StatusCompleteEvent.cs" />
<Compile Include="Manager\Event\StatusEvent.cs" />
<Compile Include="Manager\Event\UnholdEvent.cs" />
<Compile Include="Manager\Event\UnlinkEvent.cs" />
<Compile Include="Manager\Event\UnparkedCallEvent.cs" />
<Compile Include="Manager\Event\UserEvent.cs" />
<Compile Include="Manager\Event\ZapShowChannelsCompleteEvent.cs" />
<Compile Include="Manager\Event\ZapShowChannelsEvent.cs" />
<Compile Include="Manager\Exceptions\ManagerException.cs" />
<Compile Include="Manager\ManagerReader.cs" />
<Compile Include="Manager\Originate.cs" />
<Compile Include="Manager\ResponseEventHandler.cs" />
<Compile Include="Manager\ResponseEvents.cs" />
<Compile Include="Manager\ResponseHandler.cs" />
<Compile Include="Manager\Response\ChallengeResponse.cs" />
<Compile Include="Manager\Response\CommandResponse.cs" />
<Compile Include="Manager\Response\ExtensionStateResponse.cs" />
<Compile Include="Manager\IResponseHandler.cs" />
<Compile Include="Manager\Response\MailboxCountResponse.cs" />
<Compile Include="Manager\Response\MailboxStatusResponse.cs" />
<Compile Include="Manager\Response\ManagerError.cs" />
<Compile Include="Manager\Response\ManagerResponse.cs" />
<Compile Include="Manager\Exceptions\TimeoutException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Util\MD5Support.cs" />
<Compile Include="Util\ThreadClass.cs" />
<Compile Include="Util\ThreadPool.cs" />
<Compile Include="Helper.cs" />
<Compile Include="Util\ThreadTask.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}

View file

@ -0,0 +1,55 @@
using System;
using System.Text.RegularExpressions;
using System.Globalization;
namespace Asterisk.NET
{
public static class Common
{
#region Manager API Constants
/// <summary> The hostname to use if none is provided.</summary>
public const string DEFAULT_HOSTNAME = "localhost";
/// <summary> The port to use if none is provided.</summary>
public const int DEFAULT_PORT = 5038;
/// <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 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[] { '-' };
public static char INTERNAL_ACTION_ID_DELIMITER = '#';
/// <summary> Variables delimiter </summary>
public static char[] VAR_DELIMITER = new char[] { '|' };
public static IFormatProvider CultureInfoEn = new CultureInfo("en-US", false);
#endregion
#region AGI Constants
/// <summary> The default AGI bind port. </summary>
public const int AGI_BIND_PORT = 4573;
/// <summary> The default AGI thread pool size. </summary>
public const int AGI_POOL_SIZE = 10;
/// <summary> The default AGI bind address. </summary>
public const string AGI_BIND_ADDRESS = "0.0.0.0";
public const string AGI_DEFAULT_RESOURCE_BUNDLE_NAME = "fastagi-mapping.resources";
public const string AGI_END_OF_PROPER_USAGE = "520 End of proper usage.";
public const int AGI_DEFAULT_MAX_DIGITS = 1024;
public const int AGI_DEFAULT_TIMEOUT = 0;
public static Regex AGI_STATUS_PATTERN = new Regex("^(\\d{3})[ -]", RegexOptions.Compiled);
public static Regex AGI_RESULT_PATTERN = new Regex("^200 result= *(\\S+)", RegexOptions.Compiled);
public static Regex AGI_PARENTHESIS_PATTERN = new Regex("^200 result=\\S* +\\((.*)\\)", RegexOptions.Compiled);
public static Regex AGI_ADDITIONAL_ATTRIBUTES_PATTERN = new Regex("^200 result=\\S* +(\\(.*\\) )?(.+)$", RegexOptions.Compiled);
public static Regex AGI_ADDITIONAL_ATTRIBUTE_PATTERN = new Regex("(\\S+)=(\\S+)", RegexOptions.Compiled);
public static Regex AGI_SYNOPSIS_PATTERN = new Regex("^\\s*Usage:\\s*(.*)\\s*$", RegexOptions.Compiled);
public static Regex AGI_SCRIPT_PATTERN = new Regex("^([^\\?]*)\\?(.*)$");
public static Regex AGI_PARAMETER_PATTERN = new Regex("^(.*)=(.*)$");
#endregion
}
}

View file

@ -0,0 +1,44 @@
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// Default implementation of the AGIChannel interface.
/// </summary>
public class AGIChannel
{
private AGIWriter agiWriter;
private AGIReader agiReader;
private AGIReply agiReply;
public AGIChannel(IO.SocketConnection socket)
{
this.agiWriter = new AGIWriter(socket);
this.agiReader = new AGIReader(socket);
}
public AGIChannel(AGIWriter agiWriter, AGIReader agiReader)
{
this.agiWriter = agiWriter;
this.agiReader = agiReader;
}
/// <summary>
/// Get last AGI Reply.
/// </summary>
public AGIReply LastReply
{
get { return agiReply; }
}
public AGIReply SendCommand(Command.AGICommand command)
{
agiWriter.SendCommand(command);
agiReply = agiReader.ReadReply();
int status = agiReply.GetStatus();
if (status == (int)AGIReplyStatuses.SC_INVALID_OR_UNKNOWN_COMMAND)
throw new InvalidOrUnknownCommandException(command.BuildCommand());
else if (status == (int)AGIReplyStatuses.SC_INVALID_COMMAND_SYNTAX)
throw new InvalidCommandSyntaxException(agiReply.GetSynopsis(), agiReply.GetUsage());
return agiReply;
}
}
}

View file

@ -0,0 +1,118 @@
using System;
using System.IO;
using System.Threading;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// An AGIConnectionHandler is created and run by the AGIServer whenever a new
/// socket connection from an Asterisk Server is received.<br/>
/// It reads the request using an AGIReader and runs the AGIScript configured to
/// handle this type of request. Finally it closes the socket connection.
/// </summary>
public class AGIConnectionHandler
{
#if LOGGER
private Logger logger = Logger.Instance();
#endif
private static readonly LocalDataStoreSlot channel = Thread.AllocateDataSlot();
private IO.SocketConnection socket;
private MappingStrategy mappingStrategy;
#region Channel
/// <summary>
/// Returns the AGIChannel associated with the current thread.
/// </summary>
/// <returns>the AGIChannel associated with the current thread or <code>null</code> if none is associated.</returns>
internal static AGIChannel Channel
{
get
{
return (AGIChannel) Thread.GetData(AGIConnectionHandler.channel);
}
}
#endregion
#region AGIConnectionHandler(socket, mappingStrategy)
/// <summary>
/// Creates a new AGIConnectionHandler to handle the given socket connection.
/// </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)
{
this.socket = socket;
this.mappingStrategy = mappingStrategy;
}
#endregion
public void Run()
{
try
{
AGIReader reader = new AGIReader(socket);
AGIWriter writer = new AGIWriter(socket);
AGIRequest request = reader.ReadRequest();
AGIChannel channel = new AGIChannel(writer, reader);
AGIScript script = mappingStrategy.DetermineScript(request);
Thread.SetData(AGIConnectionHandler.channel, channel);
if (script != null)
{
#if LOGGER
logger.Info("Begin AGIScript " + script.GetType().FullName + " on " + Thread.CurrentThread.Name);
#endif
script.Service(request, channel);
#if LOGGER
logger.Info("End AGIScript " + script.GetType().FullName + " on " + Thread.CurrentThread.Name);
#endif
}
else
{
string error;
error = "No script configured for URL '" + request.RequestURL + "' (script '" + request.Script + "')";
channel.SendCommand(new Command.VerboseCommand(error, 1));
#if LOGGER
logger.Error(error);
#endif
}
}
catch (AGIHangupException)
{
}
catch (IOException)
{
}
catch (AGIException ex)
{
#if LOGGER
logger.Error("AGIException while handling request", ex);
#else
throw ex;
#endif
}
catch (Exception ex)
{
#if LOGGER
logger.Error("Unexpected Exception while handling request", ex);
#else
throw ex;
#endif
}
Thread.SetData(AGIConnectionHandler.channel, null);
try
{
socket.Close();
}
#if LOGGER
catch(IOException ex)
{
logger.Error("Error on close socket", ex);
}
#else
catch { }
#endif
}
}
}

View file

@ -0,0 +1,89 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace Asterisk.NET.FastAGI
{
public class AGIReader
{
#if LOGGER
private Logger logger = Logger.Instance();
#endif
private IO.SocketConnection socket;
public AGIReader(IO.SocketConnection socket)
{
this.socket = socket;
}
public AGIRequest ReadRequest()
{
string line;
List<string> lines = new List<string>();
try
{
#if LOGGER
logger.Info("AGIReader.ReadRequest():");
#endif
while ((line = socket.ReadLine()) != null)
{
if (line.Length == 0)
break;
lines.Add(line);
#if LOGGER
logger.Info(line);
#endif
}
}
catch (IOException ex)
{
throw new AGINetworkException("Unable to read request from Asterisk: " + ex.Message, ex);
}
AGIRequest request = new AGIRequest(lines);
request.LocalAddress = socket.LocalAddress;
request.LocalPort = socket.LocalPort;
request.RemoteAddress = socket.RemoteAddress;
request.RemotePort = socket.RemotePort;
return request;
}
public AGIReply ReadReply()
{
string line;
string badSyntax = ((int)AGIReplyStatuses.SC_INVALID_COMMAND_SYNTAX).ToString();
List<string> lines = new List<string>();
try
{
line = socket.ReadLine();
}
catch (IOException ex)
{
throw new AGINetworkException("Unable to read reply from Asterisk: " + ex.Message, ex);
}
if (line == null)
throw new AGIHangupException();
lines.Add(line);
// read synopsis and usage if statuscode is 520
if (line.StartsWith(badSyntax))
try
{
while ((line = socket.ReadLine()) != null)
{
lines.Add(line);
if (line.StartsWith(badSyntax))
break;
}
}
catch (IOException ex)
{
throw new AGINetworkException("Unable to read reply from Asterisk: " + ex.Message, ex);
}
return new AGIReply(lines);
}
}
}

View file

@ -0,0 +1,315 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
namespace Asterisk.NET.FastAGI
{
#region Enum - AGIReplyStatuses
public enum AGIReplyStatuses
{
/// <summary>
/// Status code (200) indicating Asterisk successfully processed the AGICommand.
/// </summary>
SC_SUCCESS = 200,
/// <summary>
/// Status code (510) indicating Asterisk was unable to process the
/// AGICommand because there is no command with the given name available.
/// </summary>
SC_INVALID_OR_UNKNOWN_COMMAND = 510,
/// <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
/// not understood.<br/>
/// Ensure proper quoting of the parameters when you receive this status
/// code.
/// </summary>
SC_INVALID_COMMAND_SYNTAX = 520
}
#endregion
/// <summary>
/// Default implementation of the AGIReply interface.
/// </summary>
public class AGIReply
{
#region Variables
private Match matcher;
private List<string> lines;
private string firstLine;
/// <summary> The result, that is the part directly following the "result=" string.</summary>
private string result;
/// <summary> The status code.</summary>
private int status;
/// <summary>Additional attributes contained in this reply, for example endpos.</summary>
private Dictionary<string, string> attributes;
/// <summary> The contents of the parenthesis.</summary>
private string extra;
/// <summary> In case of status == 520 (invalid command syntax) this attribute contains the synopsis of the command.</summary>
private string synopsis;
/// <summary> In case of status == 520 (invalid command syntax) this attribute contains the usage of the command.</summary>
private string usage;
private bool extraCreated;
private bool synopsisCreated;
private bool attributesCreated;
private bool resultCreated;
private bool statusCreated;
#endregion
#region Constructor - AGIReply()
public AGIReply()
{
}
#endregion
#region Constructor - AGIReply(lines)
public AGIReply(List<string> lines)
{
this.lines = lines;
try
{
firstLine = lines[0];
}
catch { }
}
#endregion
#region FirstLine
public string FirstLine
{
get { return firstLine; }
}
#endregion
#region Lines
public IList Lines
{
get { return lines; }
}
#endregion
#region ResultCode
/// <summary>
/// Returns the return code (the result as int).
/// </summary>
/// <returns>the return code or -1 if the result is not an int.</returns>
public int ResultCode
{
get
{
string result;
result = GetResult();
if (result == null)
return -1;
try
{
return Int32.Parse(result);
}
catch
{
return -1;
}
}
}
#endregion
#region ResultCodeAsChar
/// <summary>
/// Returns the return code as character.
/// </summary>
/// <returns>the return code as character.</returns>
public char ResultCodeAsChar
{
get
{
int resultCode;
resultCode = ResultCode;
if (resultCode < 0)
return (char)(0x0);
return (char)resultCode;
}
}
#endregion
#region Extra
/// <summary>
/// Returns the text in parenthesis contained in this reply.<br/>
/// The meaning of this property depends on the command sent. Sometimes it
/// contains a flag like "timeout" or "hangup" or - in case of the
/// GetVariableCommand - the value of the variable.
/// </summary>
/// <returns>the text in the parenthesis or <code>null</code> if not set.</returns>
public string Extra
{
get
{
if (GetStatus() != (int)AGIReplyStatuses.SC_SUCCESS)
return null;
if (extraCreated)
return extra;
matcher = Common.AGI_PARENTHESIS_PATTERN.Match(firstLine);
if (matcher.Success)
extra = matcher.Groups[1].Value;
extraCreated = true;
return extra;
}
}
#endregion
#region GetResult()
/// <summary>
/// Returns the result, that is the part directly following the "result=" string.
/// </summary>
/// <returns>the result.</returns>
public string GetResult()
{
if (resultCreated)
return result;
matcher = Common.AGI_RESULT_PATTERN.Match(firstLine);
if (matcher.Success)
result = matcher.Groups[1].Value;
resultCreated = true;
return result;
}
#endregion
#region GetStatus()
/// <summary>
/// Returns the status code.<br/>
/// Supported status codes are:<br/>
/// 200 Success<br/>
/// 510 Invalid or unknown command<br/>
/// 520 Invalid command syntax<br/>
/// </summary>
/// <returns>the status code.</returns>
public int GetStatus()
{
if (statusCreated)
return status;
matcher = Common.AGI_STATUS_PATTERN.Match(firstLine);
if (matcher.Success)
status = Int32.Parse(matcher.Groups[1].Value);
statusCreated = true;
return status;
}
#endregion
#region GetAttribute(name)
/// <summary>
/// Returns an additional attribute contained in the reply.<br/>
/// For example the reply to the StreamFileCommand contains an additional
/// endpos attribute indicating the frame where the playback was stopped.
/// This can be retrieved by calling getAttribute("endpos") on the corresponding reply.
/// </summary>
/// <param name="name">the name of the attribute to retrieve. The name is case insensitive.</param>
/// <returns>the value of the attribute or <code>null</code> if it is not set.</returns>
public string GetAttribute(string name)
{
if (GetStatus() != (int)AGIReplyStatuses.SC_SUCCESS)
return null;
if ("result".ToUpper().Equals(name.ToUpper()))
return GetResult();
if (!attributesCreated)
{
matcher = Common.AGI_ADDITIONAL_ATTRIBUTES_PATTERN.Match(firstLine);
if (matcher.Success)
{
string s;
Match attributeMatcher;
attributes = new Dictionary<string,string>();
s = matcher.Groups[2].Value;
attributeMatcher = Common.AGI_ADDITIONAL_ATTRIBUTE_PATTERN.Match(s);
while (attributeMatcher.Success)
{
string key;
string value_Renamed;
key = attributeMatcher.Groups[1].Value;
value_Renamed = attributeMatcher.Groups[2].Value;
attributes[key.ToLower(Helper.CultureInfo)] = value_Renamed;
}
}
attributesCreated = true;
}
if (attributes == null || (attributes.Count == 0))
return null;
return (string)attributes[name.ToLower(Helper.CultureInfo)];
}
#endregion
#region GetSynopsis()
/// <summary>
/// Returns the synopsis of the command sent if Asterisk expected a different
/// syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX).
/// </summary>
/// <returns>the synopsis of the command sent, <code>null</code> if there were no syntax errors.</returns>
public string GetSynopsis()
{
if (GetStatus() != (int)AGIReplyStatuses.SC_INVALID_COMMAND_SYNTAX)
return null;
if (!synopsisCreated)
{
if (lines.Count > 1)
{
string secondLine;
Match synopsisMatcher;
secondLine = ((string)lines[1]);
synopsisMatcher = Common.AGI_SYNOPSIS_PATTERN.Match(secondLine);
if (synopsisMatcher.Success)
synopsis = synopsisMatcher.Groups[1].Value;
}
synopsisCreated = true;
StringBuilder sbUsage = new StringBuilder();
string line;
for (int i = 2; i < lines.Count; i++)
{
line = ((string)lines[i]);
if (line == Common.AGI_END_OF_PROPER_USAGE)
break;
sbUsage.Append(line.Trim());
sbUsage.Append(" ");
}
usage = sbUsage.ToString().Trim();
}
return synopsis;
}
#endregion
#region GetUsage()
/// <summary>
/// Returns the usage of the command sent if Asterisk expected a different
/// syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX).
/// </summary>
/// <returns>
/// the usage of the command sent,
/// <code>null</code> if there were no syntax errors.
/// </returns>
public string GetUsage()
{
return usage;
}
#endregion
#region ToString()
public override string ToString()
{
return Helper.ToString(this);
}
#endregion
}
}

View file

@ -0,0 +1,534 @@
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Net;
using System.Collections.Generic;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// Default implementation of the AGIRequest interface.
/// </summary>
public class AGIRequest
{
#region Variables
#if LOGGER
private Logger logger = Logger.Instance();
#endif
private string rawCallerId;
private Dictionary<string, string> request;
/// <summary> A map assigning the values of a parameter (an array of Strings) to the name of the parameter.</summary>
private Dictionary<string, List<string>> parameterMap;
private string parameters;
private string script;
private bool callerIdCreated;
private IPAddress localAddress;
private int localPort;
private IPAddress remoteAddress;
private int remotePort;
#endregion
#region Constructor - AGIRequest(ICollection environment)
/// <summary>
/// Creates a new AGIRequest.
/// </summary>
/// <param name="environment">the first lines as received from Asterisk containing the environment.</param>
public AGIRequest(List<string> environment)
{
if (environment == null)
throw new ArgumentException("Environment must not be null.");
request = buildMap(environment);
}
#endregion
#region Request
public IDictionary Request
{
get { return request; }
}
#endregion
#region RequestURL
/// <summary>
/// Returns the full URL of the request in the form agi://host[:port][/script].
/// </summary>
public string RequestURL
{
get { return (string) request["request"]; }
}
#endregion
#region Channel
/// <summary>
/// Returns the name of the channel.
/// </summary>
/// <returns>the name of the channel.</returns>
public string Channel
{
get { return (string) request["channel"]; }
}
#endregion
#region UniqueId
/// <summary>
/// Returns the unqiue id of the channel.
/// </summary>
/// <returns>the unqiue id of the channel.</returns>
public string UniqueId
{
get { return (string) request["uniqueid"]; }
}
#endregion
#region Type
/// <summary>
/// Returns the type of the channel, for example "SIP".
/// </summary>
/// <returns>the type of the channel, for example "SIP".</returns>
public string Type
{
get { return (string) request["type"]; }
}
#endregion
#region Language
/// <summary>
/// Returns the language, for example "en".
/// </summary>
public string Language
{
get { return (string) request["language"]; }
}
#endregion
#region CallerId
public string CallerId
{
get
{
string callerIdName = (string)(request["calleridname"]);
string callerId = (string)(request["callerid"]);
if (callerIdName != null)
{
if (callerId == null || callerId.ToLower(Helper.CultureInfo) == "unknown")
return null;
return callerId;
}
else // Asterisk 1.0
return callerId10();
}
}
#endregion
#region CallerIdName
public string CallerIdName
{
get
{
string callerIdName = (string)(request["calleridname"]);
if (callerIdName != null)
{
// Asterisk 1.2
if (callerIdName.ToLower(Helper.CultureInfo) == "unknown")
return null;
return callerIdName;
}
else // Asterisk 1.0
return callerIdName10();
}
}
#endregion
#region Asterisk 1.0 CallerID and CallerIdName
private string callerId10()
{
int lbPosition;
int rbPosition;
if (!callerIdCreated)
{
rawCallerId = ((string) request["callerid"]);
callerIdCreated = true;
}
if (rawCallerId == null)
{
return null;
}
lbPosition = rawCallerId.IndexOf('<');
rbPosition = rawCallerId.IndexOf('>');
if (lbPosition < 0 || rbPosition < 0)
{
return rawCallerId;
}
return rawCallerId.Substring(lbPosition + 1, (rbPosition) - (lbPosition + 1));
}
private string callerIdName10()
{
int lbPosition;
string callerIdName;
if (!callerIdCreated)
{
rawCallerId = ((string) request["callerid"]);
callerIdCreated = true;
}
if (rawCallerId == null)
return null;
lbPosition = rawCallerId.IndexOf('<');
if (lbPosition < 0)
return null;
callerIdName = rawCallerId.Substring(0, (lbPosition) - (0)).Trim();
if (callerIdName.StartsWith("\"") && callerIdName.EndsWith("\""))
callerIdName = callerIdName.Substring(1, (callerIdName.Length - 1) - (1));
if (callerIdName.Length == 0)
return null;
else
return callerIdName;
}
#endregion
#region Dnid
public string Dnid
{
get
{
string dnid = (string)(request["dnid"]);
if (dnid == null || dnid.ToLower(Helper.CultureInfo) == "unknown")
return null;
return dnid;
}
}
#endregion
#region Rdnis
public string Rdnis
{
get
{
string rdnis = (string)(request["rdnis"]);
if (rdnis == null || rdnis.ToLower(Helper.CultureInfo) == "unknown")
return null;
return rdnis;
}
}
#endregion
#region Context
/// <summary>
/// Returns the context in the dial plan from which the AGI script was called.
/// </summary>
public string Context
{
get { return (string) request["context"]; }
}
#endregion
#region Extension
/// <summary>
/// Returns the extension in the dial plan from which the AGI script was called.
/// </summary>
public string Extension
{
get { return (string) request["extension"]; }
}
#endregion
#region Priority
/// <summary>
/// Returns the priority in the dial plan from which the AGI script was called.
/// </summary>
public int Priority
{
get
{
if (request["priority"] != null)
{
return Int32.Parse((string) request["priority"]);
}
return -1;
}
}
#endregion
#region Enhanced
/// <summary>
/// Returns wheather this agi is passed audio (EAGI - Enhanced AGI).<br/>
/// Enhanced AGI is currently not supported on FastAGI.<br/>
/// <code>true</code> if this agi is passed audio, <code>false</code> otherwise.
/// </summary>
public bool Enhanced
{
get
{
if (request["enhanced"] != null && (string)request["enhanced"] == "1.0")
return true;
return false;
}
}
#endregion
#region AccountCode
/// <summary>
/// Returns the account code set for the call.
/// </summary>
public string AccountCode
{
get
{
return (string) request["accountCode"];
}
}
#endregion
#region LocalAddress
public IPAddress LocalAddress
{
get { return localAddress; }
set { this.localAddress = value; }
}
#endregion
#region LocalPort
public int LocalPort
{
get { return localPort; }
set { this.localPort = value; }
}
#endregion
#region RemoteAddress
public IPAddress RemoteAddress
{
get { return remoteAddress; }
set { this.remoteAddress = value; }
}
#endregion
#region RemotePort
public int RemotePort
{
get { return remotePort; }
set { this.remotePort = value; }
}
#endregion
#region Script()
/// <summary>
/// Returns the name of the script to execute.
/// </summary>
public string Script
{
get
{
if (script != null)
return script;
script = ((string)request["network_script"]);
if (script != null)
{
Match scriptMatcher = Common.AGI_SCRIPT_PATTERN.Match(script);
if (scriptMatcher.Success)
{
script = scriptMatcher.Groups[1].Value;
parameters = scriptMatcher.Groups[2].Value;
}
}
return script;
}
}
#endregion
#region CallingAni2
public int CallingAni2
{
get
{
if (request["callingani2"] == null)
return -1;
try
{
return Int32.Parse((string)(request["callingani2"]));
}
catch {}
return -1;
}
}
#endregion
#region CallingPres
public int CallingPres
{
get
{
if (request["callingpres"] == null)
return -1;
try
{
return Int32.Parse((string)(request["callingpres"]));
}
catch {}
return -1;
}
}
#endregion
#region CallingTns
public int CallingTns
{
get
{
if (request["callingtns"] == null)
return -1;
try
{
return Int32.Parse((string)(request["callingtns"]));
}
catch {}
return -1;
}
}
#endregion
#region CallingTon
public int CallingTon
{
get
{
if (request["callington"] == null)
return -1;
try
{
return Int32.Parse((String)(request["callington"]));
}
catch {}
return -1;
}
}
#endregion
#region Parameter(string name)
public string Parameter(string name)
{
List<string> values;
values = ParameterValues(name);
if (values == null || values.Count == 0)
return null;
return values[0];
}
#endregion
#region ParameterValues(string name)
public List<string> ParameterValues(string name)
{
if (ParameterMap().Count == 0)
return null;
return parameterMap[name];
}
#endregion
#region ParameterMap()
public Dictionary<string, List<string>> ParameterMap()
{
if (parameterMap == null)
parameterMap = parseParameters(this.parameters);
return parameterMap;
}
#endregion
#region ToString()
public override string ToString()
{
return Helper.ToString(this);
}
#endregion
#region buildMap(ICollection lines)
/// <summary>
/// Builds a map containing variable names as key (with the "agi_" prefix stripped) and the corresponding values.<br/>
/// Syntactically invalid and empty variables are skipped.
/// </summary>
/// <param name="lines">the environment to transform.</param>
/// <returns> a map with the variables set corresponding to the given environment.</returns>
private Dictionary<string, string> buildMap(List<string> lines)
{
int colonPosition;
string key;
string value;
Dictionary<string, string> map = new Dictionary<string,string>(lines.Count);
foreach (string line in lines)
{
colonPosition = line.IndexOf(':');
if (colonPosition < 0 || !line.StartsWith("agi_") || line.Length < colonPosition + 2)
continue;
key = line.Substring(4, colonPosition - 4).ToLower(Helper.CultureInfo);
value = line.Substring(colonPosition + 2);
if (value.Length != 0)
map.Add(key, value);
}
return map;
}
#endregion
#region parseParameters(string s)
/// <summary>
/// Parses the given parameter string and caches the result.
/// </summary>
/// <param name="s">the parameter string to parse</param>
/// <returns> a Map made up of parameter names their values</returns>
private Dictionary<string, List<string>> parseParameters(string parameters)
{
Dictionary<string, List<string>> result = new Dictionary<string, List<string>>();
string name;
string val;
if (string.IsNullOrEmpty(parameters))
return result;
string[] pars = parameters.Split('&');
if (pars.Length == 0)
return result;
foreach (string parameter in pars)
{
val = string.Empty;
int i = parameter.IndexOf('=');
if (i > 0)
{
name = HttpUtility.UrlDecode(parameter.Substring(0, i));
if (parameter.Length > i + 1)
val = HttpUtility.UrlDecode(parameter.Substring(i + 1));
}
else if (i < 0)
name = HttpUtility.UrlDecode(parameter);
else
continue;
if (!result.ContainsKey(name))
result.Add(name, new List<string>());
result[name].Add(val);
}
return result;
}
#endregion
}
}

View file

@ -0,0 +1,817 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// The BaseAGIScript provides some convinience methods to make it easier to
/// write custom AGIScripts.<br/>
/// Just extend it by your own AGIScripts.
/// </summary>
public abstract class AGIScript
{
#region Answer()
/// <summary>
/// Answers the channel.
/// </summary>
protected internal void Answer()
{
this.Channel.SendCommand(new Command.AnswerCommand());
}
#endregion
#region Hangup()
/// <summary>
/// Hangs the channel up.
/// </summary>
protected internal void Hangup()
{
this.Channel.SendCommand(new Command.HangupCommand());
}
#endregion
#region SetAutoHangup
/// <summary>
/// Cause the channel to automatically hangup at the given number of seconds in the future.<br/>
/// 0 disables the autohangup feature.
/// </summary>
protected internal void SetAutoHangup(int time)
{
this.Channel.SendCommand(new Command.SetAutoHangupCommand(time));
}
#endregion
#region SetCallerId
/// <summary>
/// Sets the caller id on the current channel.<br/>
/// The raw caller id to set, for example "John Doe<1234>".
/// </summary>
protected internal void SetCallerId(string callerId)
{
this.Channel.SendCommand(new Command.SetCallerIdCommand(callerId));
}
#endregion
#region PlayMusicOnHold()
/// <summary>
/// Plays music on hold from the default music on hold class.
/// </summary>
protected internal void PlayMusicOnHold()
{
this.Channel.SendCommand(new Command.SetMusicOnCommand());
}
#endregion
#region PlayMusicOnHold(string musicOnHoldClass)
/// <summary>
/// Plays music on hold from the given music on hold class.
/// </summary>
/// <param name="musicOnHoldClass">the music on hold class to play music from as configures in Asterisk's <code><musiconhold.conf/code>.</param>
protected internal void PlayMusicOnHold(string musicOnHoldClass)
{
this.Channel.SendCommand(new Command.SetMusicOnCommand(musicOnHoldClass));
}
#endregion
#region StopMusicOnHold()
/// <summary>
/// Stops playing music on hold.
/// </summary>
protected internal void StopMusicOnHold()
{
this.Channel.SendCommand(new Command.SetMusicOffCommand());
}
#endregion
#region GetChannelStatus
/// <summary>
/// Returns the status of the channel.<br/>
/// Return values:
/// <ul>
/// <li>0 Channel is down and available
/// <li>1 Channel is down, but reserved
/// <li>2 Channel is off hook
/// <li>3 Digits (or equivalent) have been dialed
/// <li>4 Line is ringing
/// <li>5 Remote end is ringing
/// <li>6 Line is up
/// <li>7 Line is busy
/// </ul>
///
/// </summary>
/// <returns> the status of the channel.
/// </returns>
protected internal int GetChannelStatus()
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ChannelStatusCommand());
return channel.LastReply.ResultCode;
}
#endregion
#region GetData(string file)
/// <summary>
/// Plays the given file and waits for the user to enter DTMF digits until he
/// presses '#'. The user may interrupt the streaming by starting to enter
/// digits.
/// </summary>
/// <param name="file">the name of the file to play</param>
/// <returns> a String containing the DTMF the user entered</returns>
protected internal string GetData(string file)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetDataCommand(file));
return channel.LastReply.GetResult();
}
#endregion
#region GetData(string file, int timeout)
/// <summary>
/// Plays the given file and waits for the user to enter DTMF digits until he
/// presses '#' or the timeout occurs. The user may interrupt the streaming
/// by starting to enter digits.
/// </summary>
/// <param name="file">the name of the file to play</param>
/// <param name="timeout">the timeout in milliseconds to wait for user input.<br/>
/// 0 means standard timeout value, -1 means "ludicrous time"
/// (essentially never times out).</param>
/// <returns> a String containing the DTMF the user entered</returns>
protected internal string GetData(string file, long timeout)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetDataCommand(file, timeout));
return channel.LastReply.GetResult();
}
#endregion
#region GetData(string file, int timeout, int maxDigits)
/// <summary>
/// Plays the given file and waits for the user to enter DTMF digits until he
/// presses '#' or the timeout occurs or the maximum number of digits has
/// been entered. The user may interrupt the streaming by starting to enter
/// digits.
/// </summary>
/// <param name="file">the name of the file to play</param>
/// <param name="timeout">the timeout in milliseconds to wait for user input.<br/>
/// 0 means standard timeout value, -1 means "ludicrous time"
/// (essentially never times out).</param>
/// <param name="maxDigits">the maximum number of digits the user is allowed to enter</param>
/// <returns> a String containing the DTMF the user entered</returns>
protected internal string GetData(string file, long timeout, int maxDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetDataCommand(file, timeout, maxDigits));
return channel.LastReply.GetResult();
}
#endregion
#region GetOption(string file, string escapeDigits)
/// <summary>
/// Plays the given file, and waits for the user to press one of the given
/// digits. If none of the esacpe digits is pressed while streaming the file
/// it waits for the default timeout of 5 seconds still waiting for the user
/// to press a digit.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that the user is expected to press.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char GetOption(string file, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetOptionCommand(file, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region GetOption(string file, string escapeDigits, int timeout)
/// <summary>
/// Plays the given file, and waits for the user to press one of the given
/// digits. If none of the esacpe digits is pressed while streaming the file
/// it waits for the specified timeout still waiting for the user to press a
/// digit.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that the user is expected to press.</param>
/// <param name="timeout">the timeout in seconds to wait if none of the defined esacpe digits was presses while streaming.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char GetOption(string file, string escapeDigits, int timeout)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetOptionCommand(file, escapeDigits, timeout));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region Exec(string application)
/// <summary>
/// Executes the given command.
/// </summary>
/// <param name="application">the name of the application to execute, for example "Dial".</param>
/// <returns> the return code of the application of -2 if the application was not found.</returns>
protected internal int Exec(string application)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ExecCommand(application));
return channel.LastReply.ResultCode;
}
#endregion
#region Exec(string application, string options)
/// <summary>
/// Executes the given command.
/// </summary>
/// <param name="application">the name of the application to execute, for example "Dial".</param>
/// <param name="options">the parameters to pass to the application, for example "SIP/123".</param>
/// <returns> the return code of the application of -2 if the application was not found.</returns>
protected internal int Exec(string application, string options)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ExecCommand(application, options));
return channel.LastReply.ResultCode;
}
#endregion
#region SetContext
/// <summary>
/// Sets the context for continuation upon exiting the application.
/// </summary>
protected internal void SetContext(string context)
{
this.Channel.SendCommand(new Command.SetContextCommand(context));
}
#endregion
#region SetExtension
/// <summary>
/// Sets the extension for continuation upon exiting the application.
/// </summary>
protected internal void SetExtension(string extension)
{
this.Channel.SendCommand(new Command.SetExtensionCommand(extension));
}
#endregion
#region SetPriority(int priority)
/// <summary>
/// Sets the priority for continuation upon exiting the application.
/// </summary>
protected internal void SetPriority(int priority)
{
this.Channel.SendCommand(new Command.SetPriorityCommand(priority));
}
#endregion
#region SetPriority(string label priority)
/// <summary>
/// Sets the label for continuation upon exiting the application.
/// </summary>
protected internal void SetPriority(string label)
{
this.Channel.SendCommand(new Command.SetPriorityCommand(label));
}
#endregion
#region StreamFile(string file)
/// <summary>
/// Plays the given file.
/// </summary>
/// <param name="file">name of the file to play.</param>
protected internal void StreamFile(string file)
{
this.Channel.SendCommand(new Command.StreamFileCommand(file));
}
#endregion
#region StreamFile(string file, string escapeDigits)
/// <summary>
/// Plays the given file and allows the user to escape by pressing one of the given digit.
/// </summary>
/// <param name="file">name of the file to play.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char StreamFile(string file, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.StreamFileCommand(file, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region SayDigits(string digits)
/// <summary>
/// Says the given digit string.
/// </summary>
/// <param name="digits">the digit string to say.</param>
protected internal void SayDigits(string digits)
{
this.Channel.SendCommand(new Command.SayDigitsCommand(digits));
}
#endregion
#region SayDigits(string digits, string escapeDigits)
/// <summary>
/// Says the given number, returning early if any of the given DTMF number
/// are received on the channel.
/// </summary>
/// <param name="digits">the digit string to say.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayDigits(string digits, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayDigitsCommand(digits, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region SayNumber(string number)
/// <summary>
/// Says the given number.
/// </summary>
/// <param name="number">the number to say.</param>
protected internal void SayNumber(string number)
{
this.Channel.SendCommand(new Command.SayNumberCommand(number));
}
#endregion
#region SayNumber(string number, string escapeDigits)
/// <summary>
/// Says the given number, returning early if any of the given DTMF number
/// are received on the channel.
/// </summary>
/// <param name="number">the number to say.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayNumber(string number, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayNumberCommand(number, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region SayPhonetic(string text)
/// <summary>
/// Says the given character string with phonetics.
/// </summary>
/// <param name="text">the text to say.</param>
protected internal void SayPhonetic(string text)
{
this.Channel.SendCommand(new Command.SayPhoneticCommand(text));
}
#endregion
#region SayPhonetic(string text, string escapeDigits)
/// <summary>
/// Says the given character string with phonetics, returning early if any of
/// the given DTMF number are received on the channel.
/// </summary>
/// <param name="text">the text to say.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayPhonetic(string text, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayPhoneticCommand(text, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region SayAlpha(string text)
/// <summary>
/// Says the given character string.
/// </summary>
/// <param name="text">the text to say.</param>
protected internal void SayAlpha(string text)
{
this.Channel.SendCommand(new Command.SayAlphaCommand(text));
}
#endregion
#region SayAlpha(string text, string escapeDigits)
/// <summary>
/// Says the given character string, returning early if any of the given DTMF
/// number are received on the channel.
/// </summary>
/// <param name="text">the text to say.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayAlpha(string text, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayAlphaCommand(text, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region SayTime(long time)
/// <summary>
/// Says the given time.
/// </summary>
/// <param name="time">the time to say in seconds since 00:00:00 on January 1, 1970.</param>
protected internal void SayTime(long time)
{
this.Channel.SendCommand(new Command.SayTimeCommand(time));
}
#endregion
#region SayTime(long time, string escapeDigits)
/// <summary>
/// Says the given time, returning early if any of the given DTMF number are
/// received on the channel.
/// </summary>
/// <param name="time">the time to say in seconds since 00:00:00 on January 1, 1970.</param>
/// <param name="escapeDigits">a String containing the DTMF digits that allow the user to escape.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayTime(long time, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayTimeCommand(time, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region GetVariable(string name)
/// <summary>
/// Returns the value of the given channel variable.
/// </summary>
/// <param name="name">the name of the variable to retrieve.</param>
/// <returns> the value of the given variable or <code>null</code> if not set.</returns>
protected internal string GetVariable(string name)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetVariableCommand(name));
if (channel.LastReply.ResultCode != 1)
return null;
return channel.LastReply.Extra;
}
#endregion
#region SetVariable(string name, string value_Renamed)
/// <summary>
/// Sets the value of the given channel variable to a new value.
/// </summary>
/// <param name="name">the name of the variable to retrieve.</param>
/// <param name="val">the new value to set.</param>
protected internal void SetVariable(string name, string val)
{
this.Channel.SendCommand(new Command.SetVariableCommand(name, val));
}
#endregion
#region WaitForDigit(int timeout)
/// <summary>
/// Waits up to 'timeout' milliseconds to receive a DTMF digit.
/// </summary>
/// <param name="timeout">timeout the milliseconds to wait for the channel to receive a DTMF digit, -1 will wait forever.</param>
/// <returns> the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char WaitForDigit(int timeout)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.WaitForDigitCommand(timeout));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region GetFullVariable(string name)
/// <summary>
/// Returns the value of the current channel variable, unlike getVariable()
/// this method understands complex variable names and builtin variables.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="name">the name of the variable to retrieve.</param>
/// <returns>the value of the given variable or <code>null</code> if not et.</returns>
protected internal string GetFullVariable(string name)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetFullVariableCommand(name));
if (channel.LastReply.ResultCode != 1)
return null;
return channel.LastReply.Extra;
}
#endregion
#region GetFullVariable(string name, string channel)
/// <summary>
/// Returns the value of the given channel variable.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="name">the name of the variable to retrieve.</param>
/// <param name="channel">the name of the channel.</param>
/// <returns>the value of the given variable or <code>null</code> if not set.</returns>
protected internal string GetFullVariable(string name, string channelName)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.GetFullVariableCommand(name, channelName));
if (channel.LastReply.ResultCode != 1)
return null;
return channel.LastReply.Extra;
}
#endregion
#region SayDateTime(...)
/// <summary>
/// Says the given time.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
protected internal void SayDateTime(long time)
{
this.Channel.SendCommand(new Command.SayDateTimeCommand(time));
}
/// <summary>
/// Says the given time and allows interruption by one of the given escape digits.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
/// <returns>the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayDateTime(long time, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayDateTimeCommand(time, escapeDigits));
return channel.LastReply.ResultCodeAsChar;
}
/// <summary>
/// Says the given time in the given format and allows interruption by one of the given escape digits.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
/// <param name="format">the format the time should be said in</param>
/// <returns>the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayDateTime(long time, string escapeDigits, string format)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayDateTimeCommand(time, escapeDigits, format));
return channel.LastReply.ResultCodeAsChar;
}
/// <summary>
/// Says the given time in the given format and timezone and allows interruption by one of the given escape digits.<br/>
/// Available since Asterisk 1.2.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
/// <param name="format">the format the time should be said in</param>
/// <param name="timezone">the timezone to use when saying the time, for example "UTC" or "Europe/Berlin".</param>
/// <returns>the DTMF digit pressed or 0x0 if none was pressed.</returns>
protected internal char SayDateTime(long time, string escapeDigits, string format, string timezone)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.SayDateTimeCommand(time, escapeDigits, format, timezone));
return channel.LastReply.ResultCodeAsChar;
}
#endregion
#region DatabaseGet(string family, string key)
/// <summary>
/// Retrieves an entry in the Asterisk database for a given family and key.
/// </summary>
/// <param name="family">the family of the entry to retrieve.</param>
/// <param name="key">key the key of the entry to retrieve.</param>
/// <return>the value of the given family and key or <code>null</code> if there is no such value.</return>
protected internal string DatabaseGet(string family, string key)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.DatabaseGetCommand(family, key));
if (channel.LastReply.ResultCode != 1)
return null;
return channel.LastReply.Extra;
}
#endregion
#region DatabasePut(string family, string key, string value)
/// <summary>
/// Adds or updates an entry in the Asterisk database for a given family, key and value.
/// </summary>
/// <param name="family">the family of the entry to add or update.</param>
/// <param name="key">the key of the entry to add or update.</param>
/// <param name="value">the new value of the entry.</param>
protected internal void DatabasePut(string family, string key, string value)
{
this.Channel.SendCommand(new Command.DatabasePutCommand(family, key, value));
}
#endregion
#region DatabaseDel(string family, string key)
/// <summary>
/// Deletes an entry in the Asterisk database for a given family and key.
/// </summary>
/// <param name="family">the family of the entry to delete.</param>
/// <param name="key">the key of the entry to delete.</param>
protected internal void DatabaseDel(string family, string key)
{
this.Channel.SendCommand(new Command.DatabaseDelCommand(family, key));
}
#endregion
#region DatabaseDelTree(String family)
/// <summary>
/// Deletes a whole family of entries in the Asterisk database.
/// </summary>
/// <param name="family">the family to delete.</param>
protected internal void DatabaseDelTree(string family)
{
this.Channel.SendCommand(new Command.DatabaseDelTreeCommand(family));
}
#endregion
#region DatabaseDelTree(string family, string keytree)
/// <summary>
/// Deletes all entries of a given family in the Asterisk database that have a key that starts with a given prefix.
/// </summary>
/// <param name="family">the family of the entries to delete.</param>
/// <param name="keytree">the prefix of the keys of the entries to delete.</param>
protected internal void DatabaseDelTree(string family, string keytree)
{
this.Channel.SendCommand(new Command.DatabaseDelTreeCommand(family, keytree));
}
#endregion
#region Verbose(string message, int level)
/// <summary>
/// Sends a message to the Asterisk console via the verbose message system.
/// </summary>
/// <param name="message">the message to send</param>
/// <param name="level">the verbosity level to use. Must be in [1..4]</param>
public void Verbose(string message, int level)
{
this.Channel.SendCommand(new Command.VerboseCommand(message, level));
}
#endregion
#region RecordFile(...)
/// <summary>
/// Record to a file until a given dtmf digit in the sequence is received.<br/>
/// Returns -1 on hangup or error.<br/>
/// The format will specify what kind of file will be recorded. The timeout is
/// the maximum record time in milliseconds, or -1 for no timeout. Offset samples
/// is optional, and if provided will seek to the offset without exceeding the
/// end of the file. "maxSilence" is the number of seconds of maxSilence allowed
/// before the function returns despite the lack of dtmf digits or reaching
/// timeout.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="format">the format of the file to be recorded, for example "wav".</param>
/// <param name="escapeDigits">contains the digits that allow the user to end recording.</param>
/// <param name="timeout">the maximum record time in milliseconds, or -1 for no timeout.</param>
/// <returns>result code</returns>
protected internal int RecordFile(string file, string format, string escapeDigits, int timeout)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.RecordFileCommand(file, format, escapeDigits, timeout));
return channel.LastReply.ResultCode;
}
/// <summary>
/// Record to a file until a given dtmf digit in the sequence is received.<br/>
/// Returns -1 on hangup or error.<br/>
/// The format will specify what kind of file will be recorded. The timeout is
/// the maximum record time in milliseconds, or -1 for no timeout. Offset samples
/// is optional, and if provided will seek to the offset without exceeding the
/// end of the file. "maxSilence" is the number of seconds of maxSilence allowed
/// before the function returns despite the lack of dtmf digits or reaching
/// timeout.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="format">the format of the file to be recorded, for example "wav".</param>
/// <param name="escapeDigits">contains the digits that allow the user to end recording.</param>
/// <param name="timeout">the maximum record time in milliseconds, or -1 for no timeout.</param>
/// <param name="offset">the offset samples to skip.</param>
/// <param name="beep"><code>true</code> if a beep should be played before recording.</param>
/// <param name="maxSilence">The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout.</param>
/// <returns>result code</returns>
protected internal int RecordFile(string file, string format, string escapeDigits, int timeout, int offset, bool beep, int maxSilence)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.RecordFileCommand(file, format, escapeDigits, timeout, offset, beep, maxSilence));
return channel.LastReply.ResultCode;
}
#endregion
#region ControlStreamFile(...)
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given
/// digits, if any, and allows the listner to control the stream.<br/>
/// If offset is provided then the audio will seek to sample offset before play
/// starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.<br/>
/// Available since Asterisk 1.2
/// </summary>
/// <seealso cref="Command.ControlStreamFileCommand"/>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <returns>result code</returns>
protected internal int ControlStreamFile(string file)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ControlStreamFileCommand(file));
return channel.LastReply.ResultCode;
}
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given
/// digits, if any, and allows the listner to control the stream.<br/>
/// If offset is provided then the audio will seek to sample offset before play
/// starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.<br/>
/// Available since Asterisk 1.2
/// </summary>
/// <seealso cref="Command.ControlStreamFileCommand"/>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
/// <returns>result code</returns>
protected internal int ControlStreamFile(string file, string escapeDigits)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ControlStreamFileCommand(file, escapeDigits));
return channel.LastReply.ResultCode;
}
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given
/// digits, if any, and allows the listner to control the stream.<br/>
/// If offset is provided then the audio will seek to sample offset before play
/// starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.<br/>
/// Available since Asterisk 1.2
/// </summary>
/// <seealso cref="Command.ControlStreamFileCommand"/>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
/// <param name="offset">the offset samples to skip before streaming.</param>
/// <returns>result code</returns>
protected internal int ControlStreamFile(string file, string escapeDigits, int offset)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ControlStreamFileCommand(file, escapeDigits, offset));
return channel.LastReply.ResultCode;
}
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given
/// digits, if any, and allows the listner to control the stream.<br/>
/// If offset is provided then the audio will seek to sample offset before play
/// starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.<br/>
/// Available since Asterisk 1.2
/// </summary>
/// <seealso cref="Command.ControlStreamFileCommand"/>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
/// <param name="offset">the offset samples to skip before streaming.</param>
/// <param name="forwardDigit">the digit for fast forward.</param>
/// <param name="rewindDigit">the digit for rewind.</param>
/// <param name="pauseDigit">the digit for pause and unpause.</param>
/// <returns>result code</returns>
protected internal int ControlStreamFile(string file, string escapeDigits, int offset, string forwardDigit, string rewindDigit, string pauseDigit)
{
AGIChannel channel = this.Channel;
channel.SendCommand(new Command.ControlStreamFileCommand(file, escapeDigits, offset, forwardDigit, rewindDigit, pauseDigit));
return channel.LastReply.ResultCode;
}
#endregion
#region SendCommand(Command.AGICommand command)
/// <summary>
/// Sends the given command to the channel attached to the current thread.
/// </summary>
/// <param name="command">the command to send to Asterisk</param>
/// <returns> the reply received from Asterisk</returns>
/// <throws> AGIException if the command could not be processed properly </throws>
private AGIReply SendCommand(Command.AGICommand command)
{
return this.Channel.SendCommand(command);
}
#endregion
#region Channel
protected internal AGIChannel Channel
{
get
{
AGIChannel channel = AGIConnectionHandler.Channel;
if (channel == null)
throw new SystemException("Trying to send command from an invalid thread");
return channel;
}
}
#endregion
#region Service(AGIRequest param1, AGIChannel param2);
public abstract void Service(AGIRequest param1, AGIChannel param2);
#endregion
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.IO;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// Default implementation of the AGIWriter interface.
/// </summary>
public class AGIWriter
{
private IO.SocketConnection socket;
public AGIWriter(IO.SocketConnection socket)
{
lock (this)
this.socket = socket;
}
public void SendCommand(Command.AGICommand command)
{
string buffer = command.BuildCommand() + "\n";
try
{
socket.Write(buffer);
}
catch (IOException e)
{
throw new AGINetworkException("Unable to send command to Asterisk: " + e.Message, e);
}
}
}
}

View file

@ -0,0 +1,220 @@
using System.IO;
using System.Net;
using System.Text;
namespace Asterisk.NET.FastAGI
{
public class AsteriskFastAGI
{
#region Variables
#if LOGGER
private Logger logger = Logger.Instance();
#endif
private IO.ServerSocket serverSocket;
/// <summary> The port to listen on.</summary>
private int port;
/// <summary> The address to listen on.</summary>
private string address;
/// <summary>The thread pool that contains the worker threads to process incoming requests.</summary>
private Util.ThreadPool pool;
/// <summary>The number of worker threads in the thread pool. This equals the maximum number of concurrent requests this AGIServer can serve.</summary>
private int poolSize;
/// <summary> True while this server is shut down. </summary>
private bool stopped;
/// <summary>
/// The strategy to use for bind AGIRequests to AGIScripts that serve them.
/// </summary>
private MappingStrategy mappingStrategy;
private Encoding socketEncoding = Encoding.ASCII;
#endregion
#region PoolSize
/// <summary>
/// Sets the number of worker threads in the thread pool.<br/>
/// This equals the maximum number of concurrent requests this AGIServer can serve.<br/>
/// The default pool size is 10.
/// </summary>
public int PoolSize
{
set { this.poolSize = value; }
}
#endregion
#region BindPort
/// <summary>
/// Sets the TCP port to listen on for new connections.<br/>
/// The default bind port is 4573.
/// </summary>
public int BindPort
{
set
{
this.port = value;
}
}
#endregion
#region MappingStrategy
/// <summary>
/// Sets the strategy to use for mapping AGIRequests to AGIScripts that serve them.<br/>
/// The default mapping is a MappingStrategy.
/// </summary>
/// <seealso cref="MappingStrategy" />
public MappingStrategy MappingStrategy
{
set { this.mappingStrategy = value; }
}
#endregion
#region SocketEncoding
public Encoding SocketEncoding
{
get { return this.socketEncoding; }
set { this.socketEncoding = value; }
}
#endregion
#region Constructor - AsteriskFastAGI()
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI()
{
this.address = Common.AGI_BIND_ADDRESS;
this.port = Common.AGI_BIND_PORT;
this.poolSize = Common.AGI_POOL_SIZE;
this.mappingStrategy = new MappingStrategy();
}
#endregion
#region Constructor - AsteriskFastAGI()
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI(string mappingStrategy)
{
this.address = Common.AGI_BIND_ADDRESS;
this.port = Common.AGI_BIND_PORT;
this.poolSize = Common.AGI_POOL_SIZE;
this.mappingStrategy = new MappingStrategy(mappingStrategy);
}
#endregion
#region Constructor - AsteriskFastAGI(int port, int poolSize)
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
/// <param name="port">The port to listen on.</param>
/// <param name="poolSize">The number of worker threads in the thread pool.
/// This equals the maximum number of concurrent requests this AGIServer can serve.</param>
public AsteriskFastAGI(int port, int poolSize)
{
this.address = Common.AGI_BIND_ADDRESS;
this.port = port;
this.poolSize = poolSize;
this.mappingStrategy = new MappingStrategy();
}
#endregion
#region Constructor - AsteriskFastAGI(string address, int port, int poolSize)
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
/// <param name="ipaddress">The address to listen on.</param>
/// <param name="port">The port to listen on.</param>
/// <param name="poolSize">The number of worker threads in the thread pool.
/// This equals the maximum number of concurrent requests this AGIServer can serve.</param>
public AsteriskFastAGI(string ipaddress, int port, int poolSize)
{
this.address = ipaddress;
this.port = port;
this.poolSize = poolSize;
this.mappingStrategy = new MappingStrategy();
}
#endregion
#region Start()
public void Start()
{
stopped = false;
mappingStrategy.Load();
pool = new Util.ThreadPool("AGIServer", poolSize);
#if LOGGER
logger.Info("Thread pool started.");
#endif
try
{
IPAddress ipAddress = IPAddress.Parse(address);
serverSocket = new IO.ServerSocket(port, ipAddress, this.SocketEncoding);
}
catch(IOException ex)
{
#if LOGGER
logger.Error("Unable start AGI Server: cannot to bind to "+ address + ":" + port + ".", ex);
#endif
throw ex;
}
#if LOGGER
logger.Info("Listening on "+ address + ":" + port + ".");
#endif
AGIConnectionHandler connectionHandler;
IO.SocketConnection socket;
try
{
while ((socket = serverSocket.Accept()) != null)
{
#if LOGGER
logger.Info("Received connection.");
#endif
connectionHandler = new AGIConnectionHandler(socket, mappingStrategy);
pool.AddJob(connectionHandler);
}
}
catch (IOException ex)
{
if (!stopped)
{
#if LOGGER
logger.Error("IOException while waiting for connections (1).", ex);
#endif
throw ex;
}
}
finally
{
if (serverSocket != null)
{
try
{
serverSocket.Close();
}
#if LOGGER
catch (IOException ex)
{
logger.Error("IOException while waiting for connections (2).", ex);
}
#else
catch { }
#endif
}
serverSocket = null;
pool.Shutdown();
#if LOGGER
logger.Info("AGIServer shut down.");
#endif
}
}
#endregion
#region Stop()
public void Stop()
{
stopped = true;
if (serverSocket != null)
serverSocket.Close();
}
#endregion
}
}

View file

@ -0,0 +1,26 @@
using System.Text;
namespace Asterisk.NET.FastAGI.Command
{
public abstract class AGICommand
{
public abstract string BuildCommand();
protected internal string EscapeAndQuote(string s)
{
string tmp;
if (s == null)
return "\"\"";
tmp = s;
tmp = tmp.Replace("\\\"", "\\\\\""); // escape quotes
tmp = tmp.Replace("\\\n", ""); // filter newline
return "\"" + tmp + "\""; // add quotes
}
public override string ToString()
{
return Helper.ToString(this);
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
public class AnswerCommand : AGICommand
{
public AnswerCommand()
{
}
public override string BuildCommand()
{
return "ANSWER";
}
}
}

View file

@ -0,0 +1,55 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Returns the status of the specified channel.
/// If no channel name is given the returns the status of the current channel.<br/>
/// Return values:
/// <ul>
/// <li>0 Channel is down and available
/// <li>1 Channel is down, but reserved
/// <li>2 Channel is off hook
/// <li>3 Digits (or equivalent) have been dialed
/// <li>4 Line is ringing
/// <li>5 Remote end is ringing
/// <li>6 Line is up
/// <li>7 Line is busy
/// </ul>
/// </summary>
public class ChannelStatusCommand : AGICommand
{
private string channel;
public string Channel
{
get
{
return channel;
}
set
{
this.channel = value;
}
}
public ChannelStatusCommand()
{
this.channel = null;
}
/// <summary>
/// Creates a new ChannelStatusCommand that queries the given channel.
/// </summary>
/// <param name="channel">the name of the channel to query.</param>
public ChannelStatusCommand(string channel)
{
this.channel = channel;
}
public override string BuildCommand()
{
return "CHANNEL STATUS" + (channel == null ? "" : " " + EscapeAndQuote(channel));
}
}
}

View file

@ -0,0 +1,204 @@
using System;
using System.Text;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given
/// digits, if any, and allows the listner to control the stream.<br/>
/// If offset is provided then the audio will seek to sample offset before play
/// starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.<br/>
/// Available since Asterisk 1.2
/// </summary>
public class ControlStreamFileCommand : AGICommand
{
/// <summary> The name of the file to stream.</summary>
private string file;
/// <summary> When one of these digits is pressed while streaming the command returns.</summary>
private string escapeDigits;
/// <summary> The offset samples to skip before streaming.</summary>
private int offset;
private string forwardDigit;
private string rewindDigit;
private string pauseDigit;
#region File
/// <summary>
/// Get/Set the name of the file to stream.
/// </summary>
public string File
{
get { return file; }
set { this.file = value; }
}
#endregion
#region EscapeDigits
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command or <code>null</code> for none.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
#endregion
#region Offset
/// <summary>
/// Get/Set the offset samples to skip before streaming.
/// </summary>
public int Offset
{
get { return offset; }
set { this.offset = value; }
}
#endregion
#region ForwardDigit
/// <summary>
/// Get the digit for fast forward.
/// </summary>
public string ForwardDigit
{
get { return forwardDigit; }
}
#endregion
#region RewindDigit
/// <summary>
/// Get the digit for rewind.
/// </summary>
public string RewindDigit
{
get { return rewindDigit; }
}
#endregion
#region PauseDigit
/// <summary>
/// Get the digit for pause and unpause.
/// </summary>
public string PauseDigit
{
get { return pauseDigit; }
}
#endregion
#region ControlStreamFileCommand(string file)
/// <summary>
/// Creates a new ControlStreamFileCommand, streaming from the beginning. It
/// uses the default digit "#" for forward and "*" for rewind and does not
/// support pausing.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
public ControlStreamFileCommand(string file)
{
this.file = file;
this.escapeDigits = null;
this.offset = - 1;
}
#endregion
#region ControlStreamFileCommand(string file, string escapeDigits)
/// <summary>
/// Creates a new ControlStreamFileCommand, streaming from the beginning. It
/// uses the default digit "#" for forward and "*" for rewind and does not
/// support pausing.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
public ControlStreamFileCommand(string file, string escapeDigits)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.offset = - 1;
}
#endregion
#region ControlStreamFileCommand(string file, string escapeDigits, int offset)
/// <summary>
/// Creates a new ControlStreamFileCommand, streaming from the given offset.
/// It uses the default digit "#" for forward and "*" for rewind and does not
/// support pausing.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">
/// contains the digits that allow the user to interrupt this command.
/// Maybe <code>null</code> if you don't want the user to interrupt.
/// </param>
/// <param name="offset">the offset samples to skip before streaming.</param>
public ControlStreamFileCommand(string file, string escapeDigits, int offset)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.offset = offset;
}
#endregion
#region ControlStreamFileCommand(string file, string escapeDigits, int offset, string forwardDigit, string rewindDigit, string pauseDigit)
/// <summary>
/// Creates a new ControlStreamFileCommand, streaming from the given offset.
/// It uses the default digit "#" for forward and "*" for rewind and does not
/// support pausing.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command. Maybe <code>null</code> if you don't want the user to interrupt.</param>
/// <param name="offset">the offset samples to skip before streaming.</param>
/// <param name="forwardDigit">the digit for fast forward.</param>
/// <param name="rewindDigit">the digit for rewind.</param>
/// <param name="pauseDigit">the digit for pause and unpause.</param>
public ControlStreamFileCommand(string file, string escapeDigits, int offset, string forwardDigit, string rewindDigit, string pauseDigit)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.offset = offset;
this.forwardDigit = forwardDigit;
this.rewindDigit = rewindDigit;
this.pauseDigit = pauseDigit;
}
#endregion
#region ControlDigits(string forwardDigit, string rewindDigit)
/// <summary>
/// Sets the control digits for fast forward and rewind.
/// </summary>
/// <param name="forwardDigit">the digit for fast forward.</param>
/// <param name="rewindDigit">the digit for rewind.</param>
public void ControlDigits(string forwardDigit, string rewindDigit)
{
this.forwardDigit = forwardDigit;
this.rewindDigit = rewindDigit;
}
#endregion
#region ControlDigits(string forwardDigit, string rewindDigit, string pauseDigit)
/// <summary>
/// Sets the control digits for fast forward, rewind and pause.
/// </summary>
/// <param name="forwardDigit">the digit for fast forward.</param>
/// <param name="rewindDigit">the digit for rewind.</param>
/// <param name="pauseDigit">the digit for pause and unpause.</param>
public void ControlDigits(string forwardDigit, string rewindDigit, string pauseDigit)
{
this.forwardDigit = forwardDigit;
this.rewindDigit = rewindDigit;
this.pauseDigit = pauseDigit;
}
#endregion
#region BuildCommand()
public override string BuildCommand()
{
StringBuilder sb = new StringBuilder("CONTROL STREAM FILE ");
sb.Append(EscapeAndQuote(file) + " " + EscapeAndQuote(escapeDigits));
if (offset >= 0)
sb.Append(" " + offset.ToString());
else if (forwardDigit != null || rewindDigit != null || pauseDigit != null)
sb.Append(" 0");
if (forwardDigit != null || rewindDigit != null || pauseDigit != null)
sb.Append(" " + forwardDigit);
if (rewindDigit != null || pauseDigit != null)
sb.Append(" " + rewindDigit);
if (pauseDigit != null)
sb.Append(" " + pauseDigit);
return sb.ToString();
}
#endregion
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Deletes a family or specific keytree within a family in the Asterisk database.<br/>
/// Returns 1 if successful, 0 otherwise.
/// </summary>
public class DatabaseDelCommand : AGICommand
{
/// <summary> The family (or family of the keytree) to delete.</summary>
private string family;
/// <summary> The keyTree to delete.</summary>
private string keyTree;
/// <summary>
/// Get/Set the family (or family of the keytree) to delete.
/// </summary>
public string Family
{
get { return family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the keytree to delete.
/// </summary>
public string KeyTree
{
get { return keyTree; }
set { this.keyTree = value; }
}
/// <summary>
/// Creates a new DatabaseDelCommand to delete a family.
/// </summary>
/// <param name="family">the family to delete.</param>
public DatabaseDelCommand(string family)
{
this.family = family;
this.keyTree = null;
}
/// <summary>
/// Creates a new DatabaseDelCommand to delete a keytree.
/// </summary>
/// <param name="family">the family of the keytree to delete.</param>
/// <param name="keyTree">the keytree to delete.</param>
public DatabaseDelCommand(string family, string keyTree)
{
this.family = family;
this.keyTree = keyTree;
}
public override string BuildCommand()
{
return "DATABASE DELTREE " + EscapeAndQuote(family) + (keyTree == null?"":" " + EscapeAndQuote(keyTree));
}
}
}

View file

@ -0,0 +1,64 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Deletes an entry in the Asterisk database for a given family and key.<br/>
/// Returns 1 if successful, 0 otherwise.
/// </summary>
public class DatabaseDelTreeCommand : AGICommand
{
/// <summary> The family of the key to delete.</summary>
private string family;
/// <summary> The key to delete.</summary>
private string keyTree;
/// <summary>
/// Get/Set the family of the key to delete.
/// </summary>
public string Family
{
get { return family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the key to delete.
/// </summary>
public string KeyTree
{
get { return keyTree; }
set { this.keyTree = value; }
}
/// <summary>
/// Creates a new DatabaseDelCommand.
/// </summary>
/// <param name="family">the family of the key to delete.</param>
/// <param name="key">the key to delete.</param>
public DatabaseDelTreeCommand(string family)
{
this.family = family;
}
/// <summary>
/// Creates a new DatabaseDelCommand.
/// </summary>
/// <param name="family">the family of the key to delete.</param>
/// <param name="keytree">the key to delete.</param>
public DatabaseDelTreeCommand(string family, string keytree)
{
this.family = family;
this.keyTree = keytree;
}
public override string BuildCommand()
{
if (family != null)
{
if (keyTree != null)
{
return "DATABASE DELTREE " + EscapeAndQuote(family) + " " + EscapeAndQuote(keyTree);
}
return "DATABASE DELTREE " + EscapeAndQuote(family);
}
throw new ArgumentNullException("Family");
}
}
}

View file

@ -0,0 +1,50 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Retrieves an entry in the Asterisk database for a given family and key.<br/>
/// Returns 0 if is not set. Returns 1 if the variable is set and returns the
/// value in parenthesis.<br/>
/// Example return code: 200 result=1 (testvariable)
/// </summary>
public class DatabaseGetCommand : AGICommand
{
/// <summary> The family of the key to retrieve.</summary>
private string family;
/// <summary> The key to retrieve.</summary>
private string varKey;
/// <summary>
/// Get/Set
/// </summary>
public string Family
{
get { return family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the key to retrieve.
/// </summary>
public string Key
{
get { return varKey; }
set { this.varKey = value; }
}
/// <summary>
/// Creates a new DatabaseGetCommand.
/// </summary>
/// <param name="family">the family of the key to retrieve.</param>
/// <param name="key">the key to retrieve.</param>
public DatabaseGetCommand(string family, string key)
{
this.family = family;
this.varKey = key;
}
public override string BuildCommand()
{
return "DATABASE GET " + EscapeAndQuote(family) + " " + EscapeAndQuote(varKey);
}
}
}

View file

@ -0,0 +1,60 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Adds or updates an entry in the Asterisk database for a given family, key, and value.<br/>
/// Returns 1 if successful, 0 otherwise.
/// </summary>
public class DatabasePutCommand : AGICommand
{
/// <summary> The family of the key to set.</summary>
private string family;
/// <summary> The key to set.</summary>
private string varKey;
/// <summary> The value to set.</summary>
private string varValue;
/// <summary>
/// Get/Set the family of the key to set.
/// </summary>
public string Family
{
get { return family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the key to set.
/// </summary>
public string Key
{
get { return varKey; }
set { this.varKey = value; }
}
/// <summary>
/// Get/Set the value to set.
/// </summary>
public string Value
{
get { return varValue; }
set { this.varValue = value; }
}
/// <summary>
/// Creates a new DatabasePutCommand.
/// </summary>
/// <param name="family">the family of the key to set.</param>
/// <param name="key">the key to set.</param>
/// <param name="value">the value to set.</param>
public DatabasePutCommand(string family, string key, string value)
{
this.family = family;
this.varKey = key;
this.varValue = value;
}
public override string BuildCommand()
{
return "DATABASE PUT " + EscapeAndQuote(family) + " " + EscapeAndQuote(varKey) + " " + EscapeAndQuote(varValue);
}
}
}

View file

@ -0,0 +1,57 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Executes an application with the given options.<br/>
/// Returns whatever the application returns, or -2 if the application was not found.
/// </summary>
public class ExecCommand : AGICommand
{
/// <summary> The name of the application to execute.</summary>
private string application;
/// <summary> The options to pass to the application.</summary>
private string options;
/// <summary>
/// Get/Set the name of the application to execute.
/// </summary>
public string Application
{
get { return application; }
set { this.application = value; }
}
/// <summary>
/// Get/Set the options to pass to the application.
/// </summary>
public string Options
{
get { return options; }
set { this.options = value; }
}
/// <summary>
/// Creates a new ExecCommand.
/// </summary>
/// <param name="application">the name of the application to execute.</param>
public ExecCommand(string application)
{
this.application = application;
}
/// <summary>
/// Creates a new ExecCommand.
/// </summary>
/// <param name="application">the name of the application to execute.</param>
/// <param name="options">the options to pass to the application.</param>
public ExecCommand(string application, string options)
{
this.application = application;
this.options = options;
}
public override string BuildCommand()
{
return "EXEC " + EscapeAndQuote(application) + " " + EscapeAndQuote(options);
}
}
}

View file

@ -0,0 +1,109 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Stream the given file, and recieve DTMF data. The user may interrupt the streaming by starting to enter digits.<br/>
/// Returns the digits recieved from the channel at the other end.<br/>
/// Input ends when the timeout is reached, the maximum number of digits is read or the user presses #.
/// </summary>
public class GetDataCommand : AGICommand
{
/// <summary> The name of the file to stream.</summary>
private string file;
/// <summary> The timeout in milliseconds to wait for data.<br/>
/// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out).
/// </summary>
private long timeout;
/// <summary> The maximum number of digits to read.<br/>
/// Must be in [1..1024].
/// </summary>
private int maxDigits;
/// <summary>
/// Get/Set the name of the file to stream. Must not include extension.
/// </summary>
public string File
{
get { return file; }
set { this.file = value; }
}
/// <summary>
/// Get/Set the timeout in milliseconds to wait for data. 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out).
/// </summary>
public long Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
/// <summary>
/// Get/Set the maximum number of digits to read. The maximum number of digits to read. Must be in [1..1024].
/// </summary>
/// <throws> IllegalArgumentException if maxDigits is not in [1..1024] </throws>
public int MaxDigits
{
get { return maxDigits; }
set
{
if (value < 1 || value > 1024)
throw new ArgumentException("maxDigits must be in [1..1024]");
this.maxDigits = value;
}
}
/// <summary>
/// Creates a new GetDataCommand with default timeout and maxDigits set to 1024.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
public GetDataCommand(string file)
{
this.file = file;
this.timeout = Common.AGI_DEFAULT_TIMEOUT;
this.maxDigits = Common.AGI_DEFAULT_MAX_DIGITS;
}
/// <summary>
/// Creates a new GetDataCommand with the given timeout and maxDigits set to 1024.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="timeout">the timeout in milliseconds to wait for data.<br/>
/// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out).
/// </param>
public GetDataCommand(string file, long timeout)
{
this.file = file;
this.timeout = timeout;
this.maxDigits = Common.AGI_DEFAULT_MAX_DIGITS;
}
/// <summary>
/// Creates a new GetDataCommand with the given timeout and maxDigits.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="timeout">the timeout in milliseconds to wait for data.<br/>
/// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out).
/// </param>
/// <param name="maxDigits">the maximum number of digits to read.<br/>
/// Must be in [1..1024].
/// </param>
/// <throws> IllegalArgumentException if maxDigits is not in [1..1024] </throws>
public GetDataCommand(string file, long timeout, int maxDigits)
{
this.file = file;
this.timeout = timeout;
this.MaxDigits = maxDigits;
}
public override string BuildCommand()
{
if (maxDigits == Common.AGI_DEFAULT_MAX_DIGITS)
{
if (timeout == Common.AGI_DEFAULT_TIMEOUT)
return "GET DATA " + EscapeAndQuote(file);
else
return "GET DATA " + EscapeAndQuote(file) + " " + timeout;
}
return "GET DATA " + EscapeAndQuote(file) + " " + timeout + " " + maxDigits;
}
}
}

View file

@ -0,0 +1,73 @@
using System;
using System.Text;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Returns the value of the given channel varible and understands complex
/// variable names and builtin variables, unlike the GetVariableCommand.<br/>
/// You can also use this command to use custom Asterisk functions. Syntax is "func(args)".<br/>
/// Returns 0 if the variable is not set or channel does not exist. Returns 1 if
/// the variable is set and returns the variable in parenthesis.<br/>
/// Example return code: 200 result=1 (testvariable)
/// Available since Asterisk 1.2
/// </summary>
public class GetFullVariableCommand : AGICommand
{
private string varName;
private string channel;
/// <summary>
/// Creates a new GetFullVariableCommand.
/// </summary>
/// <param name="variable">the name of the variable to retrieve.</param>
public GetFullVariableCommand(string variable)
{
this.varName = variable;
}
/// <summary>
/// Creates a new GetFullVariableCommand.
/// </summary>
/// <param name="variable">the name of the variable to retrieve.</param>
/// <param name="channel">the name of the channel.</param>
public GetFullVariableCommand(string variable, string channel)
{
this.varName = variable;
this.channel = channel;
}
/// <summary>
/// Get/Set the name of the variable to retrieve.
/// </summary>
public string Variable
{
get { return varName; }
set { this.varName = value;}
}
/// <summary>
/// Get/Set the name of the channel.
/// </summary>
public string Channel
{
get { return channel; }
set { this.channel = value; }
}
public override string BuildCommand()
{
StringBuilder sb;
sb = new StringBuilder("GET FULL VARIABLE ");
sb.Append(EscapeAndQuote(varName));
if (channel != null)
{
sb.Append(" ");
sb.Append(EscapeAndQuote(channel));
}
return sb.ToString();
}
}
}

View file

@ -0,0 +1,79 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Plays the given file, and waits for the user to press one of the given
/// digits. If none of the esacpe digits is pressed while streaming the file this
/// command waits for the specified timeout still waiting for the user to press a
/// digit. Streaming always begins at the beginning.<br/>
/// Returns 0 if no digit being pressed, or the ASCII numerical value of the
/// digit if one was pressed, or -1 on error or if the channel was disconnected.
/// <br/>
/// Remember, the file extension must not be included in the filename.
/// </summary>
/// <seealso cref="FastAGI.Command.StreamFileCommand" />
public class GetOptionCommand : AGICommand
{
/// <summary> The name of the file to stream.</summary>
private string file;
/// <summary> When one of these digits is pressed while streaming the command returns.</summary>
private string escapeDigits;
/// <summary> The timeout in seconds.</summary>
private int timeout;
/// <summary>
/// Get/Set the name of the file to stream.
/// </summary>
public string File
{
get { return file; }
set { this.file = value; }
}
/// <summary>
/// Get/Set the digits that the user is expected to press.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Get/Set the timeout to wait if none of the defined esacpe digits was presses while streaming.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
/// <summary>
/// Creates a new GetOptionCommand with a default timeout of 5 seconds.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that the user is expected to press.</param>
public GetOptionCommand(string file, string escapeDigits)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.timeout = - 1;
}
/// <summary>
/// Creates a new GetOptionCommand with the given timeout.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that the user is expected to press.</param>
/// <param name="timeout">the timeout in seconds to wait if none of the defined esacpe digits was presses while streaming.</param>
public GetOptionCommand(string file, string escapeDigits, int timeout)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.timeout = timeout;
}
public override string BuildCommand()
{
return "GET OPTION " + EscapeAndQuote(file) + " " + EscapeAndQuote(escapeDigits) + (timeout < 0?"":" " + timeout);
}
}
}

View file

@ -0,0 +1,38 @@
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Returns the value of the given channel varible.<br/>
/// Since Asterisk 1.2 you can also use this command to use custom Asterisk functions. Syntax is "func(args)".<br/>
/// Returns 0 if the variable is not set. Returns 1 if the variable is set and returns the variable in parenthesis.<br/>
/// Example return code: 200 result=1 (testvariable)
/// </summary>
public class GetVariableCommand : AGICommand
{
/// <summary> The name of the variable to retrieve.</summary>
private string varName;
/// <summary>
/// Get/Set the name of the variable to retrieve.<br>
/// Since Asterisk 1.2 you can also use custom dialplan functions (like "func(args)") as variable.
/// </summary>
public string Variable
{
get { return varName; }
set { this.varName = value; }
}
/// <summary>
/// Creates a new GetVariableCommand.
/// </summary>
/// <param name="variable">the name of the variable to retrieve.</param>
public GetVariableCommand(string variable)
{
this.varName = variable;
}
public override string BuildCommand()
{
return "GET VARIABLE " + EscapeAndQuote(varName);
}
}
}

View file

@ -0,0 +1,47 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Hangs up the specified channel. If no channel name is given, hangs up the current channel.
/// </summary>
public class HangupCommand : AGICommand
{
/// <summary>
/// The name of the channel to hangup or <code>null</code> for the current channel.
/// </summary>
private string channel;
/// <summary>
/// Returns the name of the channel to hangup.
/// </summary>
/// <returns>the name of the channel to hangup or <code>null</code> for the current channel.</returns>
/// <summary> Sets the name of the channel to hangup.</summary>
/// <param name="channel">the name of the channel to hangup or <code>null</code> for the current channel.</param>
public string Channel
{
get { return channel; }
set { this.channel = value; }
}
/// <summary> Creates a new HangupCommand that hangs up the current channel.</summary>
public HangupCommand()
{
this.channel = null;
}
/// <summary>
/// Creates a new HangupCommand that hangs up the given channel.
/// </summary>
/// <param name="channel">the name of the channel to hangup.</param>
public HangupCommand(string channel)
{
this.channel = channel;
}
public override string BuildCommand()
{
return "HANGUP" + (channel == null ? "" : " " + EscapeAndQuote(channel));
}
}
}

View file

@ -0,0 +1,20 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Does nothing.
/// </summary>
public class NoopCommand : AGICommand
{
/// <summary>
/// Creates a new NoopCommand.
/// </summary>
public NoopCommand()
{
}
public override string BuildCommand()
{
return "NOOP";
}
}
}

View file

@ -0,0 +1,47 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Receives a character of text on a channel.<br/>
/// Specify timeout to be the maximum time to wait for input in milliseconds, or 0 for infinite.<br/>
/// Most channels do not support the reception of text.<br/>
/// Returns the decimal value of the character if one is received, or 0 if the
/// channel does not support text reception. Returns -1 only on error/hangup.
/// </summary>
public class ReceiveCharCommand : AGICommand
{
/// <summary> The milliseconds to wait for the channel to receive a character.</summary>
private int timeout;
/// <summary>
/// Get/Set the milliseconds to wait for the channel to receive a character.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
/// <summary>
/// Creates a new ReceiveCharCommand with a default timeout of 0 meaning to wait for ever.
/// </summary>
public ReceiveCharCommand()
{
this.timeout = 0;
}
/// <summary>
/// Creates a new ReceiveCharCommand.
/// </summary>
/// <param name="timeout">the milliseconds to wait for the channel to receive a character.</param>
public ReceiveCharCommand(int timeout)
{
this.timeout = timeout;
}
public override string BuildCommand()
{
return "RECEIVE CHAR " + timeout;
}
}
}

View file

@ -0,0 +1,49 @@
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Receives a string of text on a channel.<br/>
/// Specify timeout to be the maximum time to wait for input in milliseconds, or
/// 0 for infinite.<br/>
/// Most channels do not support the reception of text.<br/>
/// Returns -1 for failure or 1 for success, and the string in parentheses.<br/>
/// Available since Asterisk 1.2.
/// </summary>
public class ReceiveTextCommand : AGICommand
{
/// <summary>
/// The milliseconds to wait for the channel to receive a character.
/// </summary>
private int timeout;
/// <summary>
/// Creates a new ReceiveTextCommand with a default timeout of 0 meaning to wait for ever.
/// </summary>
public ReceiveTextCommand()
{
this.timeout = 0;
}
/// <summary>
/// Creates a new ReceiveTextCommand.
/// <param name=timeout>the milliseconds to wait for the channel to receive the text.</param>
/// </summary>
public ReceiveTextCommand(int timeout)
{
this.timeout = timeout;
}
/// <summary>
/// Get/Set the milliseconds to wait for the channel to receive the text.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
public override string BuildCommand()
{
return "RECEIVE TEXT " + timeout;
}
}
}

View file

@ -0,0 +1,143 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Record to a file until a given dtmf digit in the sequence is received.<br/>
/// Returns -1 on hangup or error.<br/>
/// The format will specify what kind of file will be recorded. The timeout is
/// the maximum record time in milliseconds, or -1 for no timeout. Offset samples
/// is optional, and if provided will seek to the offset without exceeding the
/// end of the file. "maxSilence" is the number of seconds of maxSilence allowed
/// before the function returns despite the lack of dtmf digits or reaching
/// timeout.
/// </summary>
public class RecordFileCommand : AGICommand
{
#region Variables
/// <summary> The name of the file to record.</summary>
private string file;
/// <summary> The format of the file to be recorded, for example "wav".</summary>
private string format;
/// <summary> The these digits a user can press to end the recording.</summary>
private string escapeDigits;
/// <summary> The maximum record time in milliseconds, or -1 for no timeout.</summary>
private int timeout;
/// <summary> The offset samples to skip.</summary>
private int offset;
/// <summary> Wheather a beep should be played before recording.</summary>
private bool beep;
/// <summary> The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout.</summary>
private int maxSilence;
#endregion
#region File
/// <summary>
/// Get/Set the name of the file to stream.
/// </summary>
public string File
{
get { return file; }
set { this.file = value; }
}
#endregion
#region Format
/// <summary>
/// Get/Set the format of the file to be recorded, for example "wav".
/// </summary>
public string Format
{
get { return format; }
set { this.format = value; }
}
#endregion
#region EscapeDigits
/// <summary>
/// Get/Set the digits that allow the user to end recording.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
#endregion
#region Timeout
/// <summary>
/// Get/Set the maximum record time in milliseconds, or -1 for no timeout.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
#endregion
#region Offset
/// <summary>
/// Get/Set the offset samples to skip.
/// </summary>
public int Offset
{
get { return offset; }
set { this.offset = value; }
}
#endregion
#region Beep
/// <summary>
/// Get/Set <code>true</code> if a beep should be played before recording. <code>false</code> if not.
/// </summary>
public bool Beep
{
get { return beep; }
set { this.beep = value; }
}
#endregion
#region Constructor - RecordFileCommand(string file, string format, string escapeDigits, int timeout)
/// <summary>
/// Creates a new RecordFileCommand.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="format">the format of the file to be recorded, for example "wav".</param>
/// <param name="escapeDigits">contains the digits that allow the user to end recording.</param>
/// <param name="timeout">the maximum record time in milliseconds, or -1 for no timeout.</param>
public RecordFileCommand(string file, string format, string escapeDigits, int timeout)
{
this.file = file;
this.format = format;
this.escapeDigits = escapeDigits;
this.timeout = timeout;
this.offset = 0;
this.beep = false;
this.maxSilence = 0;
}
#endregion
#region Constructor - RecordFileCommand(string file, string format, string escapeDigits, int timeout)
/// <summary>
/// Creates a new RecordFileCommand.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="format">the format of the file to be recorded, for example "wav".</param>
/// <param name="escapeDigits">contains the digits that allow the user to end recording.</param>
/// <param name="timeout">the maximum record time in milliseconds, or -1 for no timeout.</param>
/// <param name="offset">the offset samples to skip.</param>
/// <param name="beep"><code>true</code> if a beep should be played before recording.</param>
/// <param name="maxSilence">The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout.</param>
public RecordFileCommand(string file, string format, string escapeDigits, int timeout, int offset, bool beep, int maxSilence)
{
this.file = file;
this.format = format;
this.escapeDigits = escapeDigits;
this.timeout = timeout;
this.offset = offset;
this.beep = beep;
this.maxSilence = maxSilence;
}
#endregion
#region BuildCommand()
public override string BuildCommand()
{
return "RECORD FILE " + EscapeAndQuote(file) + " " + EscapeAndQuote(format) + " " + EscapeAndQuote(escapeDigits) + " " + timeout + " " + offset + (beep == true?" BEEP":"") + " s=" + maxSilence;
}
#endregion
}
}

View file

@ -0,0 +1,58 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary> Say a given character string, returning early if any of the given DTMF digits are received on the channel.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed or -1 on error/hangup.
/// </summary>
public class SayAlphaCommand : AGICommand
{
/// <summary> The text to say.</summary>
private string text;
/// <summary> When one of these digits is pressed the command returns.</summary>
private string escapeDigits;
/// <summary>
/// Get/Set Returns the text to say.
/// </summary>
public string Text
{
get { return text; }
set { this.text = value; }
}
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Creates a new SayAlphaCommand.
/// </summary>
/// <param name="text">the text to say.</param>
public SayAlphaCommand(string text)
{
this.text = text;
this.escapeDigits = null;
}
/// <summary>
/// Creates a new SayAlphaCommand.
/// </summary>
/// <param name="text">the text to say.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
public SayAlphaCommand(string text, string escapeDigits)
{
this.text = text;
this.escapeDigits = escapeDigits;
}
public override string BuildCommand()
{
return "SAY ALPHA " + EscapeAndQuote(text) + " " + EscapeAndQuote(escapeDigits);
}
}
}

View file

@ -0,0 +1,133 @@
using System.Text;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Say a given time, returning early if any of the given DTMF digits are pressed.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed or -1 on error/hangup.<br/>
/// Available since Asterisk 1.2.
/// </summary>
public class SayDateTimeCommand : AGICommand
{
private static string DEFAULT_FORMAT = "ABdY 'digits/at' IMp";
private long time;
private string escapeDigits;
private string format;
private string timezone;
/// <summary>
/// Creates a new SayDateTimeCommand that says the given time.
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// </summary>
public SayDateTimeCommand(long time)
{
this.time = time;
}
/// <summary>
/// Creates a new SayDateTimeCommand that says the given time and allows interruption by one of the given escape digits.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
public SayDateTimeCommand(long time, string escapeDigits)
{
this.time = time;
this.escapeDigits = escapeDigits;
}
/// <summary>
/// Creates a new SayDateTimeCommand that says the given time in the given
/// format and allows interruption by one of the given escape digits.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
/// <param name="format">the format the time should be said in</param>
public SayDateTimeCommand(long time, string escapeDigits, string format)
{
this.time = time;
this.escapeDigits = escapeDigits;
this.format = format;
}
/// <summary>
/// Creates a new SayDateTimeCommand that says the given time in the given
/// format and timezone and allows interruption by one of the given escape
/// digits.
/// </summary>
/// <param name="time">the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC)</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command or <code>null</code> for none.</param>
/// <param name="format">the format the time should be said in</param>
/// <param name="timezone">the timezone to use when saying the time, for example "UTC" or "Europe/Berlin".</param>
public SayDateTimeCommand(long time, string escapeDigits, string format, string timezone)
{
this.time = time;
this.escapeDigits = escapeDigits;
this.format = format;
this.timezone = timezone;
}
/// <summary>
/// Get/Set the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
/// </summary>
public long Time
{
get { return this.time; }
set { this.time = value; }
}
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string getEscapeDigits
{
get { return this.escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Get/Set the format the time should be said in.
/// </summary>
public string Format
{
get { return this.format; }
set { this.format = value; }
}
/// <summary>
/// Get/Set the timezone to use when saying the time.
/// </summary>
public string Timezone
{
get { return this.timezone; }
set { this.timezone = value; }
}
public override string BuildCommand()
{
StringBuilder sb = new StringBuilder("SAY DATETIME ");
sb.Append(time);
sb.Append(" ");
sb.Append(EscapeAndQuote(escapeDigits));
if (format == null && timezone != null)
{
sb.Append(" ");
sb.Append(EscapeAndQuote(DEFAULT_FORMAT));
}
if (format != null)
{
sb.Append(" ");
sb.Append(EscapeAndQuote(format));
}
if (timezone != null)
{
sb.Append(" ");
sb.Append(EscapeAndQuote(timezone));
}
return sb.ToString();
}
}
}

View file

@ -0,0 +1,70 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Say a given digit string, returning early if any of the given DTMF digits are received on the channel.<br/>
/// Returns 0 if playback completes without a digit being pressed,
/// or the ASCII numerical value of the digit if one was pressed or -1 on error/hangup.
/// </summary>
public class SayDigitsCommand : AGICommand
{
#region Variables
/// <summary> The digits string to say.</summary>
private string digits;
/// <summary> When one of these digits is pressed while saying the digits the command returns.</summary>
private string escapeDigits;
#endregion
#region Digits
/// <summary>
/// Get/Set the digits string to say.
/// </summary>
public string Digits
{
get { return digits; }
set { this.digits = value; }
}
#endregion
#region EscapeDigits
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
#endregion
#region Constructor - SayDigitsCommand(string digits)
/// <summary>
/// Creates a new SayDigitsCommand.
/// </summary>
/// <param name="digits">the digits to say.</param>
public SayDigitsCommand(string digits)
{
this.digits = digits;
this.escapeDigits = null;
}
#endregion
#region Constructor - SayDigitsCommand(string digits, string escapeDigits)
/// <summary>
/// Creates a new SayDigitsCommand.
/// </summary>
/// <param name="digits">the digits to say.</param>
/// <param name="escapeDigits">the digits that allow the user to interrupt this command.</param>
public SayDigitsCommand(string digits, string escapeDigits)
{
this.digits = digits;
this.escapeDigits = escapeDigits;
}
#endregion
#region BuildCommand()
public override string BuildCommand()
{
return "SAY DIGITS " + EscapeAndQuote(digits) + " " + EscapeAndQuote(escapeDigits);
}
#endregion
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Say a given number, returning early if any of the given DTMF number are received on the channel.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed or -1 on error/hangup.
/// </summary>
public class SayNumberCommand : AGICommand
{
/// <summary> The number to say.</summary>
private string number;
/// <summary> When one of these number is pressed while streaming the command returns.</summary>
private string escapeDigits;
/// <summary>
/// Get/Set the number to say.
/// </summary>
public string Number
{
get { return number; }
set { this.number = value; }
}
/// <summary>
/// Get/Set the number that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Creates a new SayNumberCommand.
/// </summary>
/// <param name="number">the number to say.</param>
public SayNumberCommand(string number)
{
this.number = number;
this.escapeDigits = null;
}
/// <summary>
/// Creates a new SayNumberCommand.
/// </summary>
/// <param name="number">the number to say.</param>
/// <param name="escapeDigits">contains the number that allow the user to interrupt this command.</param>
public SayNumberCommand(string number, string escapeDigits)
{
this.number = number;
this.escapeDigits = escapeDigits;
}
public override string BuildCommand()
{
return "SAY NUMBER " + EscapeAndQuote(number) + " " + EscapeAndQuote(escapeDigits);
}
}
}

View file

@ -0,0 +1,60 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Say a given character string with phonetics, returning early if any of the given DTMF digits are received on the channel.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed or -1 on error/hangup.
/// </summary>
public class SayPhoneticCommand : AGICommand
{
/// <summary> The text to say.</summary>
private string text;
/// <summary> When one of these digits is pressed the command returns.</summary>
private string escapeDigits;
/// <summary>
/// Get/Set the text to say.
/// </summary>
public string Text
{
get { return text; }
set { this.text = value; }
}
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Creates a new SayPhonticCommand.
/// </summary>
/// <param name="text">the text to say.</param>
public SayPhoneticCommand(string text)
{
this.text = text;
this.escapeDigits = null;
}
/// <summary>
/// Creates a new SayPhoneticCommand.
/// </summary>
/// <param name="text">the text to say.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
public SayPhoneticCommand(string text, string escapeDigits)
{
this.text = text;
this.escapeDigits = escapeDigits;
}
public override string BuildCommand()
{
return "SAY PHONETIC " + EscapeAndQuote(text) + " " + EscapeAndQuote(escapeDigits);
}
}
}

View file

@ -0,0 +1,60 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Say a given time, returning early if any of the given DTMF digits are received on the channel.<br/>
/// Time is the number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed or -1 on error/hangup.
/// </summary>
public class SayTimeCommand : AGICommand
{
/// <summary> The time to say in seconds since 00:00:00 on January 1, 1970.</summary>
private long time;
/// <summary> When one of these digits is pressed the command returns.</summary>
private string escapeDigits;
/// <summary>
/// Get/Set the time to say in seconds since 00:00:00 on January 1, 1970.
/// </summary>
public long Time
{
get { return time; }
set { this.time = value; }
}
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get { return escapeDigits; }
set { this.escapeDigits = value; }
}
/// <summary>
/// Creates a new SayTimeCommand.
/// </summary>
/// <param name="time">the time to say in seconds since 00:00:00 on January 1, 1970.</param>
public SayTimeCommand(long time)
{
this.time = time;
this.escapeDigits = null;
}
/// <summary>
/// Creates a new SayTimeCommand.
/// </summary>
/// <param name="time">the time to say in seconds since 00:00:00 on January 1, 1970.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
public SayTimeCommand(long time, string escapeDigits)
{
this.time = time;
this.escapeDigits = escapeDigits;
}
public override string BuildCommand()
{
return "SAY TIME " + time + " " + EscapeAndQuote(escapeDigits);
}
}
}

View file

@ -0,0 +1,42 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sends the given image on a channel.<br/>
/// Most channels do not support the transmission of images.<br/>
/// Returns 0 if image is sent, or if the channel does not support image
/// transmission. Returns -1 only on error/hangup.<br/>
/// Image names should not include extensions.
/// </summary>
public class SendImageCommand : AGICommand
{
/// <summary>
/// Get/Set the image to send.
/// </summary>
/// <param name="image">the image to send, should not include extension.</param>
/// <returns> the image to send.</returns>
public string Image
{
get { return image; }
set { this.image = value; }
}
/// <summary> The name of the image to send.</summary>
private string image;
/// <summary>
/// Creates a new SendImageCommand.
/// </summary>
/// <param name="image">the image to send, should not include extension.</param>
public SendImageCommand(string image)
{
this.image = image;
}
public override string BuildCommand()
{
return "SEND IMAGE " + EscapeAndQuote(image);
}
}
}

View file

@ -0,0 +1,40 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sends the given text on a channel.<br/>
/// Most channels do not support the transmission of text.<br/>
/// Returns 0 if text is sent, or if the channel does not support text
/// transmission. Returns -1 only on error/hangup.
/// </summary>
public class SendTextCommand : AGICommand
{
/// <summary> The text to send.</summary>
private string text;
/// <summary>
/// Get/Set the text to send.
/// </summary>
/// <param name="text">the text to send.</param>
/// <returns>the text to send.</returns>
public string Text
{
get { return text; }
set { this.text = value; }
}
/// <summary>
/// Creates a new SendTextCommand.
/// </summary>
/// <param name="text">the text to send.</param>
public SendTextCommand(string text)
{
this.text = text;
}
public override string BuildCommand()
{
return "SEND TEXT " + EscapeAndQuote(text);
}
}
}

View file

@ -0,0 +1,45 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Cause the channel to automatically hangup at the given number of seconds in the future.<br/>
/// Of course it can be hungup before then as well. Setting to 0 will cause the
/// autohangup feature to be disabled on this channel.
/// </summary>
public class SetAutoHangupCommand : AGICommand
{
/// <summary> The number of seconds before this channel is automatically hung up.</summary>
private int time;
/// <summary>
/// Get/Set the number of seconds before this channel is automatically hung up.
/// </summary>
/// <returns>the number of seconds before this channel is automatically hung up.</returns>
/// <param name="time">
/// the number of seconds before this channel is automatically hung up.<br/>
/// 0 disables the autohangup feature.
/// </param>
public int Time
{
get { return time; }
set { this.time = value; }
}
/// <summary>
/// Creates a new SetAutoHangupCommand.
/// </summary>
/// <param name="time">
/// the number of seconds before this channel is automatically hung up.<br/>
/// 0 disables the autohangup feature.
/// </param>
public SetAutoHangupCommand(int time)
{
this.time = time;
}
public override string BuildCommand()
{
return "SET AUTOHANGUP " + time;
}
}
}

View file

@ -0,0 +1,35 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Changes the CallerID of the current channel.
/// </summary>
public class SetCallerIdCommand : AGICommand
{
/// <summary> The new callerId.</summary>
private string callerId;
/// <summary>
/// Get/Set the new callerId.
/// </summary>
public string CallerId
{
get { return callerId; }
set { this.callerId = value; }
}
/// <summary>
/// Creates a new SetCallerIdCommand.
/// </summary>
/// <param name="callerId">the new callerId.</param>
public SetCallerIdCommand(string callerId)
{
this.callerId = callerId;
}
public override string BuildCommand()
{
return "SET CALLERID " + EscapeAndQuote(callerId);
}
}
}

View file

@ -0,0 +1,35 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sets the context for continuation upon exiting the application.
/// </summary>
public class SetContextCommand : AGICommand
{
/// <summary> The context for continuation upon exiting the application.</summary>
private string context;
/// <summary>
/// Get/Set the context for continuation upon exiting the application.
/// </summary>
public string Context
{
get { return context; }
set { this.context = value; }
}
/// <summary>
/// Creates a new SetPriorityCommand.
/// </summary>
/// <param name="context">the context for continuation upon exiting the application.</param>
public SetContextCommand(string context)
{
this.context = context;
}
public override string BuildCommand()
{
return "SET CONTEXT " + EscapeAndQuote(context);
}
}
}

View file

@ -0,0 +1,37 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sets the extension for continuation upon exiting the application.
/// </summary>
public class SetExtensionCommand : AGICommand
{
/// <summary>
/// Get/Set the extension for continuation upon exiting the application.
/// </summary>
/// <returns>the extension for continuation upon exiting the application.</returns>
/// <param name="extension">the extension for continuation upon exiting the application.</param>
public string Extension
{
get { return extension; }
set { this.extension = value; }
}
/// <summary> The extension for continuation upon exiting the application.</summary>
private string extension;
/// <summary>
/// Creates a new SetPriorityCommand.
/// </summary>
/// <param name="extension">the extension for continuation upon exiting the application.</param>
public SetExtensionCommand(string extension)
{
this.extension = extension;
}
public override string BuildCommand()
{
return "SET EXTENSION " + EscapeAndQuote(extension);
}
}
}

View file

@ -0,0 +1,20 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Turns off music on hold on the current channel.<br/>
/// Always returns 0.
/// </summary>
public class SetMusicOffCommand : AGICommand
{
/// <summary> Creates a new SetMusicOffCommand.</summary>
public SetMusicOffCommand()
{
}
public override string BuildCommand()
{
return "SET MUSIC OFF";
}
}
}

View file

@ -0,0 +1,44 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Turns on music on hold on the current channel.<br/>
/// Always returns 0.
/// </summary>
public class SetMusicOnCommand : AGICommand
{
/// <summary>
/// Get/Set the music on hold class to play music from.
/// </summary>
/// <returns>the music on hold class to play music from or <code>null</code> for the default class.</returns>
/// <param name="musicOnHoldClass">the music on hold class to play music from or <code>null</code> for the default class.</param>
public string MusicOnHoldClass
{
get { return musicOnHoldClass; }
set { this.musicOnHoldClass = value; }
}
/// <summary> The music on hold class to play music from.</summary>
private string musicOnHoldClass;
/// <summary> Creates a new SetMusicOnCommand playing music from the default music on hold class.</summary>
public SetMusicOnCommand()
{
this.musicOnHoldClass = null;
}
/// <summary>
/// Creates a new SetMusicOnCommand playing music from the default music on hold class.
/// </summary>
/// <param name="musicOnHoldClass">the music on hold class to play music from.</param>
public SetMusicOnCommand(string musicOnHoldClass)
{
this.musicOnHoldClass = musicOnHoldClass;
}
public override string BuildCommand()
{
return "SET MUSIC ON" + (musicOnHoldClass == null?"":" " + EscapeAndQuote(musicOnHoldClass));
}
}
}

View file

@ -0,0 +1,62 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sets the priority for continuation upon exiting the application.<br/>
/// Since Asterisk 1.2 SetPriorityCommand also supports labels.
/// </summary>
public class SetPriorityCommand : AGICommand
{
/// <summary> The priority or label for continuation upon exiting the application.</summary>
private string priorityOrLabel;
/// <summary>
/// Get/Set the priority or label for continuation upon exiting the application.
/// </summary>
public int Priority
{
get
{
try
{
return Int32.Parse(this.priorityOrLabel);
}
catch {}
return 0;
}
set { this.priorityOrLabel = value.ToString(); }
}
/// <summary>
/// Get/Set the label for continuation upon exiting the application.
/// </summary>
public string Label
{
get { return this.priorityOrLabel; }
set { this.priorityOrLabel = value; }
}
/// <summary>
/// Creates a new SetPriorityCommand.
/// </summary>
/// <param name="priority">the priority for continuation upon exiting the application.</param>
public SetPriorityCommand(int priority)
{
this.priorityOrLabel = priority.ToString();
}
/// <summary>
/// Creates a new SetPriorityCommand.
/// </summary>
/// <param name="label">the label for continuation upon exiting the application.</param>
public SetPriorityCommand(string label)
{
this.priorityOrLabel = label;
}
public override string BuildCommand()
{
return "SET PRIORITY " + EscapeAndQuote(priorityOrLabel);
}
}
}

View file

@ -0,0 +1,47 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Sets the given channel variable to the given value.
/// </summary>
public class SetVariableCommand : AGICommand
{
/// <summary> The name of the variable to set.</summary>
private string varName;
/// <summary> The value to set.</summary>
private string varValue;
/// <summary>
/// Get/Set the name of the variable to set.
/// </summary>
public string Variable
{
get { return varName; }
set { this.varName = value; }
}
/// <summary>
/// Get/Set the value to set.
/// </summary>
public string Value
{
get { return varValue; }
set { this.varValue = value; }
}
/// <summary>
/// Creates a new GetVariableCommand.
/// </summary>
/// <param name="variable">the name of the variable to set.</param>
/// <param name="value">the value to set.</param>
public SetVariableCommand(string name, string value)
{
this.varName = name;
this.varValue = value;
}
public override string BuildCommand()
{
return "SET VARIABLE " + EscapeAndQuote(varName) + " " + EscapeAndQuote(varValue);
}
}
}

View file

@ -0,0 +1,122 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Plays the given file, allowing playback to be interrupted by the given digits, if any.<br/>
/// If offset is provided then the audio will seek to sample offset before play starts.<br/>
/// Returns 0 if playback completes without a digit being pressed, or the ASCII
/// numerical value of the digit if one was pressed, or -1 on error or if the
/// channel was disconnected. <br/>
/// Remember, the file extension must not be included in the filename.
/// </summary>
public class StreamFileCommand : AGICommand
{
#region File
/// <summary>
/// Get/Set the name of the file to stream.
/// The name of the file to stream, must not include extension.
/// </summary>
public string File
{
get
{
return file;
}
set
{
this.file = value;
}
}
#endregion
#region EscapeDigits
/// <summary>
/// Get/Set the digits that allow the user to interrupt this command.
/// </summary>
public string EscapeDigits
{
get
{
return escapeDigits;
}
set
{
this.escapeDigits = value;
}
}
#endregion
#region Offset
/// <summary>
/// Get/Set the offset samples to skip before streaming.
/// </summary>
public int Offset
{
get
{
return offset;
}
set
{
this.offset = value;
}
}
#endregion
/// <summary> The name of the file to stream.</summary>
private string file;
/// <summary> When one of these digits is pressed while streaming the command returns.</summary>
private string escapeDigits;
/// <summary> The offset samples to skip before streaming.</summary>
private int offset;
/// <summary> Creates a new StreamFileCommand, streaming from the beginning.
///
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.
/// </param>
public StreamFileCommand(string file)
{
this.file = file;
this.escapeDigits = null;
this.offset = - 1;
}
/// <summary>
/// Creates a new StreamFileCommand, streaming from the beginning.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.</param>
public StreamFileCommand(string file, string escapeDigits)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.offset = - 1;
}
/// <summary>
/// Creates a new StreamFileCommand, streaming from the given offset.
/// </summary>
/// <param name="file">the name of the file to stream, must not include extension.</param>
/// <param name="escapeDigits">contains the digits that allow the user to interrupt this command.
/// Maybe <code>null</code> if you don't want the user to interrupt.
/// </param>
/// <param name="offset">the offset samples to skip before streaming.</param>
public StreamFileCommand(string file, string escapeDigits, int offset)
{
this.file = file;
this.escapeDigits = escapeDigits;
this.offset = offset;
}
public override string BuildCommand()
{
return "STREAM FILE " + EscapeAndQuote(file) + " " + EscapeAndQuote(escapeDigits) + (offset < 0?"":" " + offset);
}
}
}

View file

@ -0,0 +1,40 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Enable/Disable TDD transmission/reception on a channel.<br/>
/// Returns 1 if successful, or 0 if channel is not TDD-capable.
/// </summary>
public class TDDModeCommand : AGICommand
{
private string mode;
/// <summary>
/// Get the mode to set.
/// </summary>
public string Mode
{
get { return mode; }
}
/// <summary>
/// Sets the mode to set. The mode to set, this can be one of "on", "off", "mate" or "tdd".
/// </summary>
public string Timeout
{
set { this.mode = value; }
}
/// <summary>
/// Creates a new TDDModeCommand. The mode to set, this can be one of "on", "off", "mate" or "tdd".
/// </summary>
public TDDModeCommand(string mode)
{
this.mode = mode;
}
public override string BuildCommand()
{
return "TDD MODE " + EscapeAndQuote(mode);
}
}
}

View file

@ -0,0 +1,71 @@
using System;
namespace Asterisk.NET.FastAGI.Command
{
#region Class - VerboseCommand
/// <summary>
/// Sends a message to the console via the verbose message system.<br/>
/// Always returns 1.
/// </summary>
public class VerboseCommand : AGICommand
{
#region Variables
/// <summary> The message to send.</summary>
private string message;
/// <summary> The verbosity level to use.<br/>
/// Must be in [1..4]
/// </summary>
private int level;
#endregion
#region Message
/// <summary>
/// Get/Set the message to send.
/// </summary>
public string Message
{
get { return message; }
set { this.message = value; }
}
#endregion
#region Level
/// <summary>
/// Get/Set the level to use.
/// </summary>
/// <throws> IllegalArgumentException if level is not in [1..4] </throws>
public int Level
{
get { return level; }
set
{
if (value < 1 || value > 4)
{
throw new ArgumentException("level must be in [1..4]");
}
this.level = value;
}
}
#endregion
#region Constructor - VerboseCommand(string message, int level)
/// <summary>
/// Creates a new VerboseCommand.
/// </summary>
/// <param name="message">the message to send.</param>
/// <param name="level">the verbosity level to use. Must be in [1..4]</param>
/// <throws> IllegalArgumentException if level is not in [1..4] </throws>
public VerboseCommand(string message, int level)
{
this.Message = message;
this.Level = level;
}
#endregion
#region BuildCommand()
public override string BuildCommand()
{
return "VERBOSE " + EscapeAndQuote(message) + " " + level;
}
#endregion
}
#endregion
}

View file

@ -0,0 +1,45 @@
namespace Asterisk.NET.FastAGI.Command
{
/// <summary>
/// Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.<br/>
/// Returns -1 on channel failure, 0 if no digit is received in the timeout, or
/// the numerical value of the ascii of the digit if one is received. Use -1 for
/// the timeout value if you desire the call to block indefinitely.
/// </summary>
public class WaitForDigitCommand : AGICommand
{
/// <summary> The milliseconds to wait for the channel to receive a DTMF digit.</summary>
private long timeout;
/// <summary>
/// Get/Set the milliseconds to wait for the channel to receive a DTMF digit.
/// </summary>
public long Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
/// <summary>
/// Creates a new WaitForDigitCommand with a default timeout of -1 which blocks the channel indefinitely.
/// </summary>
public WaitForDigitCommand()
{
this.timeout = - 1;
}
/// <summary>
/// Creates a new WaitForDigitCommand.
/// </summary>
/// <param name="timeout">the milliseconds to wait for the channel to receive a DTMF digit.</param>
public WaitForDigitCommand(int timeout)
{
this.timeout = timeout;
}
public override string BuildCommand()
{
return "WAIT FOR DIGIT " + timeout.ToString();
}
}
}

View file

@ -0,0 +1,33 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// Abstract base class for all AGI specific exceptions.
/// </summary>
public class AGIException : Exception
{
public AGIException()
: base()
{
}
/// <summary>
/// Creates a new AGIExeption with the given message.
/// </summary>
/// <param name="message">a message decribing the AGIException.</param>
public AGIException(string message)
: base(message)
{
}
/// <summary>
/// Creates a new AGIExeption with the given message and cause.
/// </summary>
/// <param name="message">a message decribing the AGIException.</param>
/// <param name="cause">the throwable that caused this exception to be thrown.</param>
public AGIException(string message, Exception cause)
: base(message, cause)
{
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// The AGIHangupException is thrown if the channel has been hang up while processing the AGIRequest.
/// </summary>
public class AGIHangupException : AGIException
{
public AGIHangupException()
: base("Channel was hung up.")
{
}
}
}

View file

@ -0,0 +1,14 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// The AGINetworkException usally wraps an IOException denoting a network problem when talking to the Asterisk server.
/// </summary>
public class AGINetworkException : AGIException
{
public AGINetworkException(string message, Exception cause)
: base(message, cause)
{
}
}
}

View file

@ -0,0 +1,41 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// An InvalidCommandSyntaxException is thrown when the reader receives a reply with status code 520.
/// </summary>
public class InvalidCommandSyntaxException : AGIException
{
/// <summary>
/// Returns the synopsis of the command that was called with invalid syntax.
/// </summary>
/// <returns>the synopsis of the command that was called with invalid syntax.</returns>
public string Synopsis
{
get { return synopsis; }
}
/// <summary>
/// Returns a description of the command that was called with invalid syntax.
/// </summary>
/// <returns>a description of the command that was called with invalid syntax.</returns>
public string Usage
{
get { return usage; }
}
private string synopsis;
private string usage;
/// <summary>
/// Creates a new InvalidCommandSyntaxException with the given synopsis and usage.
/// </summary>
/// <param name="synopsis">the synopsis of the command.</param>
/// <param name="usage">the usage of the command.</param>
public InvalidCommandSyntaxException(string synopsis, string usage)
: base("Invalid command syntax: " + synopsis)
{
this.synopsis = synopsis;
this.usage = usage;
}
}
}

View file

@ -0,0 +1,19 @@
using System;
namespace Asterisk.NET.FastAGI
{
/// <summary>
/// An InvalidOrUnknownCommandException is thrown when the reader receives a reply with status code 510.
/// </summary>
public class InvalidOrUnknownCommandException : AGIException
{
/// <summary>
/// Creates a new InvalidOrUnknownCommandException.
/// </summary>
/// <param name="command">the invalid or unknown command.</param>
public InvalidOrUnknownCommandException(string command)
: base("Invalid or unknown command: " + command)
{
}
}
}

View file

@ -0,0 +1,128 @@
using System;
using System.Collections;
using System.Resources;
using System.Reflection;
namespace Asterisk.NET.FastAGI
{
/// <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 MappingStrategy
{
#if LOGGER
private Logger logger = Logger.Instance();
#endif
private string resourceName;
private Hashtable mapping;
public MappingStrategy()
{
this.resourceName = Common.AGI_DEFAULT_RESOURCE_BUNDLE_NAME;
this.mapping = null;
}
public MappingStrategy(string resourceName)
{
this.resourceName = resourceName;
this.mapping = null;
}
internal 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();
}
}
}
internal 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;
}
}
}

View file

@ -0,0 +1,10 @@
namespace Asterisk.NET.FastAGI.Scripts
{
class AGINoAction : AGIScript
{
public override void Service(AGIRequest request, AGIChannel channel)
{
base.Hangup();
}
}
}

View file

@ -0,0 +1,834 @@
using System;
using System.Globalization;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;
using System.Reflection;
using System.Security.Cryptography;
using System.Collections.Generic;
using Asterisk.NET.Manager.Event;
using Asterisk.NET.Manager.Response;
using Asterisk.NET.Manager;
namespace Asterisk.NET
{
internal class Helper
{
private static CultureInfo defaultCulture;
#if LOGGER
private static Logger logger = Logger.Instance();
#endif
#region CultureInfo
internal static CultureInfo CultureInfo
{
get
{
if (defaultCulture == null)
defaultCulture = System.Globalization.CultureInfo.GetCultureInfo("en");
return defaultCulture;
}
}
#endregion
#region ToHexString(sbyte[])
/// <summary> The hex digits used to build a hex string representation of a byte array.</summary>
internal static readonly char[] hexChar = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/// <summary>
/// Converts a byte array to a hex string representing it. The hex digits are lower case.
/// </summary>
/// <param name="b">the byte array to convert</param>
/// <returns> the hex representation of b</returns>
internal static string ToHexString(sbyte[] b)
{
StringBuilder sb = new StringBuilder(b.Length * 2);
for (int i = 0; i < b.Length; i++)
{
sb.Append(hexChar[Helper.URShift((b[i] & 0xf0), 4)]);
sb.Append(hexChar[b[i] & 0x0f]);
}
return sb.ToString();
}
#endregion
#region GetInternalActionId(actionId)
internal static string GetInternalActionId(string actionId)
{
if (string.IsNullOrEmpty(actionId))
return string.Empty;
int delimiterIndex = actionId.IndexOf(Common.INTERNAL_ACTION_ID_DELIMITER);
if (delimiterIndex > 0)
return actionId.Substring(0, delimiterIndex).Trim();
return string.Empty;
}
#endregion
#region StripInternalActionId(actionId)
internal static string StripInternalActionId(string actionId)
{
if (string.IsNullOrEmpty(actionId))
return string.Empty;
int delimiterIndex = actionId.IndexOf(Common.INTERNAL_ACTION_ID_DELIMITER);
if (delimiterIndex > 0)
{
if (actionId.Length > delimiterIndex + 1)
return actionId.Substring(delimiterIndex + 1).Trim();
}
return string.Empty;
}
#endregion
#region IsTrue(string)
/// <summary>
/// Checks if a String represents <code>true</code> or <code>false</code> according to Asterisk's logic.<br/>
/// The original implementation is <code>util.c</code> is as follows:
/// </summary>
/// <param name="s">the String to check for <code>true</code>.</param>
/// <returns>
/// <code>true</code> if s represents <code>true</code>,
/// <code>false</code> otherwise.
/// </returns>
internal static bool IsTrue(string s)
{
if (s == null || s.Length == 0)
return false;
string sx = s.ToLower(Helper.CultureInfo);
if (sx == "yes" || sx == "true" || sx == "y" || sx == "t" || sx == "1" || sx == "on")
return true;
return false;
}
#endregion
#region URShift(...)
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
internal static int URShift(int number, int bits)
{
if (number >= 0)
return number >> bits;
else
return (number >> bits) + (2 << ~bits);
}
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
internal static int URShift(int number, long bits)
{
return URShift(number, (int)bits);
}
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
internal static long URShift(long number, int bits)
{
if (number >= 0)
return number >> bits;
else
return (number >> bits) + (2L << ~bits);
}
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Ammount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
internal static long URShift(long number, long bits)
{
return URShift(number, (int)bits);
}
#endregion
#region ToArray(ICollection c, object[] objects)
/// <summary>
/// Obtains an array containing all the elements of the collection.
/// </summary>
/// <param name="objects">The array into which the elements of the collection will be stored.</param>
/// <returns>The array containing all the elements of the collection.</returns>
internal static object[] ToArray(ICollection c, object[] objects)
{
int index = 0;
Type type = objects.GetType().GetElementType();
object[] objs = (object[])Array.CreateInstance(type, c.Count);
IEnumerator e = c.GetEnumerator();
while (e.MoveNext())
objs[index++] = e.Current;
//If objects is smaller than c then do not return the new array in the parameter
if (objects.Length >= c.Count)
objs.CopyTo(objects, 0);
return objs;
}
#endregion
#region ParseVariables(Dictionary<string, string> dictionary, string variables, char[] delim)
/// <summary>
/// Parse variable(s) string to dictionary.
/// </summary>
/// <param name="dictionary"></param>
/// <param name="variables">variable(a) string</param>
/// <param name="delim">variable pairs delimiter</param>
/// <returns></returns>
internal static Dictionary<string, string> ParseVariables(Dictionary<string, string> dictionary, string variables, char[] delim)
{
if (dictionary == null)
dictionary = new Dictionary<string, string>();
else
dictionary.Clear();
if (string.IsNullOrEmpty(variables))
return dictionary;
string[] vars = variables.Split(delim);
int idx;
string vname, vval;
foreach (string var in vars)
{
idx = var.IndexOf('=');
if (idx > 0)
{
vname = var.Substring(0, idx);
vval = var.Substring(idx + 1);
}
else
{
vname = var;
vval = string.Empty;
}
dictionary.Add(vname, vval);
}
return dictionary;
}
#endregion
#region JoinVariables(IDictionary dictionary, string delim)
/// <summary>
/// Join variables dictionary to string.
/// </summary>
/// <param name="dictionary"></param>
/// <param name="delim"></param>
/// <returns></returns>
internal static string JoinVariables(IDictionary dictionary, char[] delim, string delimKeyValue)
{
return JoinVariables(dictionary, new string(delim), delimKeyValue);
}
internal static string JoinVariables(IDictionary dictionary, string delim, string delimKeyValue)
{
if (dictionary == null)
return string.Empty;
StringBuilder sb = new StringBuilder();
foreach (DictionaryEntry var in dictionary)
{
if (sb.Length > 0)
sb.Append(delim);
sb.Append(string.Concat(var.Key, delimKeyValue, var.Value));
}
return sb.ToString();
}
#endregion
#region GetMillisecondsFrom(DateTime start)
internal static long GetMillisecondsFrom(DateTime start)
{
TimeSpan ts = (TimeSpan)(DateTime.Now - start);
return (long)ts.TotalMilliseconds;
}
#endregion
#region ParseString(string val)
internal static object ParseString(string val)
{
if (val == "none")
return string.Empty;
return val;
}
#endregion
#region GetGetters(class)
/// <summary>
/// Returns a Map of getter methods of the given class.<br/>
/// The key of the map contains the name of the attribute that can be accessed by the getter, the
/// value the getter itself . A method is considered a getter if its name starts with "get",
/// it is declared internal and takes no arguments.
/// </summary>
/// <param name="clazz">the class to return the getters for</param>
/// <returns> a Map of attributes and their accessor methods (getters)</returns>
internal static Dictionary<string, MethodInfo> GetGetters(Type clazz)
{
string name;
string methodName;
MethodInfo method;
Dictionary<string, MethodInfo> accessors = new Dictionary<string, MethodInfo>();
MethodInfo[] methods = clazz.GetMethods();
for (int i = 0; i < methods.Length; i++)
{
method = methods[i];
methodName = method.Name;
// skip not "get..." methods and skip methods with != 0 parameters
if (!methodName.StartsWith("get_") || method.GetParameters().Length != 0)
continue;
name = methodName.Substring(4);
if (name.Length == 0)
continue;
accessors[name] = method;
}
return accessors;
}
#endregion
#region GetSetters(Type clazz)
/// <summary>
/// Returns a Map of setter methods of the given class.<br/>
/// The key of the map contains the name of the attribute that can be accessed by the setter, the
/// value the setter itself. A method is considered a setter if its name starts with "set",
/// it is declared internal and takes no arguments.
/// </summary>
/// <param name="clazz">the class to return the setters for</param>
/// <returns> a Map of attributes and their accessor methods (setters)</returns>
internal static IDictionary GetSetters(Type clazz)
{
IDictionary accessors = new Hashtable();
MethodInfo[] methods = clazz.GetMethods();
string name;
string methodName;
MethodInfo method;
for (int i = 0; i < methods.Length; i++)
{
method = methods[i];
methodName = method.Name;
// skip not "set..." methods and skip methods with != 1 parameters
if (!methodName.StartsWith("set_") || method.GetParameters().Length != 1)
continue;
name = methodName.Substring("set_".Length).ToLower(Helper.CultureInfo);
if (name.Length == 0) continue;
accessors[name] = method;
}
return accessors;
}
#endregion
#region ToString(object obj)
/// <summary>
/// Convert object with all properties to string
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
internal static string ToString(object obj)
{
object value;
StringBuilder sb = new StringBuilder(obj.GetType().Name, 1024);
sb.Append(" {");
string strValue;
IDictionary getters = Helper.GetGetters(obj.GetType());
bool notFirst = false;
List<MethodInfo> arrays = new List<MethodInfo>();
// First step - all values properties (not a list)
foreach (string name in getters.Keys)
{
MethodInfo getter = (MethodInfo)getters[name];
Type propType = getter.ReturnType;
if (propType == typeof(object))
continue;
if (!(propType == typeof(string) || propType == typeof(bool) || propType == typeof(double) || propType == typeof(DateTime) || propType == typeof(int) || propType == typeof(long)))
{
string propTypeName = propType.Name;
if (propTypeName.StartsWith("Dictionary") || propTypeName.StartsWith("List"))
{
arrays.Add(getter);
continue;
}
continue;
}
try
{
value = getter.Invoke(obj, new object[] { });
}
catch
{
continue;
}
if (value == null)
continue;
if (value is string)
{
strValue = (string)value;
if (strValue.Length == 0)
continue;
}
else if (value is bool)
{
strValue = ((bool)value ? "true" : "false");
}
else if (value is double)
{
double d = (double)value;
if (d == 0.0)
continue;
strValue = d.ToString();
}
else if (value is DateTime)
{
DateTime dt = (DateTime)value;
if (dt == DateTime.MinValue)
continue;
strValue = dt.ToLongTimeString();
}
else if (value is int)
{
int i = (int)value;
if (i == 0)
continue;
strValue = i.ToString();
}
else if (value is long)
{
long l = (long)value;
if (l == 0)
continue;
strValue = l.ToString();
}
else
strValue = value.ToString();
if (notFirst)
sb.Append("; ");
notFirst = true;
sb.Append(string.Concat(getter.Name.Substring(4), ":", strValue));
}
// Second step - all lists
foreach (MethodInfo getter in arrays)
{
value = null;
try
{
value = getter.Invoke(obj, new object[] { });
}
catch
{
continue;
}
if (value == null)
continue;
#region List
IList list;
if (value is IList && (list = (IList)value).Count > 0)
{
if (notFirst)
sb.Append("; ");
notFirst = true;
sb.Append(getter.Name.Substring(4));
sb.Append(":[");
bool notFirst2 = false;
foreach (object o in list)
{
if (notFirst2)
sb.Append("; ");
notFirst2 = true;
sb.Append(o.ToString());
}
sb.Append("]");
continue;
}
#endregion
#region IDictionary
else if (value is IDictionary && ((IDictionary)value).Count > 0)
{
if (notFirst)
sb.Append("; ");
notFirst = true;
sb.Append(getter.Name.Substring(4));
sb.Append(":[");
bool notFirst2 = false;
foreach (object key in ((IDictionary)value).Keys)
{
object o = ((IDictionary)value)[key];
if (notFirst2)
sb.Append("; ");
notFirst2 = true;
sb.Append(string.Concat(key, ":", o));
}
sb.Append("]");
continue;
}
#endregion
}
sb.Append("}");
return sb.ToString();
}
#endregion
#region SetAttributes(object evt, IDictionary attributes)
internal static void SetAttributes(IParseSupport o, Dictionary<string, string> attributes)
{
Type dataType;
object val;
// Preparse attributes
attributes = o.ParseSpecial(attributes);
IDictionary setters = Helper.GetSetters(o.GetType());
MethodInfo setter;
foreach (string name in attributes.Keys)
{
if (name == "event")
continue;
if (name == "source")
setter = (MethodInfo)setters["src"];
else
setter = (MethodInfo)setters[stripIllegalCharacters(name)];
if (setter == null)
{
// No setter found to key, try general parser
if (!o.Parse(name, (string)attributes[name]))
{
#if LOGGER
logger.Error("Unable to set property '" + name + "' on " + o.GetType() + ": no setter");
#endif
throw new ManagerException("Parse error key '" + name + "' on " + o.GetType());
}
}
else
{
dataType = (setter.GetParameters()[0]).ParameterType;
if (dataType == typeof(bool))
val = Helper.IsTrue((string)attributes[name]);
else if (dataType == typeof(string))
val = Helper.ParseString((string)attributes[name]);
else if (dataType == typeof(Int32))
{
Int32 v = 0;
Int32.TryParse((string)attributes[name], out v);
val = v;
}
else if (dataType == typeof(Int64))
{
Int64 v = 0;
Int64.TryParse((string)attributes[name], out v);
val = v;
}
else if (dataType == typeof(double))
{
Double v = 0.0;
Double.TryParse((string)attributes[name], System.Globalization.NumberStyles.AllowDecimalPoint, Common.CultureInfoEn, out v);
val = v;
}
else if (dataType == typeof(decimal))
{
Decimal v = 0;
Decimal.TryParse((string)attributes[name], System.Globalization.NumberStyles.AllowDecimalPoint, Common.CultureInfoEn, out v);
val = v;
}
else if (dataType.IsEnum)
{
try
{
val = Convert.ChangeType(Enum.Parse(dataType, (string)attributes[name], true), dataType);
}
catch (Exception ex)
{
#if LOGGER
logger.Error("Unable to convert value '" + attributes[name] + "' of property '" + name + "' on " + o.GetType() + " to required enum type " + dataType, ex);
continue;
#else
throw new ManagerException("Unable to convert value '" + attributes[name] + "' of property '" + name + "' on " + o.GetType() + " to required enum type " + dataType, ex); #endif
#endif
}
}
else
{
try
{
ConstructorInfo constructor = dataType.GetConstructor(new Type[] { typeof(string) });
val = constructor.Invoke(new object[] { attributes[name] });
}
catch (Exception ex)
{
#if LOGGER
logger.Error("Unable to convert value '" + attributes[name] + "' of property '" + name + "' on " + o.GetType() + " to required type " + dataType, ex);
continue;
#else
throw new ManagerException("Unable to convert value '" + attributes[name] + "' of property '" + name + "' on " + o.GetType() + " to required type " + dataType, ex);
#endif
}
}
try
{
setter.Invoke(o, new object[] { val });
}
catch (Exception ex)
{
#if LOGGER
logger.Error("Unable to set property '" + name + "' on " + o.GetType(), ex);
continue;
#else
throw new ManagerException("Unable to set property '" + name + "' on " + o.GetType(), ex);
#endif
}
}
}
}
#endregion
#region AddKeyValue(IDictionary list, string line)
internal static void AddKeyValue(IDictionary list, string line)
{
int delimiterIndex = line.IndexOf(":");
if (delimiterIndex > 0 && line.Length > delimiterIndex + 1)
{
string name = line.Substring(0, delimiterIndex).ToLower(Helper.CultureInfo).Trim();
string val = line.Substring(delimiterIndex + 1).Trim();
if (val == "<null>")
list[name] = null;
else
list[name] = val;
}
}
#endregion
#region stripIllegalCharacters(string s)
/// <summary>
/// Strips all illegal charaters from the given lower case string.
/// </summary>
/// <param name="s">the original string</param>
/// <returns>the string with all illegal characters stripped</returns>
private static string stripIllegalCharacters(string s)
{
char c;
bool needsStrip = false;
if (string.IsNullOrEmpty(s))
return null;
for (int i = 0; i < s.Length; i++)
{
c = s[i];
if (c >= '0' && c <= '9')
continue;
else if (c >= 'a' && c <= 'z')
continue;
else if (c >= 'A' && c <= 'Z')
continue;
else
{
needsStrip = true;
break;
}
}
if (!needsStrip)
return s;
StringBuilder sb = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; i++)
{
c = s[i];
if (c >= '0' && c <= '9')
sb.Append(c);
else if (c >= 'a' && c <= 'z')
sb.Append(c);
else if (c >= 'A' && c <= 'Z')
sb.Append(c);
}
return sb.ToString();
}
#endregion
#region BuildResponse(IDictionary attributes)
/// <summary>
/// Constructs an instance of ManagerResponse based on a map of attributes.
/// </summary>
/// <param name="attributes">the attributes and their values. The keys of this map must be all lower case.</param>
/// <returns>the response with the given attributes.</returns>
internal static ManagerResponse BuildResponse(Dictionary<string, string> attributes)
{
ManagerResponse response;
string responseType = ((string)attributes["response"]).ToLower(Helper.CultureInfo);
// Determine type
if (responseType == "error")
response = new ManagerError();
else if (attributes.ContainsKey("challenge"))
response = new ChallengeResponse();
else if (attributes.ContainsKey("mailbox") && attributes.ContainsKey("waiting"))
response = new MailboxStatusResponse();
else if (attributes.ContainsKey("mailbox") && attributes.ContainsKey("newmessages") && attributes.ContainsKey("oldmessages"))
response = new MailboxCountResponse();
else if (attributes.ContainsKey("exten") && attributes.ContainsKey("context") && attributes.ContainsKey("hint") && attributes.ContainsKey("status"))
response = new ExtensionStateResponse();
else
response = new ManagerResponse();
Helper.SetAttributes(response, attributes);
return response;
}
#endregion
#region BuildEvent(Hashtable list, object source, IDictionary attributes)
/// <summary>
/// Builds the event based on the given map of attributes and the registered event classes.
/// </summary>
/// <param name="source">source attribute for the event</param>
/// <param name="attributes">map containing event attributes</param>
/// <returns>a concrete instance of ManagerEvent or <code>null</code> if no event class was registered for the event type.</returns>
internal static ManagerEvent BuildEvent(IDictionary<int, ConstructorInfo> list, ManagerConnection source, Dictionary<string, string> attributes)
{
ManagerEvent e;
string eventType;
ConstructorInfo constructor = null;
int hash, hashEvent;
eventType = ((string)attributes["event"]).ToLower(Helper.CultureInfo);
// Remove Event tail from event name (ex. JabberEvent)
if (eventType.EndsWith("event"))
eventType = eventType.Substring(0, eventType.Length - 5);
hashEvent = eventType.GetHashCode();
if (eventType == "user")
{
string userevent = ((string)attributes["userevent"]).ToLower(Helper.CultureInfo);
hash = string.Concat(eventType, userevent).GetHashCode();
if(list.ContainsKey(hash))
constructor = list[hash];
else
constructor = list[hashEvent];
}
else if (list.ContainsKey(hashEvent))
constructor = list[hashEvent];
if (constructor == null)
e = new UnknownEvent(source);
else
{
try
{
e = (ManagerEvent)constructor.Invoke(new object[] { source });
}
catch (Exception ex)
{
#if LOGGER
logger.Error("Unable to create new instance of " + eventType, ex);
return null;
#else
throw ex;
#endif
}
}
SetAttributes(e, attributes);
// ResponseEvents are sent in response to a ManagerAction if the
// response contains lots of data. They include the actionId of
// the corresponding ManagerAction.
if (e is ResponseEvent)
{
ResponseEvent responseEvent = (ResponseEvent)e;
string actionId = responseEvent.ActionId;
if (actionId != null)
{
responseEvent.ActionId = Helper.StripInternalActionId(actionId);
responseEvent.InternalActionId = Helper.GetInternalActionId(actionId);
}
}
return e;
}
#endregion
#region RegisterBuiltinEventClasses(Hashtable list)
/// <summary>
/// Register buildin Event classes
/// </summary>
/// <param name="list"></param>
internal static void RegisterBuiltinEventClasses(Dictionary<int, ConstructorInfo> list)
{
Assembly assembly = Assembly.GetExecutingAssembly();
Type manager = typeof(ManagerEvent);
foreach (Type type in assembly.GetTypes())
if (type.IsPublic && !type.IsAbstract && manager.IsAssignableFrom(type))
RegisterEventClass(list, type);
}
#endregion
#region RegisterEventClass(Dictionary<string, ConstructorInfo> list, Type clazz)
internal static void RegisterEventClass(Dictionary<int, ConstructorInfo> list, Type clazz)
{
// Ignore all abstract classes
// Class not derived from ManagerEvent
if (clazz.IsAbstract || !typeof(ManagerEvent).IsAssignableFrom(clazz))
return;
string eventType = clazz.Name.ToLower(Helper.CultureInfo);
// Remove "event" at the end (if presents)
if (eventType.EndsWith("event"))
eventType = eventType.Substring(0, eventType.Length - 5);
// If assignable from UserEvent and no "userevent" at the start - add "userevent" to beginning
if (typeof(UserEvent).IsAssignableFrom(clazz) && !eventType.StartsWith("user"))
eventType = "user" + eventType;
int hash = eventType.GetHashCode();
if (list.ContainsKey(hash))
return;
ConstructorInfo constructor = null;
try
{
constructor = clazz.GetConstructor(new Type[] { typeof(ManagerConnection) });
}
catch (MethodAccessException ex)
{
throw new ArgumentException("RegisterEventClass : " + clazz + " has no usable constructor.", ex);
}
if (constructor != null && constructor.IsPublic)
list.Add(hash, constructor);
else
throw new ArgumentException("RegisterEventClass : " + clazz + " has no public default constructor");
}
#endregion
#region RegisterEventHandler(Dictionary<int, int> list, int index, Type eventType)
internal static void RegisterEventHandler(Dictionary<int, int> list, int index, Type eventType)
{
int eventHash = eventType.Name.GetHashCode();
if (list.ContainsKey(eventHash))
throw new ArgumentException("Event class already registered : " + eventType.Name);
list.Add(eventHash, index);
}
#endregion
}
}

View file

@ -0,0 +1,38 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Asterisk.NET.IO
{
/// <summary>
/// ServerSocket using standard socket classes.
/// </summary>
public class ServerSocket
{
private TcpListener tcpListener;
private Encoding encoding;
public ServerSocket(int port, IPAddress bindAddress, Encoding encoding)
{
this.encoding = encoding;
tcpListener = new TcpListener(new IPEndPoint(bindAddress, port));
tcpListener.Start();
}
public IO.SocketConnection Accept()
{
if (tcpListener != null)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
if (tcpClient != null)
return new IO.SocketConnection(tcpClient, encoding);
}
return null;
}
public void Close()
{
tcpListener.Stop();
}
}
}

View file

@ -0,0 +1,204 @@
using System.IO;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System;
namespace Asterisk.NET.IO
{
public class SocketConnection
{
private TcpClient tcpClient;
private NetworkStream networkStream;
private StreamReader reader;
private StreamWriter writer;
private Encoding encoding;
private bool initial;
#region Constructor - SocketConnection(string host, int port, int receiveTimeout)
/// <summary>
/// Consructor
/// </summary>
/// <param name="host">client host</param>
/// <param name="port">client port</param>
/// <param name="encoding">encoding</param>
public SocketConnection(string host, int port, Encoding encoding)
{
initial = true;
this.encoding = encoding;
this.tcpClient = new TcpClient(host, port);
this.networkStream = this.tcpClient.GetStream();
this.reader = new StreamReader(this.networkStream, encoding);
this.writer = new StreamWriter(this.networkStream, encoding);
this.writer.AutoFlush = true;
}
#endregion
#region Constructor - SocketConnection(socket)
/// <summary>
/// Constructor
/// </summary>
/// <param name="socket">TCP client from Listener</param>
/// <param name="encoding">encoding</param>
internal SocketConnection(TcpClient tcpClient, Encoding encoding)
{
initial = true;
this.encoding = encoding;
this.tcpClient = tcpClient;
this.networkStream = this.tcpClient.GetStream();
this.reader = new StreamReader(this.networkStream, encoding);
this.writer = new StreamWriter(this.networkStream, encoding);
this.writer.AutoFlush = true;
}
#endregion
public TcpClient TcpClient
{
get { return tcpClient; }
}
public NetworkStream NetworkStream
{
get { return networkStream; }
}
public Encoding Encoding
{
get { return encoding; }
}
public bool Initial
{
get { return initial; }
set { initial = value; }
}
#region IsConnected
/// <summary>
/// Returns the connection state of the socket.
/// </summary>
public bool IsConnected
{
get { return tcpClient.Connected; }
}
#endregion
#region LocalAddress
public IPAddress LocalAddress
{
get
{
return ((IPEndPoint)(tcpClient.Client.LocalEndPoint)).Address;
}
}
#endregion
#region LocalPort
public int LocalPort
{
get
{
return ((IPEndPoint)(tcpClient.Client.LocalEndPoint)).Port;
}
}
#endregion
#region RemoteAddress
public IPAddress RemoteAddress
{
get
{
return ((IPEndPoint)(tcpClient.Client.RemoteEndPoint)).Address;
}
}
#endregion
#region RemotePort
public int RemotePort
{
get
{
return ((IPEndPoint)(tcpClient.Client.LocalEndPoint)).Port;
}
}
#endregion
#region ReadLine()
/// <summary>
/// Reads a line of text from the socket connection. The current thread is
/// blocked until either the next line is received or an IOException
/// encounters.
/// </summary>
/// <returns>the line of text received excluding any newline character</returns>
/// <throws> IOException if the connection has been closed. </throws>
public string ReadLine()
{
string line = null;
try
{
line = reader.ReadLine();
}
catch
{
line = null;
}
return line;
}
#endregion
#region Write(string s)
/// <summary>
/// Sends a given String to the socket connection.
/// </summary>
/// <param name="s">the String to send.</param>
/// <throws> IOException if the String cannot be sent, maybe because the </throws>
/// <summary>connection has already been closed.</summary>
public void Write(string s)
{
writer.Write(s);
}
#endregion
#region Write(string msg)
/// <summary>
/// Sends a given String to the socket connection.
/// </summary>
/// <param name="msg">the String to send.</param>
/// <throws> IOException if the String cannot be sent, maybe because the </throws>
/// <summary>connection has already been closed.</summary>
public void WriteEx(string msg)
{
byte[] data = encoding.GetBytes(msg);
networkStream.BeginWrite(data, 0, data.Length, onWriteFinished, networkStream);
networkStream.Flush();
}
private void onWriteFinished(IAsyncResult ar)
{
NetworkStream stream = (NetworkStream)ar.AsyncState;
stream.EndWrite(ar);
}
#endregion
#region Close
/// <summary>
/// Closes the socket connection including its input and output stream and
/// frees all associated ressources.<br/>
/// When calling close() any Thread currently blocked by a call to readLine()
/// will be unblocked and receive an IOException.
/// </summary>
/// <throws> IOException if the socket connection cannot be closed. </throws>
public void Close()
{
try
{
tcpClient.Client.Shutdown(SocketShutdown.Both);
tcpClient.Client.Close();
tcpClient.Close();
}
catch { }
}
#endregion
}
}

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Asterisk.NET
{
internal interface IParseSupport
{
Dictionary<string, string> Attributes
{
get;
}
bool Parse(string key, string value);
Dictionary<string, string> ParseSpecial(Dictionary<string, string> attributes);
}
}

View file

@ -0,0 +1,261 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Text;
using System.Collections;
namespace Asterisk.NET
{
#if LOGGER
#region class LogFactory
/// <summary>
/// Facade to hide details of the underlying logging system.
/// </summary>
public sealed class Logger
{
private static Logger logger;
/// <summary>
/// Returns an instance of Log suitable for logging from the given class.
/// </summary>
/// <returns> the created logger.</returns>
public static Logger Instance()
{
if(logger == null)
logger = new Logger();
return logger;
}
public enum MessageLevel
{
Info,
Warning,
Error,
Debug
}
/// <summary>
/// Creates a new CommonsLoggingLog obtained from commons-logging's LogFactory for the given class.
/// </summary>
public Logger()
{
}
private void writeLine(string type, string msg)
{
System.Diagnostics.Debug.Print(string.Format("{0}[{1}] {2}", type, Thread.CurrentThread.Name, msg));
}
private void writeLine(string msg)
{
System.Diagnostics.Debug.Print(msg);
}
// Max 2 calls from original caller !
private string debugInfo()
{
System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame(2, true);
System.Reflection.MethodBase mb = sf.GetMethod();
return string.Concat(mb.DeclaringType.Name, ":", mb.Name);
}
Hashtable visibleDebug = new Hashtable();
Hashtable visibleError = new Hashtable();
Hashtable visibleInfo = new Hashtable();
Hashtable visibleWarning = new Hashtable();
private bool visibleDebugDef = true;
private bool visibleErrorDef = true;
private bool visibleInfoDef = true;
private bool visibleWarningDef = true;
/// <summary>
/// Get visibility for message level of class:method
/// </summary>
/// <param name="debugClass">messageType:class:method</param>
/// <returns></returns>
public bool IsVisible(MessageLevel messageLevel, string classMethod)
{
return isVisible(messageLevel, classMethod.GetHashCode());
}
private bool isVisible(MessageLevel messageLevel, int hash)
{
switch (messageLevel)
{
case MessageLevel.Debug:
return (visibleDebug.ContainsKey(hash) ? (bool)visibleDebug[hash] : visibleDebugDef);
case MessageLevel.Error:
return (visibleError.ContainsKey(hash) ? (bool)visibleError[hash] : visibleErrorDef);
case MessageLevel.Info:
return (visibleInfo.ContainsKey(hash) ? (bool)visibleInfo[hash] : visibleInfoDef);
case MessageLevel.Warning:
return (visibleWarning.ContainsKey(hash) ? (bool)visibleWarning[hash] : visibleWarningDef);
}
return true;
}
/// <summary>
/// Set visibility for message level of class:method
/// </summary>
/// <param name="visible">visible</param>
/// <param name="messageLevel">message level</param>
/// <param name="classMethod">class:method</param>
public void Visible(bool visible, MessageLevel messageLevel, string classMethod)
{
int hash = classMethod.GetHashCode();
switch (messageLevel)
{
case MessageLevel.Debug:
visibleDebug[hash] = visible;
return;
case MessageLevel.Error:
visibleError[hash] = visible;
return;
case MessageLevel.Info:
visibleInfo[hash] = visible;
return;
case MessageLevel.Warning:
visibleWarning[hash] = visible;
return;
}
}
/// <summary>
/// Set visibility for message level of class:method
/// </summary>
/// <param name="visible">visible</param>
/// <param name="messageLevel">message level</param>
/// <param name="classMethod">class:method</param>
public void Visible(bool visible, MessageLevel messageLevel)
{
switch (messageLevel)
{
case MessageLevel.Debug:
visibleDebugDef = visible;
return;
case MessageLevel.Error:
visibleErrorDef = visible;
return;
case MessageLevel.Info:
visibleInfoDef = visible;
return;
case MessageLevel.Warning:
visibleWarningDef = visible;
return;
}
}
#region Debug
public void Debug(object o)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Debug, caller.GetHashCode()))
writeLine(" Debug:", string.Concat(caller, " - ", o.ToString()));
}
public void Debug(string msg)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Debug, caller.GetHashCode()))
writeLine(" Debug:", string.Concat(caller, " - ", msg));
}
public void Debug(string format, params object[] args)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Debug, caller.GetHashCode()))
writeLine(" Debug:", string.Concat(caller, " - ", string.Format(format, args)));
}
public void Debug(string msg, Exception ex)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Debug, caller.GetHashCode()))
writeLine(" Debug:", string.Concat(caller, " - ", string.Format("{0}\n{1}", msg, ex)));
}
#endregion
#region Info
public void Info(object o)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Info, caller.GetHashCode()))
writeLine(" Info:", string.Concat(caller, " - ", o.ToString()));
}
public void Info(string msg)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Info, caller.GetHashCode()))
writeLine(" Info:", string.Concat(caller, " - ", msg));
}
public void Info(string format, params object[] args)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Info, caller.GetHashCode()))
writeLine(" Info:", string.Concat(caller, " - ", string.Format(format, args)));
}
public void Info(string msg, Exception ex)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Info, caller.GetHashCode()))
writeLine(" Info:", string.Concat(caller, " - ", string.Format("{0}\n{1}", msg, ex)));
}
#endregion
#region Warning
public void Warning(object o)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Warning, caller.GetHashCode()))
writeLine("Warning:", string.Concat(caller, " - ", o.ToString()));
}
public void Warning(string msg)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Warning, caller.GetHashCode()))
writeLine("Warning:", string.Concat(caller, " - ", msg));
}
public void Warning(string format, params object[] args)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Warning, caller.GetHashCode()))
writeLine("Warning:", string.Concat(caller, " - ", string.Format(format, args)));
}
public void Warning(string msg, Exception ex)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Warning, caller.GetHashCode()))
writeLine("Warning:", string.Concat(caller, " - ", string.Format("{0}\n{1}", msg, ex)));
}
#endregion
#region Error
public void Error(object o)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Error, caller.GetHashCode()))
writeLine(" Error:", string.Concat(caller, " - ", o.ToString()));
}
public void Error(string msg)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Error, caller.GetHashCode()))
writeLine(" Error:", string.Concat(caller, " - ", msg));
}
public void Error(string format, params object[] args)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Error, caller.GetHashCode()))
writeLine(" Error:", string.Concat(caller, " - ", string.Format(format, args)));
}
public void Error(string msg, Exception ex)
{
string caller = debugInfo();
if (isVisible(MessageLevel.Error, caller.GetHashCode()))
writeLine(" Error:", string.Concat(caller, " - ", string.Format("{0}\n{1}", msg, ex)));
}
#endregion
}
#endregion
#endif
}

View file

@ -0,0 +1,68 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The AbsoluteTimeoutAction sets the absolute maximum amount of time permitted for a call on a given channel.<br/>
/// Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up.<br/>
/// When setting a new timeout all previous absolute timeouts are cancelled.<br/>
/// When the timeout is reached the call is returned to the T extension so that
/// you can playback an explanatory note to the calling party (the called party will not hear that).<br/>
/// This action corresponds the the AbsoluteTimeout command used in the dialplan.
/// </summary>
public class AbsoluteTimeoutAction : ManagerAction
{
private string channel;
private int timeout;
#region AbsoluteTimeoutAction()
/// <summary>
/// Creates a new empty AbsoluteTimeoutAction.
/// </summary>
public AbsoluteTimeoutAction()
{
}
#endregion
#region AbsoluteTimeoutAction(channel, timeout)
/// <summary>
/// Creates a new AbsoluteTimeoutAction with the given channel and timeout.
/// </summary>
/// <param name="channel">the name of the channel</param>
/// <param name="timeout">the timeout in seconds or 0 to cancel the AbsoluteTimeout</param>
public AbsoluteTimeoutAction(string channel, int timeout)
{
this.channel = channel;
this.timeout = timeout;
}
#endregion
#region Action
/// <summary>
/// Get the name of this action, i.e. "AbsoluteTimeout".
/// </summary>
override public string Action
{
get { return "AbsoluteTimeout"; }
}
#endregion
#region Channel
/// <summary>
/// Get/Set the name of the channel.</summary>
public string Channel
{
get { return channel; }
set { this.channel = value; }
}
#endregion
#region Timeout
/// <summary>
/// Get/Set the timeout (in seconds) to set.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
#endregion
}
}

View file

@ -0,0 +1,107 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The AgentCallbackLoginAction sets an agent as logged in with callback.<br/>
/// You can pass an extentsion (and optionally a context) to specify the
/// destination of the callback.<br/>
/// In contrast to the AgentCallbackLogin application that you can use within
/// Asterisk's dialplan, you don't need to know the agent's password when logging
/// in an agent.<br/>
/// Available since Asterisk 1.2
/// </summary>
public class AgentCallbackLoginAction : ManagerAction
{
private string agent;
private string exten;
private string context;
private bool ackCall;
private long wrapupTime;
/// <summary>
/// Get the name of this action, i.e. "AgentCallbackLogin".
/// </summary>
override public string Action
{
get { return "AgentCallbackLogin"; }
}
/// <summary>
/// Get/Set the name of the agent to log in, for example "1002".<br/>
/// This is property is mandatory.
/// </summary>
public string Agent
{
get { return this.agent; }
set { this.agent = value; }
}
/// <summary>
/// Get/Set the extension to use for callback.<br/>
/// This is property is mandatory.
/// </summary>
public string Exten
{
get { return this.exten; }
set { this.exten = value; }
}
/// <summary>
/// Get/Set the context of the extension to use for callback.
/// </summary>
public string Context
{
get { return this.context; }
set { this.context = value; }
}
/// <summary>
/// Get/Set if an acknowledgement is needed when agent is called back.<br/>
/// <code>true</code> if acknowledgement by '#' is required when agent is called back, <code>false</code> otherwise.
/// This property is optional, it allows you to override the defaults defined in Asterisk's configuration.
/// </summary>
public bool AckCall
{
get { return this.ackCall; }
set { this.ackCall = value; }
}
/// <summary>
/// Returns the minimum amount of time (in milliseconds) after disconnecting before the caller can receive a new call.<br/>
/// This property is optional, it allows you to override the defaults defined in Asterisk's configuration.
/// </summary>
public long WrapupTime
{
get { return this.wrapupTime; }
set { this.wrapupTime = value; }
}
/// <summary>
/// Creates a new empty AgentCallbackLoginAction.
/// </summary>
public AgentCallbackLoginAction()
{
}
/// <summary>
/// Creates a new AgentCallbackLoginAction, that logs in the given agent at
/// the given callback extension.
/// </summary>
/// <param name="agent">the name of the agent to log in</param>
/// <param name="exten">the extension that is called to connect a queue member with this agent</param>
public AgentCallbackLoginAction(string agent, string exten)
{
this.agent = agent;
this.exten = exten;
}
/// <summary>
/// Creates a new AgentCallbackLoginAction, that logs in the given agent at
/// the given callback extension in the given context.
/// </summary>
/// <param name="agent">the name of the agent to log in</param>
/// <param name="exten">the extension that is called to connect a queue member with this agent</param>
/// <param name="context">the context of the extension to use for callback</param>
public AgentCallbackLoginAction(string agent, string exten, string context)
: this(agent, exten)
{
this.context = context;
}
}
}

View file

@ -0,0 +1,94 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The AgentLogoffAction sets an agent as no longer logged in.
/// </summary>
public class AgentLogoffAction : ManagerAction
{
private string agent;
private bool soft;
#region Action
/// <summary>
/// Returns the name of this action, i.e. "AgentLogoff".
/// </summary>
/// <returns>the name of this action</returns>
override public string Action
{
get
{
return "AgentLogoff";
}
}
#endregion
#region Agent
/// <summary>
/// Returns the name of the agent to log off, for example "1002".
/// </summary>
/// <returns>the name of the agent to log off</returns>
/// <summary> Sets the name of the agent to log off, for example "1002".<br/>
/// This is property is mandatory.
/// </summary>
/// <param name="agent">the name of the agent to log off</param>
public string Agent
{
get
{
return agent;
}
set
{
this.agent = value;
}
}
#endregion
#region Soft
/// <summary>
/// Get/Set whether to hangup existing calls or not.<br/>
/// Default is to hangup existing calls on logoff.
/// </summary>
/// <returns> true if existing calls should not be hung up, false otherwise.<br/>
/// <code>null</code> if default should be used.
/// </returns>
public bool Soft
{
get { return soft; }
set { this.soft = value; }
}
#endregion
#region Constructors - AgentLogoffAction()
/// <summary> Creates a new empty AgentLogoffAction.</summary>
public AgentLogoffAction()
{
}
/// <summary>
/// Creates a new AgentLogoffAction that logs off the given agent
/// </summary>
/// <param name="agent">the name of the agent to log off.</param>
public AgentLogoffAction(string agent)
{
this.agent = agent;
}
#endregion
#region Constructors - AgentLogoffAction(string agent, bool soft)
/// <summary>
/// Creates a new AgentLogoffAction that logs off the given agent
/// </summary>
/// <param name="agent">the name of the agent to log off.</param>
/// <param name="soft">true if exisiting calls should not be hung up on logout.</param>
public AgentLogoffAction(string agent, bool soft)
: this(agent)
{
this.soft = soft;
}
#endregion
}
}

View file

@ -0,0 +1,41 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The AgentsAction requests the state of all agents.<br/>
/// For each agent an AgentsEvent is generated.
/// After the state of all agents has been reported an AgentsCompleteEvent is generated.<br/>
/// Available since Asterisk 1.2
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.AgentsEvent" />
/// <seealso cref="Asterisk.NET.Manager.Event.AgentsCompleteEvent" />
public class AgentsAction : ManagerActionEvent
{
#region Action
/// <summary>
/// Get the name of this action, i.e. "Agents".
/// </summary>
override public string Action
{
get { return "Agents"; }
}
#endregion
#region ActionCompleteEventClass
public override Type ActionCompleteEventClass()
{
return typeof(Event.AgentsCompleteEvent);
}
#endregion
#region AgentsAction()
/// <summary>
/// Creates a new AgentsAction.
/// </summary>
public AgentsAction()
{
}
#endregion
}
}

View file

@ -0,0 +1,50 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ChallengeAction requests a challenge from the server to use when logging
/// in using challenge/response. Sending this action to the asterisk server
/// results in a ChallengeResponse being received from the server.
/// </summary>
/// <seealso cref="Manager.Action.LoginAction"/>
/// <seealso cref="Manager.Response.ChallengeResponse"/>
public class ChallengeAction : ManagerAction
{
private string authType;
/// <summary>
/// Get the name of this action, i.e. "Challenge".
/// </summary>
override public string Action
{
get { return "Challenge"; }
}
/// <summary>
/// Get/Set the digest alogrithm to use. Currently asterisk only supports "MD5".
/// </summary>
public string AuthType
{
get { return this.authType; }
set { this.authType = value; }
}
/// <summary>
/// Creates a new empty ChallengeAction with MD5 algorithm
/// </summary>
public ChallengeAction()
{
this.authType = "MD5";
}
/// <summary>
/// Creates a new ChallengeAction that requests a new login challenge for use
/// with the given digest algorithm.
/// </summary>
/// <param name="authType">the digest alogrithm to use.</param>
public ChallengeAction(string authType)
{
this.authType = authType;
}
}
}

View file

@ -0,0 +1,69 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ChangeMonitorAction changes the monitoring filename of a channel.
/// It has no effect if the channel is not monitored.<br/>
/// It is implemented in <code>res/res_monitor.c</code>
/// </summary>
public class ChangeMonitorAction : ManagerAction
{
private string channel;
private string file;
#region Action
/// <summary>
/// Get the name of this action, i.e. "ChangeMonitor".
/// </summary>
override public string Action
{
get { return "ChangeMonitor"; }
}
#endregion
#region Channel
/// <summary>
/// Get/Set the name of the monitored channel.<br/>
/// This property is mandatory.
/// </summary>
public string Channel
{
get { return this.channel; }
set { this.channel = value; }
}
#endregion
#region File
/// <summary>
/// Get/Set the name of the file to which the voice data is written.<br/>
/// This property is mandatory.
/// </summary>
public string File
{
get { return this.file; }
set { this.file = value; }
}
#endregion
#region ChangeMonitorAction()
/// <summary>
/// Creates a new empty ChangeMonitorAction.
/// </summary>
public ChangeMonitorAction()
{
}
#endregion
#region ChangeMonitorAction(string channel, string file)
/// <summary>
/// Creates a new ChangeMonitorAction that causes monitoring data for the
/// given channel to be written to the given file(s).
/// </summary>
/// <param name="channel">the name of the channel that is monitored</param>
/// <param name="file">the (base) name of the file(s) to which the voice data is written</param>
public ChangeMonitorAction(string channel, string file)
{
this.channel = channel;
this.file = file;
}
#endregion
}
}

View file

@ -0,0 +1,45 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The CommandAction sends a command line interface (CLI) command to the asterisk server.<br/>
/// For a list of supported commands type <code>help</code> on asterisk's command line.
/// </summary>
public class CommandAction : ManagerAction
{
protected internal string command;
/// <summary>
/// Get the name of this action, i.e. "Command".
/// </summary>
override public string Action
{
get { return "Command"; }
}
/// <summary>
/// Get/Set the CLI command to send to the asterisk server.
/// </summary>
public string Command
{
get { return this.command; }
set { this.command = value; }
}
/// <summary>
/// Creates a new CommandAction.
/// </summary>
public CommandAction()
{
}
/// <summary>
/// Creates a new CommandAction with the given command.
/// </summary>
/// <param name="command">the CLI command to execute.</param>
public CommandAction(string command)
{
this.command = command;
}
}
}

View file

@ -0,0 +1,67 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// Retrieves an entry in the Asterisk database for a given family and key.<br/>
/// If an entry is found a DBGetResponseEvent is sent by Asterisk containing the
/// value, otherwise a ManagerError indicates that no entry matches.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.DBGetResponseEvent" />
public class DBGetAction : ManagerActionEvent
{
private string family;
private string key;
public override string Action
{
get { return "DBGet"; }
}
/// <summary> Returns the family of the key.
///
/// </summary>
/// <returns> the family of the key.
/// </returns>
/// <summary> Sets the family of the key.
///
/// </summary>
/// <param name="family">the family of the key.
/// </param>
public string Family
{
get { return family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the key of the entry to retrieve.
/// </summary>
public string Key
{
get { return key; }
set { this.key = value; }
}
public override Type ActionCompleteEventClass()
{
return typeof(Event.DBGetResponseEvent);
}
/// <summary>
/// Creates a new empty DBGetAction.
/// </summary>
public DBGetAction()
{
}
/// <summary>
/// Creates a new DBGetAction that retrieves the value of the database entry
/// with the given key in the given family.
/// </summary>
/// <param name="family">the family of the key</param>
/// <param name="key">the key of the entry to retrieve</param>
public DBGetAction(string family, string key)
{
this.family = family;
this.key = key;
}
}
}

View file

@ -0,0 +1,64 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// Adds or updates an entry in the Asterisk database for a given family, key, and value.<br/>
/// Available since Asterisk 1.2
/// </summary>
public class DBPutAction : ManagerAction
{
private string family;
private string key;
private string val;
override public string Action
{
get { return "DBPut"; }
}
/// <summary>
/// Get/Set the family of the key to set.
/// </summary>
public string Family
{
get { return this.family; }
set { this.family = value; }
}
/// <summary>
/// Get/Set the the key to set.
/// </summary>
public string Key
{
get { return this.key; }
set { this.key = value; }
}
/// <summary>
/// Get/Set the value to set.
/// </summary>
public string Val
{
get { return val; }
set { this.val = value; }
}
/// <summary>
/// Creates a new empty DBPutAction.
/// </summary>
public DBPutAction()
{
}
/// <summary>
/// Creates a new DBPutAction that sets the value of the database entry with the given key in the given family.
/// </summary>
/// <param name="family">the family of the key</param>
/// <param name="key">the key of the entry to set</param>
/// <param name="val">the value to set</param>
public DBPutAction(string family, string key, string val)
{
this.family = family;
this.key = key;
this.val = val;
}
}
}

View file

@ -0,0 +1,50 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// With the EventsAction you can specify what kind of events should be sent to this manager connection.
/// </summary>
public class EventsAction : ManagerAction
{
private string eventMask;
/// <summary>
/// Get the name of this action, i.e. "Events".
/// </summary>
override public string Action
{
get { return "Events"; }
}
/// <summary>
/// Get/Set the event mask.<br/>
/// Set to "on" if all events should be send, "off" if not events should be
/// sent or a combination of "system", "call" and "log" (separated by ',') to
/// specify what kind of events should be sent.
/// </summary>
public string EventMask
{
get { return this.eventMask; }
set { this.eventMask = value; }
}
/// <summary>
/// Creates a new empty EventsAction.
/// </summary>
public EventsAction()
{
}
/// <summary>
/// Creates a new EventsAction that applies the given event mask to the current manager connection.
/// </summary>
/// <param name="eventMask">the event mask.<br/>
/// Set to "on" if all events should be send, "off" if not events should be sent
/// or a combination of "system", "call" and "log" (separated by ',') to specify what kind of events should be sent.
/// </param>
public EventsAction(string eventMask)
{
this.eventMask = eventMask;
}
}
}

View file

@ -0,0 +1,43 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ExtensionStateAction queries the state of an extension in a given context.
/// </summary>
public class ExtensionStateAction : ManagerAction
{
private string exten;
private string context;
#region Action
/// <summary>
/// Get the name of this action, i.e. "ExtensionState".
/// </summary>
override public string Action
{
get { return "ExtensionState"; }
}
#endregion
#region Exten
/// <summary>
/// Get/Set the extension to query.
/// </summary>
public string Exten
{
get { return exten; }
set { this.exten = value; }
}
#endregion
#region Context
/// <summary>
/// Get/Set the name of the context that contains the extension to query.
/// </summary>
public string Context
{
get { return context; }
set { this.context = value; }
}
#endregion
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text;
using Asterisk.NET.Manager.Response;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The GetConfigAction sends a GetConfig command to the asterisk server.
/// </summary>
public class GetConfigAction : ManagerActionResponse
{
private string filename;
/// <summary>
/// Creates a new GetConfigAction.
/// </summary>
public GetConfigAction()
{
}
/// <summary>
/// Get the name of this action.
/// </summary>
public override string Action
{
get { return "GetConfig"; }
}
/// <summary>
/// Get the name of this action.
/// </summary>
/// <param name="filename">the configuration filename.</param>
/// </summary>
public GetConfigAction(string filename)
{
this.filename = filename;
}
/// <summary>
/// Get/Set the configuration filename.
/// </summary>
public string Filename
{
get { return this.filename; }
set { this.filename = value; }
}
public override object ActionCompleteResponseClass()
{
return new GetConfigResponse();
}
}
}

View file

@ -0,0 +1,65 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The GetVarAction queries for a channel variable.
/// </summary>
public class GetVarAction : ManagerAction
{
private string channel;
private string varName;
/// <summary>
/// Creates a new empty GetVarAction.
/// </summary>
public GetVarAction()
{
}
/// <summary>
/// Creates a new GetVarAction that queries for the given global variable.
/// </summary>
/// <param name="variable">the name of the global variable to query.</param>
public GetVarAction(string variable)
{
this.varName = variable;
}
/// <summary>
/// Creates a new GetVarAction that queries for the given local channel variable.
/// </summary>
/// <param name="channel">the name of the channel, for example "SIP/1234-9cd".</param>
/// <param name="variable">the name of the variable to query.</param>
public GetVarAction(string channel, string variable)
{
this.channel = channel;
this.varName = variable;
}
/// <summary>
/// Get the name of this action, i.e. "GetVar".
/// </summary>
override public string Action
{
get { return "GetVar"; }
}
/// <summary>
/// Get/Set the name of the channel, if you query for a local channel variable.
/// Leave empty to query for a global variable.
/// </summary>
public string Channel
{
get { return this.channel; }
set { this.channel = value; }
}
/// <summary>
/// Get/Set the name of the variable to query.
/// </summary>
public string Variable
{
get { return this.varName; }
set { this.varName = value; }
}
}
}

View file

@ -0,0 +1,44 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The HangupAction causes the pbx to hang up a given channel.
/// </summary>
public class HangupAction : ManagerAction
{
private string channel;
/// <summary>
/// Creates a new empty HangupAction.
/// </summary>
public HangupAction()
{
}
/// <summary>
/// Creates a new HangupAction that hangs up the given channel.
/// </summary>
/// <param name="channel">the name of the channel to hangup.</param>
public HangupAction(string channel)
{
this.channel = channel;
}
/// <summary>
/// Get the name of this action, i.e. "Hangup".
/// </summary>
override public string Action
{
get { return "Hangup"; }
}
/// <summary>
/// Get/Set the name of the channel to hangup.
/// </summary>
public string Channel
{
get { return this.channel; }
set { this.channel = value; }
}
}
}

View file

@ -0,0 +1,128 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The LoginAction authenticates the connection.<br/>
/// A successful login is the precondition for sending any other action except
/// for the ChallengeAction.<br/>
/// An unsuccessful login results in an ManagerError being received from the
/// server with a message set to "Authentication failed" and the socket being
/// closed by Asterisk.
/// </summary>
/// <seealso cref="Manager.Action.ChallengeAction" />
/// <seealso cref="Manager.Response.ManagerError" />
public class LoginAction : ManagerAction
{
private string username;
private string secret;
private string authType;
private string key;
private string events;
/// <summary>
/// Get the name of this action, i.e. "Login".
/// </summary>
override public string Action
{
get { return "Login"; }
}
/// <summary>
/// Get/Set the username as configured in asterik's <code>manager.conf</code>.</summary>
public string Username
{
get { return this.username; }
set { this.username = value; }
}
/// <summary>
/// Get/Set the secret to use when using cleartext login.<br/>
/// The secret contains the user's password as configured in Asterisk's <code>manager.conf</code>.<br/>
/// The secret and key properties are mutually exclusive.
/// </summary>
public string Secret
{
get { return this.secret; }
set { this.secret = value; }
}
/// <summary>
/// Get/Set the digest alogrithm when using challenge/response.<br/>
/// The digest algorithm is used to create the key based on the challenge and
/// the user's password.<br/>
/// Currently Asterisk supports only "MD5".
/// </summary>
public string AuthType
{
get { return this.authType; }
set { this.authType = value; }
}
/// <summary>
/// Get/Set the key.
/// </summary>
public string Key
{
get { return this.key; }
set { this.key = value; }
}
/// <summary>
/// Get/Set the event mask.<br/>
/// Set to "on" if all events should be send, "off" if not events should be sent or a combination of
/// "system", "call" and "log" (separated by ',') to specify what kind of events should be sent.
/// </summary>
public string Events
{
get { return this.events; }
set { this.events = value; }
}
/// <summary>
/// Creates a new empty LoginAction.
/// </summary>
public LoginAction()
{
}
/// <summary>
/// Creates a new LoginAction that performs a cleartext login.<br/>
/// You should not use cleartext login if you are concerned about security and login with a password hash instead.
/// </summary>
/// <param name="username">the username as configured in Asterisk's <code>manager.conf</code></param>
/// <param name="secret">the user's password as configured in Asterisk's <code>manager.conf</code></param>
/// <seealso cref="Manager.Action.ChallengeAction" />
public LoginAction(string username, string secret)
{
this.username = username;
this.secret = secret;
}
/// <summary>
/// Creates a new LoginAction that performs a login via challenge/response.
/// </summary>
/// <param name="username">the username as configured in Asterisk's <code>manager.conf</code></param>
/// <param name="authType">the digest alogrithm, must match the digest algorithm that was used with the corresponding ChallengeAction.</param>
/// <param name="key">the hash of the user's password and the challenge</param>
public LoginAction(string username, string authType, string key)
{
this.username = username;
this.authType = authType;
this.key = key;
}
/// <summary>
/// Creates a new LoginAction that performs a login via challenge/response.
/// </summary>
/// <param name="username">the username as configured in Asterisk's <code>manager.conf</code></param>
/// <param name="authType">the digest alogrithm, must match the digest algorithm that was used with the corresponding ChallengeAction.</param>
/// <param name="key">the hash of the user's password and the challenge</param>
/// <param name="events">the event mask.<br/>
/// Set to "on" if all events should be send, "off" if not events should be sent
/// or a combination of "system", "call" and "log" (separated by ',') to specify what kind of events should be sent.
/// </param>
public LoginAction(string username, string authType, string key, string events)
{
this.username = username;
this.authType = authType;
this.key = key;
this.events = events;
}
}
}

View file

@ -0,0 +1,25 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The LogoffAction causes the server to close the connection.
/// </summary>
public class LogoffAction : ManagerAction
{
/// <summary>
/// Get the name of this action, i.e. "Logoff".
/// </summary>
override public string Action
{
get { return "Logoff"; }
}
/// <summary>
/// Creates a new LogoffAction.
/// </summary>
public LogoffAction()
{
}
}
}

View file

@ -0,0 +1,54 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The MailboxCountAction queries the number of unread and read messages in a mailbox.<br/>
/// The MailboxCountAction returns a MailboxStatusResponse.
/// </summary>
/// <seealso cref="Manager.Response.MailboxCountResponse" />
public class MailboxCountAction : ManagerAction
{
private string mailbox;
/// <summary>
/// Get the name of this action, i.e. "MailboxCount".
/// </summary>
override public string Action
{
get { return "MailboxCount"; }
}
/// <summary>
/// Get/Set the name of the mailbox to query.<br/>
/// This can either be only the number of the mailbox or a string of the form
/// mailboxnumber@context.If no context is specified "default" is assumed.<br/>
/// This property is mandatory.
/// </summary>
public string Mailbox
{
get { return this.mailbox; }
set { this.mailbox = value; }
}
/// <summary>
/// Creates a new empty MailboxCountAction.
/// </summary>
public MailboxCountAction()
{
}
/// <summary>
/// Creates a new MailboxCountAction that queries the number of unread and
/// read messages in the given mailbox.
/// </summary>
/// <param name="mailbox">the name of the mailbox to query.<br/>
/// This can either be only the number of the mailbox or a string
/// of the form mailboxnumber@context.If no context is specified
/// "default" is assumed.
/// </param>
public MailboxCountAction(string mailbox)
{
this.mailbox = mailbox;
}
}
}

View file

@ -0,0 +1,57 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The MailboxStatusAction checks if a mailbox contains waiting messages.<br/>
/// The MailboxStatusAction returns a MailboxStatusResponse.
/// </summary>
/// <seealso cref="Manager.Response.MailboxStatusResponse" />
public class MailboxStatusAction : ManagerAction
{
private string mailbox;
/// <summary>
/// Get the name of this action, i.e. "MailboxStatus".
/// </summary>
override public string Action
{
get { return "MailboxStatus"; }
}
/// <summary>
/// Get/Set the name of the mailbox to query.<br/>
/// This can either be only the name of the mailbox or a string of the form
/// mailboxnumber@context. If no context is specified "default" is assumed.<br/>
/// Multiple mailboxes may be given, separated by ','. In this case the
/// action checks whether at least one of the given mailboxes has waiting
/// messages.<br/>
/// This property is mandatory.<br/>
/// Example: "1234,1235@mycontext"
/// </summary>
public string Mailbox
{
get { return this.mailbox; }
set { this.mailbox = value; }
}
/// <summary>
/// Creates a new empty MailboxStatusAction.
/// </summary>
public MailboxStatusAction()
{
}
/// <summary>
/// Creates a new MailboxStatusAction that checks for waiting messages in the given mailbox.
/// </summary>
/// <param name="mailbox">the name of the mailbox to check.<br/>
/// This can either be only the number of the mailbox or a string
/// of the form mailboxnumber@context.If no context is specified
/// "default" is assumed.
/// </param>
public MailboxStatusAction(string mailbox)
{
this.mailbox = mailbox;
}
}
}

View file

@ -0,0 +1,66 @@
using System.Text;
using System.Collections.Generic;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// This class implements the ManagerAction interface
/// and can serve as base class for your concrete Action implementations.
/// </summary>
public abstract class ManagerAction
{
private string actionId;
private string server;
private string proxyKey;
/// <summary>
/// Manager API Action key. Also use as ProxyAction key to <see cref="ProxyAction">ProxyAction</see> actions.
/// </summary>
public abstract string Action
{
get;
}
#region ActionId
public string ActionId
{
get { return this.actionId; }
set { this.actionId = value; }
}
#endregion
#region Server
/// <summary>
/// Specify a server to which to send your commands (x.x.x.x or hostname).<br/>
/// This should match the server name specified in your config file's "host" entry.
/// If you do not specify a server, the proxy will pick the first one it finds -- fine in single-server configurations.
/// </summary>
public string Server
{
get { return this.server; }
set { this.server = value; }
}
#endregion
#region ProxyKey
/// <summary>
/// You can use this as a simple authentication mechanism.<br/>
/// Rather than have to login with a username & password,
/// you can specify a <b>ProxyKey</b> that must be passed from
/// a client before requests are processed.<br/>
/// This is helpful in situations where you would like to authenticate and
/// execute an action in a single step.
/// </summary>
public virtual string ProxyKey
{
get { return this.proxyKey; }
set { this.proxyKey = value; }
}
#endregion
public override string ToString()
{
return Helper.ToString(this);
}
}
}

View file

@ -0,0 +1,20 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ManagerActionEvent is implemented by ManagerActions that
/// return their result not in a ManagerResponse but by sending a series of events.<br/>
/// The event type that indicates that Asterisk is finished is returned by the
/// ActionCompleteEventClass property.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.ResponseEvent"/>
public abstract class ManagerActionEvent : ManagerAction
{
/// <summary>
/// Returns the event type that indicates that Asterisk is finished sending response events for this action.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.ResponseEvent"/>
public abstract Type ActionCompleteEventClass();
}
}

View file

@ -0,0 +1,21 @@
using System;
using Asterisk.NET.Manager.Response;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ManagerActionResponse is implemented by ManagerActions that
/// return their result in a custom ManagerResponse<br/>
/// The response type that indicates that Asterisk is finished is returned by the
/// ActionCompleteResponseClass property.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.ResponseEvent"/>
public abstract class ManagerActionResponse : ManagerAction
{
/// <summary>
/// Returns the response type that indicates that Asterisk is finished sending response for this action.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Response"/>
public abstract object ActionCompleteResponseClass();
}
}

View file

@ -0,0 +1,130 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The MonitorAction starts monitoring (recording) a channel.<br/>
/// It is implemented in <code>res/res_monitor.c</code>
/// </summary>
public class MonitorAction : ManagerAction
{
private string channel;
private string file;
private string format;
private bool mix;
#region Action
/// <summary>
/// Get the name of this action, i.e. "Monitor".
/// </summary>
override public string Action
{
get { return "Monitor"; }
}
#endregion
#region Channel
/// <summary>
/// Get/Set the name of the channel to monitor.<br/>
/// This property is mandatory.
/// </summary>
public string Channel
{
get { return this.channel; }
set { this.channel = value; }
}
#endregion
#region File
/// <summary>
/// Get/Set the name of the file to which the voice data is written.<br/>
/// If this property is not set it defaults to to the channel name as per CLI with the '/' replaced by '-'.
/// </summary>
public string File
{
get { return this.file; }
set { this.file = value; }
}
#endregion
#region Format
/// <summary>
/// Get/Set the format to use for encoding the voice files.<br/>
/// If this property is not set it defaults to "wav".
/// </summary>
public string Format
{
get { return this.format; }
set { this.format = value; }
}
#endregion
#region Mix
/// <summary>
/// Returns true if the two voice files should be joined at the end of the call.
/// </summary>
public bool Mix
{
get { return this.mix; }
set { this.mix = value; }
}
#endregion
#region MonitorAction()
/// <summary>
/// Creates a new empty MonitorAction.
/// </summary>
public MonitorAction()
{
}
#endregion
#region MonitorAction(string channel, string file)
/// <summary>
/// Creates a new MonitorAction that starts monitoring the given channel and
/// writes voice data to the given file(s).
/// </summary>
/// <param name="channel">the name of the channel to monitor</param>
/// <param name="file">the (base) name of the file(s) to which the voice data is written</param>
public MonitorAction(string channel, string file)
{
this.channel = channel;
this.file = file;
}
#endregion
#region MonitorAction(string channel, string file)
/// <summary>
/// Creates a new MonitorAction that starts monitoring the given channel and
/// writes voice data to the given file(s).
/// </summary>
/// <param name="channel">the name of the channel to monitor</param>
/// <param name="file">the (base) name of the file(s) to which the voice data is written</param>
/// <param name="format">the format to use for encoding the voice files</param>
public MonitorAction(string channel, string file, string format)
{
this.channel = channel;
this.file = file;
this.format = format;
}
#endregion
#region MonitorAction(string channel, string file, string format, int mix)
/// <summary>
/// Creates a new MonitorAction that starts monitoring the given channel and
/// writes voice data to the given file(s).
/// </summary>
/// <param name="channel">the name of the channel to monitor</param>
/// <param name="file">the (base) name of the file(s) to which the voice data is written</param>
/// <param name="format">the format to use for encoding the voice files</param>
/// <param name="mix">true if the two voice files should be joined at the end of the call</param>
public MonitorAction(string channel, string file, string format, bool mix)
{
this.channel = channel;
this.file = file;
this.format = format;
this.mix = mix;
}
#endregion
}
}

View file

@ -0,0 +1,239 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The OriginateAction generates an outgoing call to the extension in the given
/// context with the given priority or to a given application with optional
/// parameters.<br/>
/// If you want to connect to an extension use the properties context, exten and
/// priority. If you want to connect to an application use the properties
/// application and data if needed. Note that no call detail record will be
/// written when directly connecting to an application, so it may be better to
/// connect to an extension that starts the application you wish to connect to.<br/>
/// The response to this action is sent when the channel has been answered and
/// asterisk starts connecting it to the given extension. So be careful not to
/// choose a too short timeout when waiting for the response.<br/>
/// If you set async to <code>true</code> Asterisk reports an OriginateSuccess-
/// and OriginateFailureEvents. The action id of these events equals the action
/// id of this OriginateAction.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.OriginateSuccessEvent" />
/// <seealso cref="Asterisk.NET.Manager.Event.OriginateFailureEvent" />
public class OriginateAction : ManagerActionEvent
{
private string channel;
private string exten;
private string context;
private int priority;
private int timeout;
private string callerId;
private Dictionary<string, string> variables;
private string account;
private string application;
private string data;
private bool async;
#region Action
/// <summary>
/// Get the name of this action, i.e. "Originate".
/// </summary>
override public string Action
{
get { return "Originate"; }
}
#endregion
#region Account
/// <summary>
/// Get/Set the account code to use for the originated call.
/// The account code is included in the call detail record generated for this call and will be used for billing.
/// </summary>
public string Account
{
get { return account; }
set { this.account = value; }
}
#endregion
#region CallerId
/// <summary>
/// Get/Set the caller id to set on the outgoing channel.
/// </summary>
public string CallerId
{
get { return callerId; }
set { this.callerId = value; }
}
#endregion
#region Channel
/// <summary>
/// Get/Set Channel on which to originate the call (The same as you specify in the Dial application command)<br/>
/// This property is required.
/// </summary>
public string Channel
{
get { return channel; }
set { this.channel = value; }
}
#endregion
#region Context
/// <summary>
/// Get/Set the name of the context of the extension to connect to.
/// If you set the context you also have to set the exten and priority properties.
/// </summary>
public string Context
{
get { return context; }
set { this.context = value; }
}
#endregion
#region Exten
/// <summary>
/// Get/Ser the extension to connect to.
/// If you set the extension you also have to set the context and priority properties.
/// </summary>
public string Exten
{
get { return exten; }
set { this.exten = value; }
}
#endregion
#region Priority
/// <summary>
/// Get /Set the priority of the extension to connect to.
/// If you set the priority you also have to set the context and exten properties.
/// </summary>
public int Priority
{
get { return priority; }
set { this.priority = value; }
}
#endregion
#region Application
/// <summary>
/// Get/Set Application to use on connect (use Data for parameters)
/// </summary>
public string Application
{
get { return application; }
set { this.application = value; }
}
#endregion
#region Data
/// <summary>
/// Get/Set the parameters to pass to the application.
/// Data if Application parameter is user
/// </summary>
/// <summary> Sets the parameters to pass to the application.</summary>
public string Data
{
get { return data; }
set { this.data = value; }
}
#endregion
#region Async
/// <summary>
/// Get/Set <code>true</code> if this is a fast origination.<br/>
/// For the origination to be asynchronous (allows multiple calls to be generated without waiting for a response).<br/>
/// Will send OriginateSuccess- and OriginateFailureEvents.
/// </summary>
public bool Async
{
get { return async; }
set { this.async = value; }
}
#endregion
#region ActionCompleteEventClass
public override Type ActionCompleteEventClass()
{
return typeof(Event.OriginateResponseEvent);
}
#endregion
#region Timeout
/// <summary>
/// Get/Set the timeout for the origination in seconds.<br/>
/// The channel must be answered within this time, otherwise the origination
/// is considered to have failed and an OriginateFailureEvent is generated.<br/>
/// If not set, Asterisk assumes a default value of 30000 meaning 30 seconds.
/// </summary>
public int Timeout
{
get { return timeout; }
set { this.timeout = value; }
}
#endregion
#region Variable
/// <summary>
/// Get/Set the variables to set on the originated call.<br/>
/// Variable assignments are of the form "VARNAME=VALUE". You can specify
/// multiple variable assignments separated by the '|' character.<br/>
/// Example: "VAR1=abc|VAR2=def" sets the channel variables VAR1 to "abc" and VAR2 to "def".
/// </summary>
public string Variable
{
get { return Helper.JoinVariables(variables, Common.VAR_DELIMITER, "="); }
set { variables = Helper.ParseVariables(variables, value, Common.VAR_DELIMITER); }
}
#endregion
#region GetVariables()
/// <summary>
/// Get the variables dictionary to set on the originated call.
/// </summary>
public Dictionary<string, string> GetVariables()
{
return variables;
}
#endregion
#region SetVariables(IDictionary vars)
/// <summary>
/// Set the variables dictionary to set on the originated call.
/// </summary>
public void SetVariables(Dictionary<string, string> vars)
{
this.variables = vars;
}
#endregion
#region GetVariable(string name, string val)
/// <summary>
/// Gets a variable on the originated call. Replaces any existing variable with the same name.
/// </summary>
public string GetVariable(string key)
{
if (variables == null)
return string.Empty;
return variables[key];
}
#endregion
#region SetVariable(string name, string val)
/// <summary>
/// Sets a variable dictionary on the originated call. Replaces any existing variable with the same name.
/// </summary>
public void SetVariable(string key, string value)
{
if (variables == null)
variables = new Dictionary<string, string>();
if (variables.ContainsKey(key))
variables[key] = value;
else
variables.Add(key, value);
}
#endregion
}
}

View file

@ -0,0 +1,32 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The ParkedCallsAction requests a list of all currently parked calls.<br/>
/// For each active channel a ParkedCallEvent is generated. After all parked
/// calls have been reported a ParkedCallsCompleteEvent is generated.
/// </summary>
/// <seealso cref="Asterisk.NET.Manager.Event.ParkedCallEvent"/>
/// <seealso cref="Asterisk.NET.Manager.Event.ParkedCallsCompleteEvent"/>
public class ParkedCallsAction : ManagerActionEvent
{
/// <summary> Get the name of this action, i.e. "ParkedCalls".</summary>
public override string Action
{
get { return "ParkedCalls"; }
}
public override Type ActionCompleteEventClass()
{
return typeof(Event.ParkedCallsCompleteEvent);
}
/// <summary>
/// Creates a new ParkedCallsAction.
/// </summary>
public ParkedCallsAction()
{
}
}
}

View file

@ -0,0 +1,26 @@
using System;
namespace Asterisk.NET.Manager.Action
{
/// <summary>
/// The PingAction will ellicit a 'Pong' response, it is used to keep the manager
/// connection open and performs no operation.
/// </summary>
public class PingAction : ManagerAction
{
/// <summary>
/// Get the name of this action, i.e. "Ping".
/// </summary>
override public string Action
{
get { return "Ping"; }
}
/// <summary>
/// Creates a new PingAction.
/// </summary>
public PingAction()
{
}
}
}

Some files were not shown because too many files have changed in this diff Show more