asternet/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs

366 lines
11 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace AsterNET.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 (511) indicating Asterisk was unable to process the
/// AGICommand because the channel is dead.
/// </summary>
SC_DEAD_CHANNEL = 511,
/// <summary>
/// Status code (520) indicating Asterisk was unable to process the
/// AGICommand because the syntax used was not correct. This is most likely
/// due to missing required parameters or additional parameters sent that are
/// 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 readonly string firstLine;
private readonly List<string> lines;
/// <summary>Additional attributes contained in this reply, for example endpos.</summary>
private Dictionary<string, string> attributes;
private bool attributesCreated;
/// <summary> The contents of the parenthesis.</summary>
private string extra;
private bool extraCreated;
private Match matcher;
/// <summary> The result, that is the part directly following the "result=" string.</summary>
private string result;
private bool resultCreated;
/// <summary> The status code.</summary>
private int status;
private bool statusCreated;
/// <summary> In case of status == 520 (invalid command syntax) this attribute contains the synopsis of the command.</summary>
private string synopsis;
private bool synopsisCreated;
/// <summary> In case of status == 520 (invalid command syntax) this attribute contains the usage of the command.</summary>
private string usage;
#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;
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 null 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 null 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 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, null 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 = lines[1];
synopsisMatcher = Common.AGI_SYNOPSIS_PATTERN.Match(secondLine);
if (synopsisMatcher.Success)
synopsis = synopsisMatcher.Groups[1].Value;
}
synopsisCreated = true;
var sbUsage = new StringBuilder();
string line;
for (int i = 2; i < lines.Count; i++)
{
line = 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,
/// null if there were no syntax errors.
/// </returns>
public string GetUsage()
{
return usage;
}
#endregion
#region ToString()
public override string ToString()
{
return Helper.ToString(this);
}
#endregion
}
}