578 lines
15 KiB
C#
578 lines
15 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Text.RegularExpressions;
|
|
using System.Web;
|
|
|
|
namespace AsterNET.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 readonly 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;
|
|
|
|
#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 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 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 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 request["type"]; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Language
|
|
|
|
/// <summary>
|
|
/// Returns the language, for example "en".
|
|
/// </summary>
|
|
public string Language
|
|
{
|
|
get { return request["language"]; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CallerId
|
|
|
|
public string CallerId
|
|
{
|
|
get
|
|
{
|
|
string callerIdName = request["calleridname"];
|
|
string callerId = request["callerid"];
|
|
if (callerIdName != null)
|
|
{
|
|
if (callerId == null || callerId.ToLower(Helper.CultureInfo) == "unknown")
|
|
return null;
|
|
return callerId;
|
|
}
|
|
return callerId10();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CallerIdName
|
|
|
|
public string CallerIdName
|
|
{
|
|
get
|
|
{
|
|
string callerIdName = request["calleridname"];
|
|
if (callerIdName != null)
|
|
{
|
|
// Asterisk 1.2
|
|
if (callerIdName.ToLower(Helper.CultureInfo) == "unknown")
|
|
return null;
|
|
return callerIdName;
|
|
}
|
|
return callerIdName10();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Asterisk 1.0 CallerID and CallerIdName
|
|
|
|
private string callerId10()
|
|
{
|
|
int lbPosition;
|
|
int rbPosition;
|
|
|
|
if (!callerIdCreated)
|
|
{
|
|
rawCallerId = 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 = 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;
|
|
return callerIdName;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Dnid
|
|
|
|
public string Dnid
|
|
{
|
|
get
|
|
{
|
|
string dnid = request["dnid"];
|
|
if (dnid == null || dnid.ToLower(Helper.CultureInfo) == "unknown")
|
|
return null;
|
|
return dnid;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rdnis
|
|
|
|
public string Rdnis
|
|
{
|
|
get
|
|
{
|
|
string rdnis = 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 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 request["extension"]; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Priority
|
|
|
|
/// <summary>
|
|
/// Returns the priority in the dial plan from which the AGI script was called.
|
|
/// </summary>
|
|
public string Priority
|
|
{
|
|
get
|
|
{
|
|
if (request["priority"] != null)
|
|
{
|
|
return request["priority"];
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Enhanced
|
|
|
|
/// <summary>
|
|
/// Returns wheather this agi is passed audio (EAGI - Enhanced AGI).<br />
|
|
/// Enhanced AGI is currently not supported on FastAGI.<br />
|
|
/// true if this agi is passed audio, false otherwise.
|
|
/// </summary>
|
|
public bool Enhanced
|
|
{
|
|
get
|
|
{
|
|
if (request["enhanced"] != null && 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 request["accountCode"]; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region LocalAddress
|
|
|
|
public IPAddress LocalAddress { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region LocalPort
|
|
|
|
public int LocalPort { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region RemoteAddress
|
|
|
|
public IPAddress RemoteAddress { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region RemotePort
|
|
|
|
public int RemotePort { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region Script()
|
|
|
|
/// <summary>
|
|
/// Returns the name of the script to execute.
|
|
/// </summary>
|
|
public string Script
|
|
{
|
|
get
|
|
{
|
|
if (script != null)
|
|
return script;
|
|
|
|
script = 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(request["callingani2"]);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CallingPres
|
|
|
|
public int CallingPres
|
|
{
|
|
get
|
|
{
|
|
if (request["callingpres"] == null)
|
|
return -1;
|
|
try
|
|
{
|
|
return Int32.Parse(request["callingpres"]);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CallingTns
|
|
|
|
public int CallingTns
|
|
{
|
|
get
|
|
{
|
|
if (request["callingtns"] == null)
|
|
return -1;
|
|
try
|
|
{
|
|
return Int32.Parse(request["callingtns"]);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CallingTon
|
|
|
|
public int CallingTon
|
|
{
|
|
get
|
|
{
|
|
if (request["callington"] == null)
|
|
return -1;
|
|
try
|
|
{
|
|
return Int32.Parse(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(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;
|
|
|
|
var map = new Dictionary<string, string>(lines.Count);
|
|
foreach (var 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)
|
|
{
|
|
var 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 (var 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
|
|
}
|
|
} |