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 { /// /// Status code (200) indicating Asterisk successfully processed the AGICommand. /// SC_SUCCESS = 200, /// /// Status code (510) indicating Asterisk was unable to process the /// AGICommand because there is no command with the given name available. /// SC_INVALID_OR_UNKNOWN_COMMAND = 510, /// /// Status code (511) indicating Asterisk was unable to process the /// AGICommand because the channel is dead. /// SC_DEAD_CHANNEL = 511, /// /// Status code (520) indicating Asterisk was unable to process the /// AGICommand because the syntax used was not correct. This is most likely /// due to missing required parameters or additional parameters sent that are /// not understood.
/// Ensure proper quoting of the parameters when you receive this status /// code. ///
SC_INVALID_COMMAND_SYNTAX = 520 } #endregion /// /// Default implementation of the AGIReply interface. /// public class AGIReply { #region Variables private readonly string firstLine; private readonly List lines; /// Additional attributes contained in this reply, for example endpos. private Dictionary attributes; private bool attributesCreated; /// The contents of the parenthesis. private string extra; private bool extraCreated; private Match matcher; /// The result, that is the part directly following the "result=" string. private string result; private bool resultCreated; /// The status code. private int status; private bool statusCreated; /// In case of status == 520 (invalid command syntax) this attribute contains the synopsis of the command. private string synopsis; private bool synopsisCreated; /// In case of status == 520 (invalid command syntax) this attribute contains the usage of the command. private string usage; #endregion #region Constructor - AGIReply() public AGIReply() { } #endregion #region Constructor - AGIReply(lines) public AGIReply(List 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 /// /// Returns the return code (the result as int). /// /// the return code or -1 if the result is not an int. public int ResultCode { get { string result; result = GetResult(); if (result == null) return -1; try { return Int32.Parse(result); } catch { return -1; } } } #endregion #region ResultCodeAsChar /// /// Returns the return code as character. /// /// the return code as character. public char ResultCodeAsChar { get { int resultCode = ResultCode; if (resultCode < 0) return (char) (0x0); return (char) resultCode; } } #endregion #region Extra /// /// Returns the text in parenthesis contained in this reply.
/// 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. ///
/// the text in the parenthesis or null if not set. 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() /// /// Returns the result, that is the part directly following the "result=" string. /// /// the result. 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() /// /// Returns the status code.
/// Supported status codes are:
/// 200 Success
/// 510 Invalid or unknown command
/// 520 Invalid command syntax
///
/// the status code. 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) /// /// Returns an additional attribute contained in the reply.
/// 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. ///
/// the name of the attribute to retrieve. The name is case insensitive. /// the value of the attribute or null if it is not set. 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(); 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() /// /// Returns the synopsis of the command sent if Asterisk expected a different /// syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX). /// /// the synopsis of the command sent, null if there were no syntax errors. 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() /// /// Returns the usage of the command sent if Asterisk expected a different /// syntax (getStatus() == SC_INVALID_COMMAND_SYNTAX). /// /// /// the usage of the command sent, /// null if there were no syntax errors. /// public string GetUsage() { return usage; } #endregion #region ToString() public override string ToString() { return Helper.ToString(this); } #endregion } }