From f6d419aa39f16cb17f2b8e59ac51951340ef6895 Mon Sep 17 00:00:00 2001 From: ppumkin_cp Date: Fri, 18 Jan 2013 07:55:50 -0800 Subject: [PATCH] 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. --- Asterisk.2013/Asterisk.2013.sln | 43 + Asterisk.2013/Asterisk.2013.vssscc | 10 + .../Asterisk.NET.WinForm.csproj | 127 + .../Asterisk.NET.WinForm.csproj.vspscc | 10 + .../Asterisk.NET.WinForm/FormMain.Designer.cs | 188 ++ .../Asterisk.NET.WinForm/FormMain.cs | 64 + .../Asterisk.NET.WinForm/FormMain.resx | 120 + Asterisk.2013/Asterisk.NET.WinForm/Program.cs | 20 + .../Properties/AssemblyInfo.cs | 33 + .../Properties/Resources.Designer.cs | 63 + .../Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + Asterisk.2013/Asterisk.NET.WinForm/app.config | 3 + .../Asterisk.NET/Asterisk.NET.csproj | 324 +++ .../Asterisk.NET/Asterisk.NET.csproj.vspscc | 10 + Asterisk.2013/Asterisk.NET/Common.cs | 55 + .../Asterisk.NET/FastAGI/AGIChannel.cs | 44 + .../FastAGI/AGIConnectionHandler.cs | 118 + .../Asterisk.NET/FastAGI/AGIReader.cs | 89 + .../Asterisk.NET/FastAGI/AGIReply.cs | 315 +++ .../Asterisk.NET/FastAGI/AGIRequest.cs | 534 ++++ .../Asterisk.NET/FastAGI/AGIScript.cs | 817 ++++++ .../Asterisk.NET/FastAGI/AGIWriter.cs | 31 + .../Asterisk.NET/FastAGI/AsteriskFastAGI.cs | 220 ++ .../FastAGI/Command/AGICommand.cs | 26 + .../FastAGI/Command/AnswerCommand.cs | 14 + .../FastAGI/Command/ChannelStatusCommand.cs | 55 + .../Command/ControlStreamFileCommand.cs | 204 ++ .../FastAGI/Command/DatabaseDelCommand.cs | 59 + .../FastAGI/Command/DatabaseDelTreeCommand.cs | 64 + .../FastAGI/Command/DatabaseGetCommand.cs | 50 + .../FastAGI/Command/DatabasePutCommand.cs | 60 + .../FastAGI/Command/ExecCommand.cs | 57 + .../FastAGI/Command/GetDataCommand.cs | 109 + .../FastAGI/Command/GetFullVariableCommand.cs | 73 + .../FastAGI/Command/GetOptionCommand.cs | 79 + .../FastAGI/Command/GetVariableCommand.cs | 38 + .../FastAGI/Command/HangupCommand.cs | 47 + .../FastAGI/Command/NoopCommand.cs | 20 + .../FastAGI/Command/ReceiveCharCommand.cs | 47 + .../FastAGI/Command/ReceiveTextCommand.cs | 49 + .../FastAGI/Command/RecordFileCommand.cs | 143 ++ .../FastAGI/Command/SayAlphaCommand.cs | 58 + .../FastAGI/Command/SayDateTimeCommand.cs | 133 + .../FastAGI/Command/SayDigitsCommand.cs | 70 + .../FastAGI/Command/SayNumberCommand.cs | 59 + .../FastAGI/Command/SayPhoneticCommand.cs | 60 + .../FastAGI/Command/SayTimeCommand.cs | 60 + .../FastAGI/Command/SendImageCommand.cs | 42 + .../FastAGI/Command/SendTextCommand.cs | 40 + .../FastAGI/Command/SetAutoHangupCommand.cs | 45 + .../FastAGI/Command/SetCallerIdCommand.cs | 35 + .../FastAGI/Command/SetContextCommand.cs | 35 + .../FastAGI/Command/SetExtensionCommand.cs | 37 + .../FastAGI/Command/SetMusicOffCommand.cs | 20 + .../FastAGI/Command/SetMusicOnCommand.cs | 44 + .../FastAGI/Command/SetPriorityCommand.cs | 62 + .../FastAGI/Command/SetVariableCommand.cs | 47 + .../FastAGI/Command/StreamFileCommand.cs | 122 + .../FastAGI/Command/TDDModeCommand.cs | 40 + .../FastAGI/Command/VerboseCommand.cs | 71 + .../FastAGI/Command/WaitForDigitCommand.cs | 45 + .../FastAGI/Exceptions/AGIException.cs | 33 + .../FastAGI/Exceptions/AGIHangupException.cs | 14 + .../FastAGI/Exceptions/AGINetworkException.cs | 14 + .../InvalidCommandSyntaxException.cs | 41 + .../InvalidOrUnknownCommandException.cs | 19 + .../Asterisk.NET/FastAGI/MappingStrategy.cs | 128 + .../FastAGI/Script/AGINoAction.cs | 10 + Asterisk.2013/Asterisk.NET/Helper.cs | 834 +++++++ Asterisk.2013/Asterisk.NET/IO/ServerSocket.cs | 38 + .../Asterisk.NET/IO/SocketConnection.cs | 204 ++ Asterisk.2013/Asterisk.NET/IParseSupport.cs | 16 + Asterisk.2013/Asterisk.NET/Logger.cs | 261 ++ .../Manager/Action/AbsoluteTimeoutAction.cs | 68 + .../Action/AgentCallbackLoginAction.cs | 107 + .../Manager/Action/AgentLogoffAction.cs | 94 + .../Manager/Action/AgentsAction.cs | 41 + .../Manager/Action/ChallengeAction.cs | 50 + .../Manager/Action/ChangeMonitorAction.cs | 69 + .../Manager/Action/CommandAction.cs | 45 + .../Manager/Action/DBGetAction.cs | 67 + .../Manager/Action/DBPutAction.cs | 64 + .../Manager/Action/EventsAction.cs | 50 + .../Manager/Action/ExtensionStateAction.cs | 43 + .../Manager/Action/GetConfigAction.cs | 54 + .../Manager/Action/GetVarAction.cs | 65 + .../Manager/Action/HangupAction.cs | 44 + .../Manager/Action/LoginAction.cs | 128 + .../Manager/Action/LogoffAction.cs | 25 + .../Manager/Action/MailboxCountAction.cs | 54 + .../Manager/Action/MailboxStatusAction.cs | 57 + .../Manager/Action/ManagerAction.cs | 66 + .../Manager/Action/ManagerActionEvent.cs | 20 + .../Manager/Action/ManagerActionResponse.cs | 21 + .../Manager/Action/MonitorAction.cs | 130 + .../Manager/Action/OriginateAction.cs | 239 ++ .../Manager/Action/ParkedCallsAction.cs | 32 + .../Asterisk.NET/Manager/Action/PingAction.cs | 26 + .../Manager/Action/ProxyAction.cs | 10 + .../Manager/Action/QueueAddAction.cs | 120 + .../Manager/Action/QueuePauseAction.cs | 112 + .../Manager/Action/QueueRemoveAction.cs | 62 + .../Manager/Action/QueueStatusAction.cs | 65 + .../Manager/Action/RedirectAction.cs | 102 + .../Manager/Action/SIPPeersAction.cs | 31 + .../Manager/Action/SIPShowPeerAction.cs | 48 + .../Manager/Action/SetCDRUserFieldAction.cs | 81 + .../Manager/Action/SetVarAction.cs | 81 + .../Manager/Action/StatusAction.cs | 33 + .../Manager/Action/StopMonitorAction.cs | 53 + .../Manager/Action/UpdateConfigAction.cs | 197 ++ .../Manager/Action/ZapDNDOffAction.cs | 42 + .../Manager/Action/ZapDNDOnAction.cs | 43 + .../Manager/Action/ZapDialOffhookAction.cs | 53 + .../Manager/Action/ZapHangupAction.cs | 44 + .../Manager/Action/ZapShowChannelsAction.cs | 32 + .../Manager/Action/ZapTransferAction.cs | 28 + .../Asterisk.NET/Manager/AsteriskVersion.cs | 12 + .../Manager/Event/AGIExecEvent.cs | 51 + .../Manager/Event/AbstractAgentEvent.cs | 42 + .../Manager/Event/AbstractAgentVariables.cs | 75 + .../Manager/Event/AbstractChannelEvent.cs | 91 + .../Manager/Event/AbstractMeetmeEvent.cs | 30 + .../Manager/Event/AbstractParkedCallEvent.cs | 50 + .../Manager/Event/AbstractQueueMemberEvent.cs | 33 + .../Manager/Event/AgentCallbackLoginEvent.cs | 31 + .../Manager/Event/AgentCallbackLogoffEvent.cs | 92 + .../Manager/Event/AgentCalledEvent.cs | 89 + .../Manager/Event/AgentCompleteEvent.cs | 41 + .../Manager/Event/AgentConnectEvent.cs | 44 + .../Manager/Event/AgentDumpEvent.cs | 14 + .../Manager/Event/AgentLoginEvent.cs | 32 + .../Manager/Event/AgentLogoffEvent.cs | 32 + .../Manager/Event/AgentsCompleteEvent.cs | 16 + .../Asterisk.NET/Manager/Event/AgentsEvent.cs | 82 + .../Manager/Event/AlarmClearEvent.cs | 14 + .../Asterisk.NET/Manager/Event/AlarmEvent.cs | 34 + .../Manager/Event/AsyncAGIEvent.cs | 40 + .../Asterisk.NET/Manager/Event/BridgeEvent.cs | 169 ++ .../Asterisk.NET/Manager/Event/CdrEvent.cs | 114 + .../Manager/Event/ChannelReloadEvent.cs | 57 + .../Manager/Event/ChannelUpdateEvent.cs | 75 + .../Manager/Event/ConnectEvent.cs | 25 + .../Manager/Event/ConnectionStateEvent.cs | 22 + .../Manager/Event/DBGetResponseEvent.cs | 44 + .../Manager/Event/DNDStateEvent.cs | 40 + .../Asterisk.NET/Manager/Event/DTMFEvent.cs | 43 + .../Asterisk.NET/Manager/Event/DialEvent.cs | 102 + .../Manager/Event/DisconnectEvent.cs | 14 + .../Manager/Event/ExtensionStatusEvent.cs | 52 + .../Manager/Event/FaxReceivedEvent.cs | 98 + .../Asterisk.NET/Manager/Event/HangupEvent.cs | 34 + .../Asterisk.NET/Manager/Event/HoldEvent.cs | 24 + .../Manager/Event/HoldedCallEvent.cs | 52 + .../Asterisk.NET/Manager/Event/JabberEvent.cs | 28 + .../Manager/Event/JitterBufStatsEvent.cs | 110 + .../Asterisk.NET/Manager/Event/JoinEvent.cs | 45 + .../Asterisk.NET/Manager/Event/LeaveEvent.cs | 14 + .../Asterisk.NET/Manager/Event/LinkEvent.cs | 18 + .../Manager/Event/LogChannelEvent.cs | 59 + .../Manager/Event/ManagerEvent.cs | 178 ++ .../Manager/Event/MasqueradeEvent.cs | 43 + .../Manager/Event/MeetmeEndEvent.cs | 11 + .../Manager/Event/MeetmeJoinEvent.cs | 28 + .../Manager/Event/MeetmeLeaveEvent.cs | 35 + .../Manager/Event/MeetmeMuteEvent.cs | 18 + .../Manager/Event/MeetmeStopTalkingEvent.cs | 14 + .../Manager/Event/MeetmeTalkRequestEvent.cs | 18 + .../Manager/Event/MeetmeTalkingEvent.cs | 21 + .../Manager/Event/MessageWaitingEvent.cs | 53 + .../Manager/Event/MobileStatusEvent.cs | 27 + .../Manager/Event/ModuleLoadReportEvent.cs | 32 + .../Manager/Event/MonitorStartEvent.cs | 14 + .../Manager/Event/MonitorStopEvent.cs | 14 + .../Manager/Event/NewAccountCodeEvent.cs | 27 + .../Manager/Event/NewCallerIdEvent.cs | 84 + .../Manager/Event/NewChannelEvent.cs | 14 + .../Manager/Event/NewExtenEvent.cs | 68 + .../Manager/Event/NewStateEvent.cs | 14 + .../Manager/Event/OriginateResponseEvent.cs | 57 + .../Asterisk.NET/Manager/Event/PRIEvent.cs | 41 + .../Manager/Event/ParkedCallEvent.cs | 70 + .../Manager/Event/ParkedCallGiveUpEvent.cs | 15 + .../Manager/Event/ParkedCallTimeOutEvent.cs | 15 + .../Manager/Event/ParkedCallsCompleteEvent.cs | 15 + .../Manager/Event/PeerEntryEvent.cs | 118 + .../Manager/Event/PeerStatusEvent.cs | 83 + .../Manager/Event/PeerlistCompleteEvent.cs | 27 + .../Manager/Event/QueueCallerAbandonEvent.cs | 40 + .../Manager/Event/QueueEntryEvent.cs | 62 + .../Asterisk.NET/Manager/Event/QueueEvent.cs | 33 + .../Manager/Event/QueueMemberAddedEvent.cs | 101 + .../Manager/Event/QueueMemberEvent.cs | 131 + .../Manager/Event/QueueMemberPausedEvent.cs | 45 + .../Manager/Event/QueueMemberPenaltyEvent.cs | 24 + .../Manager/Event/QueueMemberRemovedEvent.cs | 27 + .../Manager/Event/QueueMemberStatusEvent.cs | 13 + .../Manager/Event/QueueParamsEvent.cs | 114 + .../Manager/Event/QueueStatusCompleteEvent.cs | 15 + .../Manager/Event/RTCPReceivedEvent.cs | 92 + .../Manager/Event/RTCPSentEvent.cs | 93 + .../Manager/Event/RTPReceiverStatEvent.cs | 54 + .../Manager/Event/RTPSenderStatEvent.cs | 55 + .../Manager/Event/RegistryEvent.cs | 95 + .../Asterisk.NET/Manager/Event/ReloadEvent.cs | 47 + .../Asterisk.NET/Manager/Event/RenameEvent.cs | 34 + .../Manager/Event/ResponseEvent.cs | 35 + .../Event/ShowDialPlanCompleteEvent.cs | 44 + .../Manager/Event/ShutdownEvent.cs | 60 + .../Manager/Event/StatusCompleteEvent.cs | 24 + .../Asterisk.NET/Manager/Event/StatusEvent.cs | 166 ++ .../Manager/Event/TransferEvent.cs | 75 + .../Asterisk.NET/Manager/Event/UnholdEvent.cs | 19 + .../Manager/Event/UnknownEvent.cs | 20 + .../Asterisk.NET/Manager/Event/UnlinkEvent.cs | 16 + .../Manager/Event/UnparkedCallEvent.cs | 26 + .../Asterisk.NET/Manager/Event/UserEvent.cs | 60 + .../Asterisk.NET/Manager/Event/VarSet.cs | 60 + .../Event/ZapShowChannelsCompleteEvent.cs | 16 + .../Manager/Event/ZapShowChannelsEvent.cs | 79 + .../AuthenticationFailedException.cs | 28 + .../Exceptions/EventTimeoutException.cs | 41 + .../Manager/Exceptions/ManagerException.cs | 28 + .../Manager/Exceptions/TimeoutException.cs | 18 + .../Asterisk.NET/Manager/IResponseHandler.cs | 33 + .../Asterisk.NET/Manager/ManagerConnection.cs | 2217 +++++++++++++++++ .../Asterisk.NET/Manager/ManagerReader.cs | 354 +++ .../Asterisk.NET/Manager/Originate.cs | 179 ++ .../Manager/Response/ChallengeResponse.cs | 21 + .../Manager/Response/CommandResponse.cs | 27 + .../Response/ExtensionStateResponse.cs | 33 + .../Manager/Response/GetConfigResponse.cs | 113 + .../Manager/Response/MailboxCountResponse.cs | 39 + .../Manager/Response/MailboxStatusResponse.cs | 32 + .../Manager/Response/ManagerError.cs | 22 + .../Manager/Response/ManagerResponse.cs | 217 ++ .../Manager/Response/OriginateResponse.cs | 110 + .../Manager/ResponseEventHandler.cs | 91 + .../Asterisk.NET/Manager/ResponseEvents.cs | 55 + .../Asterisk.NET/Manager/ResponseHandler.cs | 60 + .../Asterisk.NET/Properties/AssemblyInfo.cs | 20 + Asterisk.2013/Asterisk.NET/Util/MD5Support.cs | 98 + .../Asterisk.NET/Util/ThreadClass.cs | 125 + Asterisk.2013/Asterisk.NET/Util/ThreadPool.cs | 118 + Asterisk.2013/Asterisk.NET/Util/ThreadTask.cs | 44 + Asterisk.2013/ChangeLog.txt | 69 + 248 files changed, 19520 insertions(+) create mode 100644 Asterisk.2013/Asterisk.2013.sln create mode 100644 Asterisk.2013/Asterisk.2013.vssscc create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj.vspscc create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/FormMain.Designer.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/FormMain.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/FormMain.resx create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Program.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Properties/AssemblyInfo.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.Designer.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.resx create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.Designer.cs create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.settings create mode 100644 Asterisk.2013/Asterisk.NET.WinForm/app.config create mode 100644 Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj create mode 100644 Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj.vspscc create mode 100644 Asterisk.2013/Asterisk.NET/Common.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIReader.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIRequest.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIScript.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AGIWriter.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/AGICommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/AnswerCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/ChannelStatusCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/ControlStreamFileCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelTreeCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseGetCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabasePutCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/ExecCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/GetDataCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/GetFullVariableCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/GetOptionCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/GetVariableCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/HangupCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/NoopCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveCharCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveTextCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/RecordFileCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayAlphaCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDateTimeCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDigitsCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayNumberCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayPhoneticCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SayTimeCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SendImageCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SendTextCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetAutoHangupCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetCallerIdCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetContextCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetExtensionCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOffCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOnCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetPriorityCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/SetVariableCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/StreamFileCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/TDDModeCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/VerboseCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Command/WaitForDigitCommand.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIException.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIHangupException.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGINetworkException.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidCommandSyntaxException.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidOrUnknownCommandException.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs create mode 100644 Asterisk.2013/Asterisk.NET/FastAGI/Script/AGINoAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Helper.cs create mode 100644 Asterisk.2013/Asterisk.NET/IO/ServerSocket.cs create mode 100644 Asterisk.2013/Asterisk.NET/IO/SocketConnection.cs create mode 100644 Asterisk.2013/Asterisk.NET/IParseSupport.cs create mode 100644 Asterisk.2013/Asterisk.NET/Logger.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/AbsoluteTimeoutAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/AgentCallbackLoginAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/AgentLogoffAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/AgentsAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ChallengeAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ChangeMonitorAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/CommandAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/DBGetAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/DBPutAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/EventsAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ExtensionStateAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/GetConfigAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/GetVarAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/HangupAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/LoginAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/LogoffAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/MailboxCountAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/MailboxStatusAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ManagerAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/MonitorAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/OriginateAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ParkedCallsAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/PingAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ProxyAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/QueueAddAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/QueuePauseAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/QueueRemoveAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/QueueStatusAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/RedirectAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/SIPPeersAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/SIPShowPeerAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/SetCDRUserFieldAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/SetVarAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/StatusAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/StopMonitorAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/UpdateConfigAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOffAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOnAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapDialOffhookAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapHangupAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapShowChannelsAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Action/ZapTransferAction.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/AsteriskVersion.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AGIExecEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentVariables.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractChannelEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractMeetmeEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractParkedCallEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AbstractQueueMemberEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLoginEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLogoffEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentCalledEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentConnectEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentDumpEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentLoginEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentLogoffEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentsCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AgentsEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AlarmClearEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AlarmEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/AsyncAGIEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/BridgeEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/CdrEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ChannelReloadEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ChannelUpdateEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ConnectEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ConnectionStateEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/DBGetResponseEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/DNDStateEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/DTMFEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/DialEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/DisconnectEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ExtensionStatusEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/FaxReceivedEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/HangupEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/HoldEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/HoldedCallEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/JabberEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/JitterBufStatsEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/JoinEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/LeaveEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/LinkEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/LogChannelEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ManagerEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MasqueradeEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeEndEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeJoinEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeLeaveEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeMuteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeStopTalkingEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkRequestEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkingEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MessageWaitingEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MobileStatusEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ModuleLoadReportEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStartEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStopEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/NewAccountCodeEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/NewCallerIdEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/NewChannelEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/NewExtenEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/NewStateEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/OriginateResponseEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/PRIEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallGiveUpEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallTimeOutEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallsCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/PeerEntryEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/PeerStatusEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/PeerlistCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueCallerAbandonEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueEntryEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberAddedEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPausedEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPenaltyEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberRemovedEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberStatusEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueParamsEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/QueueStatusCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RTCPReceivedEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RTCPSentEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RTPReceiverStatEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RTPSenderStatEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RegistryEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ReloadEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/RenameEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ResponseEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ShowDialPlanCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ShutdownEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/StatusCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/StatusEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/TransferEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/UnholdEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/UnknownEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/UnlinkEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/UnparkedCallEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/UserEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/VarSet.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsCompleteEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsEvent.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Exceptions/AuthenticationFailedException.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Exceptions/EventTimeoutException.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Exceptions/ManagerException.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Exceptions/TimeoutException.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/IResponseHandler.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/ManagerReader.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Originate.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/ChallengeResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/CommandResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/ExtensionStateResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/GetConfigResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/MailboxCountResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/MailboxStatusResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/ManagerError.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/ManagerResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/Response/OriginateResponse.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/ResponseEventHandler.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/ResponseEvents.cs create mode 100644 Asterisk.2013/Asterisk.NET/Manager/ResponseHandler.cs create mode 100644 Asterisk.2013/Asterisk.NET/Properties/AssemblyInfo.cs create mode 100644 Asterisk.2013/Asterisk.NET/Util/MD5Support.cs create mode 100644 Asterisk.2013/Asterisk.NET/Util/ThreadClass.cs create mode 100644 Asterisk.2013/Asterisk.NET/Util/ThreadPool.cs create mode 100644 Asterisk.2013/Asterisk.NET/Util/ThreadTask.cs create mode 100644 Asterisk.2013/ChangeLog.txt diff --git a/Asterisk.2013/Asterisk.2013.sln b/Asterisk.2013/Asterisk.2013.sln new file mode 100644 index 0000000..49cc63e --- /dev/null +++ b/Asterisk.2013/Asterisk.2013.sln @@ -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 diff --git a/Asterisk.2013/Asterisk.2013.vssscc b/Asterisk.2013/Asterisk.2013.vssscc new file mode 100644 index 0000000..794f014 --- /dev/null +++ b/Asterisk.2013/Asterisk.2013.vssscc @@ -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" +} diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj b/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj new file mode 100644 index 0000000..e135556 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj @@ -0,0 +1,127 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {03687626-613A-4E41-8F60-7C7839D6DD5D} + WinExe + Properties + Asterisk.NET.WinForm + Asterisk.NET.WinForm + v4.5 + + + + + 2.0 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + Form + + + FormMain.cs + + + + + Designer + FormMain.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {BC6E7DBA-C05A-45FE-A2A3-B1637CE16274} + Asterisk.NET + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + + + \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj.vspscc b/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Asterisk.NET.WinForm.csproj.vspscc @@ -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" +} diff --git a/Asterisk.2013/Asterisk.NET.WinForm/FormMain.Designer.cs b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.Designer.cs new file mode 100644 index 0000000..918378a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.Designer.cs @@ -0,0 +1,188 @@ +namespace Asterisk.NET.WinForm +{ + partial class FormMain + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + + } +} + diff --git a/Asterisk.2013/Asterisk.NET.WinForm/FormMain.cs b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.cs new file mode 100644 index 0000000..450a12d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.cs @@ -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; + } + } +} diff --git a/Asterisk.2013/Asterisk.NET.WinForm/FormMain.resx b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/FormMain.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Program.cs b/Asterisk.2013/Asterisk.NET.WinForm/Program.cs new file mode 100644 index 0000000..c46ec46 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace Asterisk.NET.WinForm +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormMain()); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Properties/AssemblyInfo.cs b/Asterisk.2013/Asterisk.NET.WinForm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ae7e807 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Properties/AssemblyInfo.cs @@ -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")] diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.Designer.cs b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.Designer.cs new file mode 100644 index 0000000..121d47b --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace Asterisk.NET.WinForm.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.resx b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.resx new file mode 100644 index 0000000..c40a448 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.Designer.cs b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5a7f5eb --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +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; + } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.settings b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.settings new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Asterisk.2013/Asterisk.NET.WinForm/app.config b/Asterisk.2013/Asterisk.NET.WinForm/app.config new file mode 100644 index 0000000..b7a7ef1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET.WinForm/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj new file mode 100644 index 0000000..f4c5e94 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj @@ -0,0 +1,324 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {BC6E7DBA-C05A-45FE-A2A3-B1637CE16274} + Library + Properties + Asterisk.NET + Asterisk.NET + v4.5 + + + + + 2.0 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + TRACE;DEBUG;LOGGER + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + + + + + + + \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj.vspscc b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Asterisk.NET.csproj.vspscc @@ -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" +} diff --git a/Asterisk.2013/Asterisk.NET/Common.cs b/Asterisk.2013/Asterisk.NET/Common.cs new file mode 100644 index 0000000..6df3c54 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Common.cs @@ -0,0 +1,55 @@ +using System; +using System.Text.RegularExpressions; +using System.Globalization; + +namespace Asterisk.NET +{ + public static class Common + { + #region Manager API Constants + + /// The hostname to use if none is provided. + public const string DEFAULT_HOSTNAME = "localhost"; + /// The port to use if none is provided. + public const int DEFAULT_PORT = 5038; + /// Line separator + 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 = '#'; + /// Variables delimiter + public static char[] VAR_DELIMITER = new char[] { '|' }; + public static IFormatProvider CultureInfoEn = new CultureInfo("en-US", false); + + #endregion + + #region AGI Constants + + /// The default AGI bind port. + public const int AGI_BIND_PORT = 4573; + /// The default AGI thread pool size. + public const int AGI_POOL_SIZE = 10; + /// The default AGI bind address. + 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 + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs new file mode 100644 index 0000000..721ae6a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIChannel.cs @@ -0,0 +1,44 @@ +namespace Asterisk.NET.FastAGI +{ + /// + /// Default implementation of the AGIChannel interface. + /// + 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; + } + + /// + /// Get last AGI Reply. + /// + 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; + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs new file mode 100644 index 0000000..fb5532b --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIConnectionHandler.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using System.Threading; + +namespace Asterisk.NET.FastAGI +{ + /// + /// An AGIConnectionHandler is created and run by the AGIServer whenever a new + /// socket connection from an Asterisk Server is received.
+ /// It reads the request using an AGIReader and runs the AGIScript configured to + /// handle this type of request. Finally it closes the socket connection. + ///
+ 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 + /// + /// Returns the AGIChannel associated with the current thread. + /// + /// the AGIChannel associated with the current thread or null if none is associated. + internal static AGIChannel Channel + { + get + { + return (AGIChannel) Thread.GetData(AGIConnectionHandler.channel); + } + } + #endregion + + #region AGIConnectionHandler(socket, mappingStrategy) + /// + /// Creates a new AGIConnectionHandler to handle the given socket connection. + /// + /// the socket connection to handle. + /// the strategy to use to determine which script to run. + 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 + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIReader.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReader.cs new file mode 100644 index 0000000..e9fbf44 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReader.cs @@ -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 lines = new List(); + 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 lines = new List(); + 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); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs new file mode 100644 index 0000000..2863394 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIReply.cs @@ -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 + { + /// + /// 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 (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 Match matcher; + private List lines; + private string firstLine; + /// The result, that is the part directly following the "result=" string. + private string result; + /// The status code. + private int status; + /// Additional attributes contained in this reply, for example endpos. + private Dictionary attributes; + /// The contents of the parenthesis. + private string extra; + /// In case of status == 520 (invalid command syntax) this attribute contains the synopsis of the command. + private string synopsis; + /// In case of status == 520 (invalid command syntax) this attribute contains the usage of the command. + 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 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 = 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 (string)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 = ((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() + /// + /// 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 + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIRequest.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIRequest.cs new file mode 100644 index 0000000..70ddf3c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIRequest.cs @@ -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 +{ + /// + /// Default implementation of the AGIRequest interface. + /// + public class AGIRequest + { + #region Variables +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + private string rawCallerId; + private Dictionary request; + + /// A map assigning the values of a parameter (an array of Strings) to the name of the parameter. + private Dictionary> 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) + /// + /// Creates a new AGIRequest. + /// + /// the first lines as received from Asterisk containing the environment. + public AGIRequest(List 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 + /// + /// Returns the full URL of the request in the form agi://host[:port][/script]. + /// + public string RequestURL + { + get { return (string) request["request"]; } + } + #endregion + + #region Channel + /// + /// Returns the name of the channel. + /// + /// the name of the channel. + public string Channel + { + get { return (string) request["channel"]; } + } + #endregion + + #region UniqueId + /// + /// Returns the unqiue id of the channel. + /// + /// the unqiue id of the channel. + public string UniqueId + { + get { return (string) request["uniqueid"]; } + } + #endregion + + #region Type + /// + /// Returns the type of the channel, for example "SIP". + /// + /// the type of the channel, for example "SIP". + public string Type + { + get { return (string) request["type"]; } + } + #endregion + + #region Language + /// + /// Returns the language, for example "en". + /// + 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 + /// + /// Returns the context in the dial plan from which the AGI script was called. + /// + public string Context + { + get { return (string) request["context"]; } + } + #endregion + + #region Extension + /// + /// Returns the extension in the dial plan from which the AGI script was called. + /// + public string Extension + { + get { return (string) request["extension"]; } + } + #endregion + + #region Priority + /// + /// Returns the priority in the dial plan from which the AGI script was called. + /// + public int Priority + { + get + { + if (request["priority"] != null) + { + return Int32.Parse((string) request["priority"]); + } + return -1; + } + + } + #endregion + + #region Enhanced + /// + /// Returns wheather this agi is passed audio (EAGI - Enhanced AGI).
+ /// Enhanced AGI is currently not supported on FastAGI.
+ /// true if this agi is passed audio, false otherwise. + ///
+ public bool Enhanced + { + get + { + if (request["enhanced"] != null && (string)request["enhanced"] == "1.0") + return true; + return false; + } + } + #endregion + + #region AccountCode + /// + /// Returns the account code set for the call. + /// + 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() + /// + /// Returns the name of the script to execute. + /// + 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 values; + values = ParameterValues(name); + if (values == null || values.Count == 0) + return null; + return values[0]; + } + #endregion + + #region ParameterValues(string name) + public List ParameterValues(string name) + { + if (ParameterMap().Count == 0) + return null; + + return parameterMap[name]; + } + #endregion + + #region ParameterMap() + public Dictionary> 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) + /// + /// Builds a map containing variable names as key (with the "agi_" prefix stripped) and the corresponding values.
+ /// Syntactically invalid and empty variables are skipped. + ///
+ /// the environment to transform. + /// a map with the variables set corresponding to the given environment. + private Dictionary buildMap(List lines) + { + int colonPosition; + string key; + string value; + + Dictionary map = new Dictionary(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) + /// + /// Parses the given parameter string and caches the result. + /// + /// the parameter string to parse + /// a Map made up of parameter names their values + private Dictionary> parseParameters(string parameters) + { + Dictionary> result = new Dictionary>(); + 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()); + result[name].Add(val); + } + return result; + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIScript.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIScript.cs new file mode 100644 index 0000000..38ab6b4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIScript.cs @@ -0,0 +1,817 @@ +using System; + +namespace Asterisk.NET.FastAGI +{ + /// + /// The BaseAGIScript provides some convinience methods to make it easier to + /// write custom AGIScripts.
+ /// Just extend it by your own AGIScripts. + ///
+ public abstract class AGIScript + { + #region Answer() + /// + /// Answers the channel. + /// + protected internal void Answer() + { + this.Channel.SendCommand(new Command.AnswerCommand()); + } + #endregion + + #region Hangup() + /// + /// Hangs the channel up. + /// + protected internal void Hangup() + { + this.Channel.SendCommand(new Command.HangupCommand()); + } + #endregion + + #region SetAutoHangup + /// + /// Cause the channel to automatically hangup at the given number of seconds in the future.
+ /// 0 disables the autohangup feature. + ///
+ protected internal void SetAutoHangup(int time) + { + this.Channel.SendCommand(new Command.SetAutoHangupCommand(time)); + } + #endregion + + #region SetCallerId + /// + /// Sets the caller id on the current channel.
+ /// The raw caller id to set, for example "John Doe<1234>". + ///
+ protected internal void SetCallerId(string callerId) + { + this.Channel.SendCommand(new Command.SetCallerIdCommand(callerId)); + } + #endregion + + #region PlayMusicOnHold() + /// + /// Plays music on hold from the default music on hold class. + /// + protected internal void PlayMusicOnHold() + { + this.Channel.SendCommand(new Command.SetMusicOnCommand()); + } + #endregion + + #region PlayMusicOnHold(string musicOnHoldClass) + /// + /// Plays music on hold from the given music on hold class. + /// + /// the music on hold class to play music from as configures in Asterisk's . + protected internal void PlayMusicOnHold(string musicOnHoldClass) + { + this.Channel.SendCommand(new Command.SetMusicOnCommand(musicOnHoldClass)); + } + #endregion + + #region StopMusicOnHold() + /// + /// Stops playing music on hold. + /// + protected internal void StopMusicOnHold() + { + this.Channel.SendCommand(new Command.SetMusicOffCommand()); + } + #endregion + + #region GetChannelStatus + /// + /// Returns the status of the channel.
+ /// Return values: + ///
    + ///
  • 0 Channel is down and available + ///
  • 1 Channel is down, but reserved + ///
  • 2 Channel is off hook + ///
  • 3 Digits (or equivalent) have been dialed + ///
  • 4 Line is ringing + ///
  • 5 Remote end is ringing + ///
  • 6 Line is up + ///
  • 7 Line is busy + ///
+ /// + ///
+ /// the status of the channel. + /// + protected internal int GetChannelStatus() + { + AGIChannel channel = this.Channel; + channel.SendCommand(new Command.ChannelStatusCommand()); + return channel.LastReply.ResultCode; + } + #endregion + + #region GetData(string file) + /// + /// 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. + /// + /// the name of the file to play + /// a String containing the DTMF the user entered + 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) + /// + /// 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. + /// + /// the name of the file to play + /// the timeout in milliseconds to wait for user input.
+ /// 0 means standard timeout value, -1 means "ludicrous time" + /// (essentially never times out). + /// a String containing the DTMF the user entered + 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) + /// + /// 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. + /// + /// the name of the file to play + /// the timeout in milliseconds to wait for user input.
+ /// 0 means standard timeout value, -1 means "ludicrous time" + /// (essentially never times out). + /// the maximum number of digits the user is allowed to enter + /// a String containing the DTMF the user entered + 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) + /// + /// 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. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that the user is expected to press. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// 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. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that the user is expected to press. + /// the timeout in seconds to wait if none of the defined esacpe digits was presses while streaming. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Executes the given command. + /// + /// the name of the application to execute, for example "Dial". + /// the return code of the application of -2 if the application was not found. + 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) + /// + /// Executes the given command. + /// + /// the name of the application to execute, for example "Dial". + /// the parameters to pass to the application, for example "SIP/123". + /// the return code of the application of -2 if the application was not found. + 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 + /// + /// Sets the context for continuation upon exiting the application. + /// + protected internal void SetContext(string context) + { + this.Channel.SendCommand(new Command.SetContextCommand(context)); + } + #endregion + + #region SetExtension + /// + /// Sets the extension for continuation upon exiting the application. + /// + protected internal void SetExtension(string extension) + { + this.Channel.SendCommand(new Command.SetExtensionCommand(extension)); + } + #endregion + + #region SetPriority(int priority) + /// + /// Sets the priority for continuation upon exiting the application. + /// + protected internal void SetPriority(int priority) + { + this.Channel.SendCommand(new Command.SetPriorityCommand(priority)); + } + #endregion + + #region SetPriority(string label priority) + /// + /// Sets the label for continuation upon exiting the application. + /// + protected internal void SetPriority(string label) + { + this.Channel.SendCommand(new Command.SetPriorityCommand(label)); + } + #endregion + + #region StreamFile(string file) + /// + /// Plays the given file. + /// + /// name of the file to play. + protected internal void StreamFile(string file) + { + this.Channel.SendCommand(new Command.StreamFileCommand(file)); + } + #endregion + + #region StreamFile(string file, string escapeDigits) + /// + /// Plays the given file and allows the user to escape by pressing one of the given digit. + /// + /// name of the file to play. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Says the given digit string. + /// + /// the digit string to say. + protected internal void SayDigits(string digits) + { + this.Channel.SendCommand(new Command.SayDigitsCommand(digits)); + } + #endregion + + #region SayDigits(string digits, string escapeDigits) + /// + /// Says the given number, returning early if any of the given DTMF number + /// are received on the channel. + /// + /// the digit string to say. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Says the given number. + /// + /// the number to say. + protected internal void SayNumber(string number) + { + this.Channel.SendCommand(new Command.SayNumberCommand(number)); + } + #endregion + + #region SayNumber(string number, string escapeDigits) + /// + /// Says the given number, returning early if any of the given DTMF number + /// are received on the channel. + /// + /// the number to say. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Says the given character string with phonetics. + /// + /// the text to say. + protected internal void SayPhonetic(string text) + { + this.Channel.SendCommand(new Command.SayPhoneticCommand(text)); + } + #endregion + + #region SayPhonetic(string text, string escapeDigits) + /// + /// Says the given character string with phonetics, returning early if any of + /// the given DTMF number are received on the channel. + /// + /// the text to say. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Says the given character string. + /// + /// the text to say. + protected internal void SayAlpha(string text) + { + this.Channel.SendCommand(new Command.SayAlphaCommand(text)); + } + #endregion + + #region SayAlpha(string text, string escapeDigits) + /// + /// Says the given character string, returning early if any of the given DTMF + /// number are received on the channel. + /// + /// the text to say. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Says the given time. + /// + /// the time to say in seconds since 00:00:00 on January 1, 1970. + protected internal void SayTime(long time) + { + this.Channel.SendCommand(new Command.SayTimeCommand(time)); + } + #endregion + + #region SayTime(long time, string escapeDigits) + /// + /// Says the given time, returning early if any of the given DTMF number are + /// received on the channel. + /// + /// the time to say in seconds since 00:00:00 on January 1, 1970. + /// a String containing the DTMF digits that allow the user to escape. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Returns the value of the given channel variable. + /// + /// the name of the variable to retrieve. + /// the value of the given variable or null if not set. + 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) + /// + /// Sets the value of the given channel variable to a new value. + /// + /// the name of the variable to retrieve. + /// the new value to set. + protected internal void SetVariable(string name, string val) + { + this.Channel.SendCommand(new Command.SetVariableCommand(name, val)); + } + #endregion + + #region WaitForDigit(int timeout) + /// + /// Waits up to 'timeout' milliseconds to receive a DTMF digit. + /// + /// timeout the milliseconds to wait for the channel to receive a DTMF digit, -1 will wait forever. + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Returns the value of the current channel variable, unlike getVariable() + /// this method understands complex variable names and builtin variables.
+ /// Available since Asterisk 1.2. + ///
+ /// the name of the variable to retrieve. + /// the value of the given variable or null if not et. + 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) + /// + /// Returns the value of the given channel variable.
+ /// Available since Asterisk 1.2. + ///
+ /// the name of the variable to retrieve. + /// the name of the channel. + /// the value of the given variable or null if not set. + 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(...) + /// + /// Says the given time.
+ /// Available since Asterisk 1.2. + ///
+ /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + protected internal void SayDateTime(long time) + { + this.Channel.SendCommand(new Command.SayDateTimeCommand(time)); + } + + /// + /// Says the given time and allows interruption by one of the given escape digits.
+ /// Available since Asterisk 1.2. + ///
+ /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + /// the DTMF digit pressed or 0x0 if none was pressed. + protected internal char SayDateTime(long time, string escapeDigits) + { + AGIChannel channel = this.Channel; + channel.SendCommand(new Command.SayDateTimeCommand(time, escapeDigits)); + return channel.LastReply.ResultCodeAsChar; + } + + /// + /// Says the given time in the given format and allows interruption by one of the given escape digits.
+ /// Available since Asterisk 1.2. + ///
+ /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + /// the format the time should be said in + /// the DTMF digit pressed or 0x0 if none was pressed. + 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; + } + + /// + /// Says the given time in the given format and timezone and allows interruption by one of the given escape digits.
+ /// Available since Asterisk 1.2. + ///
+ /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + /// the format the time should be said in + /// the timezone to use when saying the time, for example "UTC" or "Europe/Berlin". + /// the DTMF digit pressed or 0x0 if none was pressed. + 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) + /// + /// Retrieves an entry in the Asterisk database for a given family and key. + /// + /// the family of the entry to retrieve. + /// key the key of the entry to retrieve. + /// the value of the given family and key or null if there is no such value. + 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) + /// + /// Adds or updates an entry in the Asterisk database for a given family, key and value. + /// + /// the family of the entry to add or update. + /// the key of the entry to add or update. + /// the new value of the entry. + 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) + /// + /// Deletes an entry in the Asterisk database for a given family and key. + /// + /// the family of the entry to delete. + /// the key of the entry to delete. + protected internal void DatabaseDel(string family, string key) + { + this.Channel.SendCommand(new Command.DatabaseDelCommand(family, key)); + } + #endregion + + #region DatabaseDelTree(String family) + /// + /// Deletes a whole family of entries in the Asterisk database. + /// + /// the family to delete. + protected internal void DatabaseDelTree(string family) + { + this.Channel.SendCommand(new Command.DatabaseDelTreeCommand(family)); + } + #endregion + + #region DatabaseDelTree(string family, string keytree) + /// + /// Deletes all entries of a given family in the Asterisk database that have a key that starts with a given prefix. + /// + /// the family of the entries to delete. + /// the prefix of the keys of the entries to delete. + protected internal void DatabaseDelTree(string family, string keytree) + { + this.Channel.SendCommand(new Command.DatabaseDelTreeCommand(family, keytree)); + } + #endregion + + #region Verbose(string message, int level) + /// + /// Sends a message to the Asterisk console via the verbose message system. + /// + /// the message to send + /// the verbosity level to use. Must be in [1..4] + public void Verbose(string message, int level) + { + this.Channel.SendCommand(new Command.VerboseCommand(message, level)); + } + #endregion + + #region RecordFile(...) + /// + /// Record to a file until a given dtmf digit in the sequence is received.
+ /// Returns -1 on hangup or error.
+ /// 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. + ///
+ /// the name of the file to stream, must not include extension. + /// the format of the file to be recorded, for example "wav". + /// contains the digits that allow the user to end recording. + /// the maximum record time in milliseconds, or -1 for no timeout. + /// result code + 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; + } + + /// + /// Record to a file until a given dtmf digit in the sequence is received.
+ /// Returns -1 on hangup or error.
+ /// 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. + ///
+ /// the name of the file to stream, must not include extension. + /// the format of the file to be recorded, for example "wav". + /// contains the digits that allow the user to end recording. + /// the maximum record time in milliseconds, or -1 for no timeout. + /// the offset samples to skip. + /// true if a beep should be played before recording. + /// The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout. + /// result code + 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(...) + /// + /// Plays the given file, allowing playback to be interrupted by the given + /// digits, if any, and allows the listner to control the stream.
+ /// If offset is provided then the audio will seek to sample offset before play + /// starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// the name of the file to stream, must not include extension. + /// result code + protected internal int ControlStreamFile(string file) + { + AGIChannel channel = this.Channel; + channel.SendCommand(new Command.ControlStreamFileCommand(file)); + return channel.LastReply.ResultCode; + } + /// + /// Plays the given file, allowing playback to be interrupted by the given + /// digits, if any, and allows the listner to control the stream.
+ /// If offset is provided then the audio will seek to sample offset before play + /// starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + /// result code + protected internal int ControlStreamFile(string file, string escapeDigits) + { + AGIChannel channel = this.Channel; + channel.SendCommand(new Command.ControlStreamFileCommand(file, escapeDigits)); + return channel.LastReply.ResultCode; + } + /// + /// Plays the given file, allowing playback to be interrupted by the given + /// digits, if any, and allows the listner to control the stream.
+ /// If offset is provided then the audio will seek to sample offset before play + /// starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + /// the offset samples to skip before streaming. + /// result code + 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; + } + /// + /// Plays the given file, allowing playback to be interrupted by the given + /// digits, if any, and allows the listner to control the stream.
+ /// If offset is provided then the audio will seek to sample offset before play + /// starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + /// the offset samples to skip before streaming. + /// the digit for fast forward. + /// the digit for rewind. + /// the digit for pause and unpause. + /// result code + 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) + /// + /// Sends the given command to the channel attached to the current thread. + /// + /// the command to send to Asterisk + /// the reply received from Asterisk + /// AGIException if the command could not be processed properly + 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 + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AGIWriter.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AGIWriter.cs new file mode 100644 index 0000000..3557ab6 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AGIWriter.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +namespace Asterisk.NET.FastAGI +{ + /// + /// Default implementation of the AGIWriter interface. + /// + 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); + } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs b/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs new file mode 100644 index 0000000..acc9f92 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs @@ -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; + + /// The port to listen on. + private int port; + /// The address to listen on. + private string address; + /// The thread pool that contains the worker threads to process incoming requests. + private Util.ThreadPool pool; + /// The number of worker threads in the thread pool. This equals the maximum number of concurrent requests this AGIServer can serve. + private int poolSize; + /// True while this server is shut down. + private bool stopped; + /// + /// The strategy to use for bind AGIRequests to AGIScripts that serve them. + /// + private MappingStrategy mappingStrategy; + private Encoding socketEncoding = Encoding.ASCII; + #endregion + + #region PoolSize + /// + /// Sets the number of worker threads in the thread pool.
+ /// This equals the maximum number of concurrent requests this AGIServer can serve.
+ /// The default pool size is 10. + ///
+ public int PoolSize + { + set { this.poolSize = value; } + } + #endregion + + #region BindPort + /// + /// Sets the TCP port to listen on for new connections.
+ /// The default bind port is 4573. + ///
+ public int BindPort + { + set + { + this.port = value; + } + } + #endregion + + #region MappingStrategy + /// + /// Sets the strategy to use for mapping AGIRequests to AGIScripts that serve them.
+ /// The default mapping is a 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() + /// + /// Creates a new AsteriskFastAGI. + /// + 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() + /// + /// Creates a new AsteriskFastAGI. + /// + 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) + /// + /// Creates a new AsteriskFastAGI. + /// + /// The port to listen on. + /// The number of worker threads in the thread pool. + /// This equals the maximum number of concurrent requests this AGIServer can serve. + 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) + /// + /// Creates a new AsteriskFastAGI. + /// + /// The address to listen on. + /// The port to listen on. + /// The number of worker threads in the thread pool. + /// This equals the maximum number of concurrent requests this AGIServer can serve. + 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 + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/AGICommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/AGICommand.cs new file mode 100644 index 0000000..277a692 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/AGICommand.cs @@ -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); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/AnswerCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/AnswerCommand.cs new file mode 100644 index 0000000..0f94768 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/AnswerCommand.cs @@ -0,0 +1,14 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + public class AnswerCommand : AGICommand + { + public AnswerCommand() + { + } + public override string BuildCommand() + { + return "ANSWER"; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/ChannelStatusCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ChannelStatusCommand.cs new file mode 100644 index 0000000..a731fb3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ChannelStatusCommand.cs @@ -0,0 +1,55 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Returns the status of the specified channel. + /// If no channel name is given the returns the status of the current channel.
+ /// Return values: + ///
    + ///
  • 0 Channel is down and available + ///
  • 1 Channel is down, but reserved + ///
  • 2 Channel is off hook + ///
  • 3 Digits (or equivalent) have been dialed + ///
  • 4 Line is ringing + ///
  • 5 Remote end is ringing + ///
  • 6 Line is up + ///
  • 7 Line is busy + ///
+ ///
+ public class ChannelStatusCommand : AGICommand + { + private string channel; + public string Channel + { + get + { + return channel; + } + + set + { + this.channel = value; + } + + } + + public ChannelStatusCommand() + { + this.channel = null; + } + + /// + /// Creates a new ChannelStatusCommand that queries the given channel. + /// + /// the name of the channel to query. + public ChannelStatusCommand(string channel) + { + this.channel = channel; + } + + public override string BuildCommand() + { + return "CHANNEL STATUS" + (channel == null ? "" : " " + EscapeAndQuote(channel)); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/ControlStreamFileCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ControlStreamFileCommand.cs new file mode 100644 index 0000000..3974a48 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ControlStreamFileCommand.cs @@ -0,0 +1,204 @@ +using System; +using System.Text; + +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Plays the given file, allowing playback to be interrupted by the given + /// digits, if any, and allows the listner to control the stream.
+ /// If offset is provided then the audio will seek to sample offset before play + /// starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename.
+ /// Available since Asterisk 1.2 + ///
+ public class ControlStreamFileCommand : AGICommand + { + /// The name of the file to stream. + private string file; + /// When one of these digits is pressed while streaming the command returns. + private string escapeDigits; + /// The offset samples to skip before streaming. + private int offset; + private string forwardDigit; + private string rewindDigit; + private string pauseDigit; + + #region File + /// + /// Get/Set the name of the file to stream. + /// + public string File + { + get { return file; } + set { this.file = value; } + } + #endregion + #region EscapeDigits + /// + /// Get/Set the digits that allow the user to interrupt this command or null for none. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + #endregion + #region Offset + /// + /// Get/Set the offset samples to skip before streaming. + /// + public int Offset + { + get { return offset; } + set { this.offset = value; } + } + #endregion + #region ForwardDigit + /// + /// Get the digit for fast forward. + /// + public string ForwardDigit + { + get { return forwardDigit; } + } + #endregion + #region RewindDigit + /// + /// Get the digit for rewind. + /// + public string RewindDigit + { + get { return rewindDigit; } + } + #endregion + #region PauseDigit + /// + /// Get the digit for pause and unpause. + /// + public string PauseDigit + { + get { return pauseDigit; } + } + #endregion + + #region ControlStreamFileCommand(string file) + /// + /// Creates a new ControlStreamFileCommand, streaming from the beginning. It + /// uses the default digit "#" for forward and "*" for rewind and does not + /// support pausing. + /// + /// the name of the file to stream, must not include extension. + public ControlStreamFileCommand(string file) + { + this.file = file; + this.escapeDigits = null; + this.offset = - 1; + } + #endregion + #region ControlStreamFileCommand(string file, string escapeDigits) + /// + /// Creates a new ControlStreamFileCommand, streaming from the beginning. It + /// uses the default digit "#" for forward and "*" for rewind and does not + /// support pausing. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + public ControlStreamFileCommand(string file, string escapeDigits) + { + this.file = file; + this.escapeDigits = escapeDigits; + this.offset = - 1; + } + #endregion + #region ControlStreamFileCommand(string file, string escapeDigits, int offset) + /// + /// Creates a new ControlStreamFileCommand, streaming from the given offset. + /// It uses the default digit "#" for forward and "*" for rewind and does not + /// support pausing. + /// + /// the name of the file to stream, must not include extension. + /// + /// contains the digits that allow the user to interrupt this command. + /// Maybe null if you don't want the user to interrupt. + /// + /// the offset samples to skip before streaming. + 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) + /// + /// Creates a new ControlStreamFileCommand, streaming from the given offset. + /// It uses the default digit "#" for forward and "*" for rewind and does not + /// support pausing. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. Maybe null if you don't want the user to interrupt. + /// the offset samples to skip before streaming. + /// the digit for fast forward. + /// the digit for rewind. + /// the digit for pause and unpause. + 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) + /// + /// Sets the control digits for fast forward and rewind. + /// + /// the digit for fast forward. + /// the digit for rewind. + public void ControlDigits(string forwardDigit, string rewindDigit) + { + this.forwardDigit = forwardDigit; + this.rewindDigit = rewindDigit; + } + #endregion + #region ControlDigits(string forwardDigit, string rewindDigit, string pauseDigit) + /// + /// Sets the control digits for fast forward, rewind and pause. + /// + /// the digit for fast forward. + /// the digit for rewind. + /// the digit for pause and unpause. + 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 + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelCommand.cs new file mode 100644 index 0000000..ad087d9 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelCommand.cs @@ -0,0 +1,59 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Deletes a family or specific keytree within a family in the Asterisk database.
+ /// Returns 1 if successful, 0 otherwise. + ///
+ public class DatabaseDelCommand : AGICommand + { + /// The family (or family of the keytree) to delete. + private string family; + /// The keyTree to delete. + private string keyTree; + + /// + /// Get/Set the family (or family of the keytree) to delete. + /// + public string Family + { + get { return family; } + set { this.family = value; } + } + + /// + /// Get/Set the the keytree to delete. + /// + public string KeyTree + { + get { return keyTree; } + set { this.keyTree = value; } + } + + /// + /// Creates a new DatabaseDelCommand to delete a family. + /// + /// the family to delete. + public DatabaseDelCommand(string family) + { + this.family = family; + this.keyTree = null; + } + + /// + /// Creates a new DatabaseDelCommand to delete a keytree. + /// + /// the family of the keytree to delete. + /// the keytree to delete. + 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)); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelTreeCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelTreeCommand.cs new file mode 100644 index 0000000..3a20f0e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseDelTreeCommand.cs @@ -0,0 +1,64 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Deletes an entry in the Asterisk database for a given family and key.
+ /// Returns 1 if successful, 0 otherwise. + ///
+ public class DatabaseDelTreeCommand : AGICommand + { + /// The family of the key to delete. + private string family; + /// The key to delete. + private string keyTree; + + /// + /// Get/Set the family of the key to delete. + /// + public string Family + { + get { return family; } + set { this.family = value; } + } + /// + /// Get/Set the the key to delete. + /// + public string KeyTree + { + get { return keyTree; } + set { this.keyTree = value; } + } + /// + /// Creates a new DatabaseDelCommand. + /// + /// the family of the key to delete. + /// the key to delete. + public DatabaseDelTreeCommand(string family) + { + this.family = family; + } + /// + /// Creates a new DatabaseDelCommand. + /// + /// the family of the key to delete. + /// the key to delete. + 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"); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseGetCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseGetCommand.cs new file mode 100644 index 0000000..a6a892d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabaseGetCommand.cs @@ -0,0 +1,50 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Retrieves an entry in the Asterisk database for a given family and key.
+ /// Returns 0 if is not set. Returns 1 if the variable is set and returns the + /// value in parenthesis.
+ /// Example return code: 200 result=1 (testvariable) + ///
+ public class DatabaseGetCommand : AGICommand + { + /// The family of the key to retrieve. + private string family; + /// The key to retrieve. + private string varKey; + + /// + /// Get/Set + /// + public string Family + { + get { return family; } + set { this.family = value; } + } + /// + /// Get/Set the the key to retrieve. + /// + public string Key + { + get { return varKey; } + set { this.varKey = value; } + } + + /// + /// Creates a new DatabaseGetCommand. + /// + /// the family of the key to retrieve. + /// the key to retrieve. + public DatabaseGetCommand(string family, string key) + { + this.family = family; + this.varKey = key; + } + + public override string BuildCommand() + { + return "DATABASE GET " + EscapeAndQuote(family) + " " + EscapeAndQuote(varKey); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabasePutCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabasePutCommand.cs new file mode 100644 index 0000000..d1c2408 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/DatabasePutCommand.cs @@ -0,0 +1,60 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Adds or updates an entry in the Asterisk database for a given family, key, and value.
+ /// Returns 1 if successful, 0 otherwise. + ///
+ public class DatabasePutCommand : AGICommand + { + /// The family of the key to set. + private string family; + /// The key to set. + private string varKey; + /// The value to set. + private string varValue; + + /// + /// Get/Set the family of the key to set. + /// + public string Family + { + get { return family; } + set { this.family = value; } + } + /// + /// Get/Set the the key to set. + /// + public string Key + { + get { return varKey; } + set { this.varKey = value; } + } + /// + /// Get/Set the value to set. + /// + public string Value + { + get { return varValue; } + set { this.varValue = value; } + } + + /// + /// Creates a new DatabasePutCommand. + /// + /// the family of the key to set. + /// the key to set. + /// the value to set. + 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); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/ExecCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ExecCommand.cs new file mode 100644 index 0000000..a9977e4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ExecCommand.cs @@ -0,0 +1,57 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Executes an application with the given options.
+ /// Returns whatever the application returns, or -2 if the application was not found. + ///
+ public class ExecCommand : AGICommand + { + /// The name of the application to execute. + private string application; + /// The options to pass to the application. + private string options; + + /// + /// Get/Set the name of the application to execute. + /// + public string Application + { + get { return application; } + set { this.application = value; } + } + /// + /// Get/Set the options to pass to the application. + /// + public string Options + { + get { return options; } + set { this.options = value; } + } + + /// + /// Creates a new ExecCommand. + /// + /// the name of the application to execute. + public ExecCommand(string application) + { + this.application = application; + } + + /// + /// Creates a new ExecCommand. + /// + /// the name of the application to execute. + /// the options to pass to the application. + public ExecCommand(string application, string options) + { + this.application = application; + this.options = options; + } + + public override string BuildCommand() + { + return "EXEC " + EscapeAndQuote(application) + " " + EscapeAndQuote(options); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetDataCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetDataCommand.cs new file mode 100644 index 0000000..fb302e4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetDataCommand.cs @@ -0,0 +1,109 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Stream the given file, and recieve DTMF data. The user may interrupt the streaming by starting to enter digits.
+ /// Returns the digits recieved from the channel at the other end.
+ /// Input ends when the timeout is reached, the maximum number of digits is read or the user presses #. + ///
+ public class GetDataCommand : AGICommand + { + /// The name of the file to stream. + private string file; + /// The timeout in milliseconds to wait for data.
+ /// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out). + ///
+ private long timeout; + + /// The maximum number of digits to read.
+ /// Must be in [1..1024]. + ///
+ private int maxDigits; + + /// + /// Get/Set the name of the file to stream. Must not include extension. + /// + public string File + { + get { return file; } + set { this.file = value; } + } + /// + /// Get/Set the timeout in milliseconds to wait for data. 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out). + /// + public long Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + /// + /// Get/Set the maximum number of digits to read. The maximum number of digits to read. Must be in [1..1024]. + /// + /// IllegalArgumentException if maxDigits is not in [1..1024] + public int MaxDigits + { + get { return maxDigits; } + set + { + if (value < 1 || value > 1024) + throw new ArgumentException("maxDigits must be in [1..1024]"); + this.maxDigits = value; + } + } + + /// + /// Creates a new GetDataCommand with default timeout and maxDigits set to 1024. + /// + /// the name of the file to stream, must not include extension. + public GetDataCommand(string file) + { + this.file = file; + this.timeout = Common.AGI_DEFAULT_TIMEOUT; + this.maxDigits = Common.AGI_DEFAULT_MAX_DIGITS; + } + + /// + /// Creates a new GetDataCommand with the given timeout and maxDigits set to 1024. + /// + /// the name of the file to stream, must not include extension. + /// the timeout in milliseconds to wait for data.
+ /// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out). + /// + public GetDataCommand(string file, long timeout) + { + this.file = file; + this.timeout = timeout; + this.maxDigits = Common.AGI_DEFAULT_MAX_DIGITS; + } + + /// + /// Creates a new GetDataCommand with the given timeout and maxDigits. + /// + /// the name of the file to stream, must not include extension. + /// the timeout in milliseconds to wait for data.
+ /// 0 means standard timeout value, -1 means "ludicrous time" (essentially never times out). + /// + /// the maximum number of digits to read.
+ /// Must be in [1..1024]. + /// + /// IllegalArgumentException if maxDigits is not in [1..1024] + 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; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetFullVariableCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetFullVariableCommand.cs new file mode 100644 index 0000000..14fc262 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetFullVariableCommand.cs @@ -0,0 +1,73 @@ +using System; +using System.Text; + +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Returns the value of the given channel varible and understands complex + /// variable names and builtin variables, unlike the GetVariableCommand.
+ /// You can also use this command to use custom Asterisk functions. Syntax is "func(args)".
+ /// 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.
+ /// Example return code: 200 result=1 (testvariable) + /// Available since Asterisk 1.2 + ///
+ public class GetFullVariableCommand : AGICommand + { + private string varName; + private string channel; + + /// + /// Creates a new GetFullVariableCommand. + /// + /// the name of the variable to retrieve. + public GetFullVariableCommand(string variable) + { + this.varName = variable; + } + + /// + /// Creates a new GetFullVariableCommand. + /// + /// the name of the variable to retrieve. + /// the name of the channel. + public GetFullVariableCommand(string variable, string channel) + { + this.varName = variable; + this.channel = channel; + } + + /// + /// Get/Set the name of the variable to retrieve. + /// + public string Variable + { + get { return varName; } + set { this.varName = value;} + } + + /// + /// Get/Set the name of the channel. + /// + 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(); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetOptionCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetOptionCommand.cs new file mode 100644 index 0000000..c68d387 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetOptionCommand.cs @@ -0,0 +1,79 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// 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.
+ /// 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. + ///
+ /// Remember, the file extension must not be included in the filename. + ///
+ /// + public class GetOptionCommand : AGICommand + { + /// The name of the file to stream. + private string file; + /// When one of these digits is pressed while streaming the command returns. + private string escapeDigits; + /// The timeout in seconds. + private int timeout; + + /// + /// Get/Set the name of the file to stream. + /// + public string File + { + get { return file; } + set { this.file = value; } + } + /// + /// Get/Set the digits that the user is expected to press. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + /// + /// Get/Set the timeout to wait if none of the defined esacpe digits was presses while streaming. + /// + public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + + /// + /// Creates a new GetOptionCommand with a default timeout of 5 seconds. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that the user is expected to press. + public GetOptionCommand(string file, string escapeDigits) + { + this.file = file; + this.escapeDigits = escapeDigits; + this.timeout = - 1; + } + + /// + /// Creates a new GetOptionCommand with the given timeout. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that the user is expected to press. + /// the timeout in seconds to wait if none of the defined esacpe digits was presses while streaming. + 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); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetVariableCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetVariableCommand.cs new file mode 100644 index 0000000..df82b99 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/GetVariableCommand.cs @@ -0,0 +1,38 @@ +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Returns the value of the given channel varible.
+ /// Since Asterisk 1.2 you can also use this command to use custom Asterisk functions. Syntax is "func(args)".
+ /// Returns 0 if the variable is not set. Returns 1 if the variable is set and returns the variable in parenthesis.
+ /// Example return code: 200 result=1 (testvariable) + ///
+ public class GetVariableCommand : AGICommand + { + /// The name of the variable to retrieve. + private string varName; + + /// + /// Get/Set the name of the variable to retrieve.
+ /// Since Asterisk 1.2 you can also use custom dialplan functions (like "func(args)") as variable. + ///
+ public string Variable + { + get { return varName; } + set { this.varName = value; } + } + + /// + /// Creates a new GetVariableCommand. + /// + /// the name of the variable to retrieve. + public GetVariableCommand(string variable) + { + this.varName = variable; + } + + public override string BuildCommand() + { + return "GET VARIABLE " + EscapeAndQuote(varName); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/HangupCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/HangupCommand.cs new file mode 100644 index 0000000..e1a921c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/HangupCommand.cs @@ -0,0 +1,47 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Hangs up the specified channel. If no channel name is given, hangs up the current channel. + /// + public class HangupCommand : AGICommand + { + + /// + /// The name of the channel to hangup or null for the current channel. + /// + private string channel; + + /// + /// Returns the name of the channel to hangup. + /// + /// the name of the channel to hangup or null for the current channel. + /// Sets the name of the channel to hangup. + /// the name of the channel to hangup or null for the current channel. + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + + /// Creates a new HangupCommand that hangs up the current channel. + public HangupCommand() + { + this.channel = null; + } + + /// + /// Creates a new HangupCommand that hangs up the given channel. + /// + /// the name of the channel to hangup. + public HangupCommand(string channel) + { + this.channel = channel; + } + + public override string BuildCommand() + { + return "HANGUP" + (channel == null ? "" : " " + EscapeAndQuote(channel)); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/NoopCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/NoopCommand.cs new file mode 100644 index 0000000..8543df7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/NoopCommand.cs @@ -0,0 +1,20 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Does nothing. + /// + public class NoopCommand : AGICommand + { + /// + /// Creates a new NoopCommand. + /// + public NoopCommand() + { + } + public override string BuildCommand() + { + return "NOOP"; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveCharCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveCharCommand.cs new file mode 100644 index 0000000..831c862 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveCharCommand.cs @@ -0,0 +1,47 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Receives a character of text on a channel.
+ /// Specify timeout to be the maximum time to wait for input in milliseconds, or 0 for infinite.
+ /// Most channels do not support the reception of text.
+ /// 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. + ///
+ public class ReceiveCharCommand : AGICommand + { + /// The milliseconds to wait for the channel to receive a character. + private int timeout; + + /// + /// Get/Set the milliseconds to wait for the channel to receive a character. + /// + public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + + /// + /// Creates a new ReceiveCharCommand with a default timeout of 0 meaning to wait for ever. + /// + public ReceiveCharCommand() + { + this.timeout = 0; + } + + /// + /// Creates a new ReceiveCharCommand. + /// + /// the milliseconds to wait for the channel to receive a character. + public ReceiveCharCommand(int timeout) + { + this.timeout = timeout; + } + + public override string BuildCommand() + { + return "RECEIVE CHAR " + timeout; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveTextCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveTextCommand.cs new file mode 100644 index 0000000..8f13247 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/ReceiveTextCommand.cs @@ -0,0 +1,49 @@ +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Receives a string of text on a channel.
+ /// Specify timeout to be the maximum time to wait for input in milliseconds, or + /// 0 for infinite.
+ /// Most channels do not support the reception of text.
+ /// Returns -1 for failure or 1 for success, and the string in parentheses.
+ /// Available since Asterisk 1.2. + ///
+ public class ReceiveTextCommand : AGICommand + { + /// + /// The milliseconds to wait for the channel to receive a character. + /// + private int timeout; + + /// + /// Creates a new ReceiveTextCommand with a default timeout of 0 meaning to wait for ever. + /// + public ReceiveTextCommand() + { + this.timeout = 0; + } + + /// + /// Creates a new ReceiveTextCommand. + /// the milliseconds to wait for the channel to receive the text. + /// + public ReceiveTextCommand(int timeout) + { + this.timeout = timeout; + } + + /// + /// Get/Set the milliseconds to wait for the channel to receive the text. + /// + public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + + public override string BuildCommand() + { + return "RECEIVE TEXT " + timeout; + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/RecordFileCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/RecordFileCommand.cs new file mode 100644 index 0000000..901a74e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/RecordFileCommand.cs @@ -0,0 +1,143 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Record to a file until a given dtmf digit in the sequence is received.
+ /// Returns -1 on hangup or error.
+ /// 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. + ///
+ public class RecordFileCommand : AGICommand + { + #region Variables + /// The name of the file to record. + private string file; + /// The format of the file to be recorded, for example "wav". + private string format; + /// The these digits a user can press to end the recording. + private string escapeDigits; + /// The maximum record time in milliseconds, or -1 for no timeout. + private int timeout; + /// The offset samples to skip. + private int offset; + /// Wheather a beep should be played before recording. + private bool beep; + /// The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout. + private int maxSilence; + #endregion + + #region File + /// + /// Get/Set the name of the file to stream. + /// + public string File + { + get { return file; } + set { this.file = value; } + } + #endregion + #region Format + /// + /// Get/Set the format of the file to be recorded, for example "wav". + /// + public string Format + { + get { return format; } + set { this.format = value; } + } + #endregion + #region EscapeDigits + /// + /// Get/Set the digits that allow the user to end recording. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + #endregion + #region Timeout + /// + /// Get/Set the maximum record time in milliseconds, or -1 for no timeout. + /// + public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + #endregion + #region Offset + /// + /// Get/Set the offset samples to skip. + /// + public int Offset + { + get { return offset; } + set { this.offset = value; } + } + #endregion + #region Beep + /// + /// Get/Set true if a beep should be played before recording. false if not. + /// + public bool Beep + { + get { return beep; } + set { this.beep = value; } + } + #endregion + + #region Constructor - RecordFileCommand(string file, string format, string escapeDigits, int timeout) + /// + /// Creates a new RecordFileCommand. + /// + /// the name of the file to stream, must not include extension. + /// the format of the file to be recorded, for example "wav". + /// contains the digits that allow the user to end recording. + /// the maximum record time in milliseconds, or -1 for no timeout. + 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) + /// + /// Creates a new RecordFileCommand. + /// + /// the name of the file to stream, must not include extension. + /// the format of the file to be recorded, for example "wav". + /// contains the digits that allow the user to end recording. + /// the maximum record time in milliseconds, or -1 for no timeout. + /// the offset samples to skip. + /// true if a beep should be played before recording. + /// The amount of silence (in seconds) to allow before returning despite the lack of dtmf digits or reaching timeout. + 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 + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayAlphaCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayAlphaCommand.cs new file mode 100644 index 0000000..c9e7fb0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayAlphaCommand.cs @@ -0,0 +1,58 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// Say a given character string, returning early if any of the given DTMF digits are received on the channel.
+ /// 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. + ///
+ public class SayAlphaCommand : AGICommand + { + /// The text to say. + private string text; + /// When one of these digits is pressed the command returns. + private string escapeDigits; + + /// + /// Get/Set Returns the text to say. + /// + public string Text + { + get { return text; } + set { this.text = value; } + } + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + + /// + /// Creates a new SayAlphaCommand. + /// + /// the text to say. + public SayAlphaCommand(string text) + { + this.text = text; + this.escapeDigits = null; + } + + /// + /// Creates a new SayAlphaCommand. + /// + /// the text to say. + /// contains the digits that allow the user to interrupt this command. + public SayAlphaCommand(string text, string escapeDigits) + { + this.text = text; + this.escapeDigits = escapeDigits; + } + + public override string BuildCommand() + { + return "SAY ALPHA " + EscapeAndQuote(text) + " " + EscapeAndQuote(escapeDigits); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDateTimeCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDateTimeCommand.cs new file mode 100644 index 0000000..0c4b2b7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDateTimeCommand.cs @@ -0,0 +1,133 @@ +using System.Text; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Say a given time, returning early if any of the given DTMF digits are pressed.
+ /// 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.
+ /// Available since Asterisk 1.2. + ///
+ 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; + + /// + /// Creates a new SayDateTimeCommand that says the given time. + /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// + public SayDateTimeCommand(long time) + { + this.time = time; + } + + /// + /// Creates a new SayDateTimeCommand that says the given time and allows interruption by one of the given escape digits. + /// + /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + public SayDateTimeCommand(long time, string escapeDigits) + { + this.time = time; + this.escapeDigits = escapeDigits; + } + + /// + /// Creates a new SayDateTimeCommand that says the given time in the given + /// format and allows interruption by one of the given escape digits. + /// + /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + /// the format the time should be said in + public SayDateTimeCommand(long time, string escapeDigits, string format) + { + this.time = time; + this.escapeDigits = escapeDigits; + this.format = format; + } + + /// + /// 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. + /// + /// the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC) + /// the digits that allow the user to interrupt this command or null for none. + /// the format the time should be said in + /// the timezone to use when saying the time, for example "UTC" or "Europe/Berlin". + public SayDateTimeCommand(long time, string escapeDigits, string format, string timezone) + { + this.time = time; + this.escapeDigits = escapeDigits; + this.format = format; + this.timezone = timezone; + } + + /// + /// Get/Set the time to say in seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC). + /// + public long Time + { + get { return this.time; } + set { this.time = value; } + } + + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string getEscapeDigits + { + get { return this.escapeDigits; } + set { this.escapeDigits = value; } + } + + /// + /// Get/Set the format the time should be said in. + /// + public string Format + { + get { return this.format; } + set { this.format = value; } + } + + /// + /// Get/Set the timezone to use when saying the time. + /// + 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(); + } + } + +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDigitsCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDigitsCommand.cs new file mode 100644 index 0000000..868bdfe --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayDigitsCommand.cs @@ -0,0 +1,70 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Say a given digit string, returning early if any of the given DTMF digits are received on the channel.
+ /// 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. + ///
+ public class SayDigitsCommand : AGICommand + { + #region Variables + /// The digits string to say. + private string digits; + /// When one of these digits is pressed while saying the digits the command returns. + private string escapeDigits; + #endregion + + #region Digits + /// + /// Get/Set the digits string to say. + /// + public string Digits + { + get { return digits; } + set { this.digits = value; } + } + #endregion + #region EscapeDigits + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + #endregion + + #region Constructor - SayDigitsCommand(string digits) + /// + /// Creates a new SayDigitsCommand. + /// + /// the digits to say. + public SayDigitsCommand(string digits) + { + this.digits = digits; + this.escapeDigits = null; + } + #endregion + #region Constructor - SayDigitsCommand(string digits, string escapeDigits) + /// + /// Creates a new SayDigitsCommand. + /// + /// the digits to say. + /// the digits that allow the user to interrupt this command. + 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 + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayNumberCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayNumberCommand.cs new file mode 100644 index 0000000..2dfa5aa --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayNumberCommand.cs @@ -0,0 +1,59 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Say a given number, returning early if any of the given DTMF number are received on the channel.
+ /// 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. + ///
+ public class SayNumberCommand : AGICommand + { + /// The number to say. + private string number; + /// When one of these number is pressed while streaming the command returns. + private string escapeDigits; + + /// + /// Get/Set the number to say. + /// + public string Number + { + get { return number; } + set { this.number = value; } + } + /// + /// Get/Set the number that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + + /// + /// Creates a new SayNumberCommand. + /// + /// the number to say. + public SayNumberCommand(string number) + { + this.number = number; + this.escapeDigits = null; + } + + /// + /// Creates a new SayNumberCommand. + /// + /// the number to say. + /// contains the number that allow the user to interrupt this command. + public SayNumberCommand(string number, string escapeDigits) + { + this.number = number; + this.escapeDigits = escapeDigits; + } + + public override string BuildCommand() + { + return "SAY NUMBER " + EscapeAndQuote(number) + " " + EscapeAndQuote(escapeDigits); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayPhoneticCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayPhoneticCommand.cs new file mode 100644 index 0000000..691e59a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayPhoneticCommand.cs @@ -0,0 +1,60 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + + /// + /// Say a given character string with phonetics, returning early if any of the given DTMF digits are received on the channel.
+ /// 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. + ///
+ public class SayPhoneticCommand : AGICommand + { + /// The text to say. + private string text; + /// When one of these digits is pressed the command returns. + private string escapeDigits; + + /// + /// Get/Set the text to say. + /// + public string Text + { + get { return text; } + set { this.text = value; } + } + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + + /// + /// Creates a new SayPhonticCommand. + /// + /// the text to say. + public SayPhoneticCommand(string text) + { + this.text = text; + this.escapeDigits = null; + } + + /// + /// Creates a new SayPhoneticCommand. + /// + /// the text to say. + /// contains the digits that allow the user to interrupt this command. + public SayPhoneticCommand(string text, string escapeDigits) + { + this.text = text; + this.escapeDigits = escapeDigits; + } + + public override string BuildCommand() + { + return "SAY PHONETIC " + EscapeAndQuote(text) + " " + EscapeAndQuote(escapeDigits); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayTimeCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayTimeCommand.cs new file mode 100644 index 0000000..0507ad3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SayTimeCommand.cs @@ -0,0 +1,60 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Say a given time, returning early if any of the given DTMF digits are received on the channel.
+ /// Time is the number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
+ /// 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. + ///
+ public class SayTimeCommand : AGICommand + { + /// The time to say in seconds since 00:00:00 on January 1, 1970. + private long time; + /// When one of these digits is pressed the command returns. + private string escapeDigits; + + /// + /// Get/Set the time to say in seconds since 00:00:00 on January 1, 1970. + /// + public long Time + { + get { return time; } + set { this.time = value; } + } + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get { return escapeDigits; } + set { this.escapeDigits = value; } + } + + /// + /// Creates a new SayTimeCommand. + /// + /// the time to say in seconds since 00:00:00 on January 1, 1970. + public SayTimeCommand(long time) + { + this.time = time; + this.escapeDigits = null; + } + + /// + /// Creates a new SayTimeCommand. + /// + /// the time to say in seconds since 00:00:00 on January 1, 1970. + /// contains the digits that allow the user to interrupt this command. + public SayTimeCommand(long time, string escapeDigits) + { + this.time = time; + this.escapeDigits = escapeDigits; + } + + public override string BuildCommand() + { + return "SAY TIME " + time + " " + EscapeAndQuote(escapeDigits); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendImageCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendImageCommand.cs new file mode 100644 index 0000000..d734bf0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendImageCommand.cs @@ -0,0 +1,42 @@ +using System; + +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sends the given image on a channel.
+ /// Most channels do not support the transmission of images.
+ /// Returns 0 if image is sent, or if the channel does not support image + /// transmission. Returns -1 only on error/hangup.
+ /// Image names should not include extensions. + ///
+ public class SendImageCommand : AGICommand + { + /// + /// Get/Set the image to send. + /// + /// the image to send, should not include extension. + /// the image to send. + public string Image + { + get { return image; } + set { this.image = value; } + } + + /// The name of the image to send. + private string image; + + /// + /// Creates a new SendImageCommand. + /// + /// the image to send, should not include extension. + public SendImageCommand(string image) + { + this.image = image; + } + + public override string BuildCommand() + { + return "SEND IMAGE " + EscapeAndQuote(image); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendTextCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendTextCommand.cs new file mode 100644 index 0000000..9fee75d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SendTextCommand.cs @@ -0,0 +1,40 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sends the given text on a channel.
+ /// Most channels do not support the transmission of text.
+ /// Returns 0 if text is sent, or if the channel does not support text + /// transmission. Returns -1 only on error/hangup. + ///
+ public class SendTextCommand : AGICommand + { + /// The text to send. + private string text; + + /// + /// Get/Set the text to send. + /// + /// the text to send. + /// the text to send. + public string Text + { + get { return text; } + set { this.text = value; } + } + + /// + /// Creates a new SendTextCommand. + /// + /// the text to send. + public SendTextCommand(string text) + { + this.text = text; + } + + public override string BuildCommand() + { + return "SEND TEXT " + EscapeAndQuote(text); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetAutoHangupCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetAutoHangupCommand.cs new file mode 100644 index 0000000..e166fe7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetAutoHangupCommand.cs @@ -0,0 +1,45 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Cause the channel to automatically hangup at the given number of seconds in the future.
+ /// Of course it can be hungup before then as well. Setting to 0 will cause the + /// autohangup feature to be disabled on this channel. + ///
+ public class SetAutoHangupCommand : AGICommand + { + /// The number of seconds before this channel is automatically hung up. + private int time; + + /// + /// Get/Set the number of seconds before this channel is automatically hung up. + /// + /// the number of seconds before this channel is automatically hung up. + /// + /// the number of seconds before this channel is automatically hung up.
+ /// 0 disables the autohangup feature. + /// + public int Time + { + get { return time; } + set { this.time = value; } + } + + /// + /// Creates a new SetAutoHangupCommand. + /// + /// + /// the number of seconds before this channel is automatically hung up.
+ /// 0 disables the autohangup feature. + /// + public SetAutoHangupCommand(int time) + { + this.time = time; + } + + public override string BuildCommand() + { + return "SET AUTOHANGUP " + time; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetCallerIdCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetCallerIdCommand.cs new file mode 100644 index 0000000..b42ef8a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetCallerIdCommand.cs @@ -0,0 +1,35 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Changes the CallerID of the current channel. + /// + public class SetCallerIdCommand : AGICommand + { + /// The new callerId. + private string callerId; + + /// + /// Get/Set the new callerId. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + + /// + /// Creates a new SetCallerIdCommand. + /// + /// the new callerId. + public SetCallerIdCommand(string callerId) + { + this.callerId = callerId; + } + + public override string BuildCommand() + { + return "SET CALLERID " + EscapeAndQuote(callerId); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetContextCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetContextCommand.cs new file mode 100644 index 0000000..491107d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetContextCommand.cs @@ -0,0 +1,35 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sets the context for continuation upon exiting the application. + /// + public class SetContextCommand : AGICommand + { + /// The context for continuation upon exiting the application. + private string context; + + /// + /// Get/Set the context for continuation upon exiting the application. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + + /// + /// Creates a new SetPriorityCommand. + /// + /// the context for continuation upon exiting the application. + public SetContextCommand(string context) + { + this.context = context; + } + + public override string BuildCommand() + { + return "SET CONTEXT " + EscapeAndQuote(context); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetExtensionCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetExtensionCommand.cs new file mode 100644 index 0000000..ac74f39 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetExtensionCommand.cs @@ -0,0 +1,37 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sets the extension for continuation upon exiting the application. + /// + public class SetExtensionCommand : AGICommand + { + /// + /// Get/Set the extension for continuation upon exiting the application. + /// + /// the extension for continuation upon exiting the application. + /// the extension for continuation upon exiting the application. + public string Extension + { + get { return extension; } + set { this.extension = value; } + } + + /// The extension for continuation upon exiting the application. + private string extension; + + /// + /// Creates a new SetPriorityCommand. + /// + /// the extension for continuation upon exiting the application. + public SetExtensionCommand(string extension) + { + this.extension = extension; + } + + public override string BuildCommand() + { + return "SET EXTENSION " + EscapeAndQuote(extension); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOffCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOffCommand.cs new file mode 100644 index 0000000..32f94bb --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOffCommand.cs @@ -0,0 +1,20 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Turns off music on hold on the current channel.
+ /// Always returns 0. + ///
+ public class SetMusicOffCommand : AGICommand + { + /// Creates a new SetMusicOffCommand. + public SetMusicOffCommand() + { + } + + public override string BuildCommand() + { + return "SET MUSIC OFF"; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOnCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOnCommand.cs new file mode 100644 index 0000000..6521cde --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetMusicOnCommand.cs @@ -0,0 +1,44 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Turns on music on hold on the current channel.
+ /// Always returns 0. + ///
+ public class SetMusicOnCommand : AGICommand + { + /// + /// Get/Set the music on hold class to play music from. + /// + /// the music on hold class to play music from or null for the default class. + /// the music on hold class to play music from or null for the default class. + public string MusicOnHoldClass + { + get { return musicOnHoldClass; } + set { this.musicOnHoldClass = value; } + } + + /// The music on hold class to play music from. + private string musicOnHoldClass; + + /// Creates a new SetMusicOnCommand playing music from the default music on hold class. + public SetMusicOnCommand() + { + this.musicOnHoldClass = null; + } + + /// + /// Creates a new SetMusicOnCommand playing music from the default music on hold class. + /// + /// the music on hold class to play music from. + public SetMusicOnCommand(string musicOnHoldClass) + { + this.musicOnHoldClass = musicOnHoldClass; + } + + public override string BuildCommand() + { + return "SET MUSIC ON" + (musicOnHoldClass == null?"":" " + EscapeAndQuote(musicOnHoldClass)); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetPriorityCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetPriorityCommand.cs new file mode 100644 index 0000000..34917bf --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetPriorityCommand.cs @@ -0,0 +1,62 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sets the priority for continuation upon exiting the application.
+ /// Since Asterisk 1.2 SetPriorityCommand also supports labels. + ///
+ public class SetPriorityCommand : AGICommand + { + /// The priority or label for continuation upon exiting the application. + private string priorityOrLabel; + + /// + /// Get/Set the priority or label for continuation upon exiting the application. + /// + public int Priority + { + get + { + try + { + return Int32.Parse(this.priorityOrLabel); + } + catch {} + return 0; + } + set { this.priorityOrLabel = value.ToString(); } + } + + /// + /// Get/Set the label for continuation upon exiting the application. + /// + public string Label + { + get { return this.priorityOrLabel; } + set { this.priorityOrLabel = value; } + } + + /// + /// Creates a new SetPriorityCommand. + /// + /// the priority for continuation upon exiting the application. + public SetPriorityCommand(int priority) + { + this.priorityOrLabel = priority.ToString(); + } + + /// + /// Creates a new SetPriorityCommand. + /// + /// the label for continuation upon exiting the application. + public SetPriorityCommand(string label) + { + this.priorityOrLabel = label; + } + + public override string BuildCommand() + { + return "SET PRIORITY " + EscapeAndQuote(priorityOrLabel); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetVariableCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetVariableCommand.cs new file mode 100644 index 0000000..229f22c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/SetVariableCommand.cs @@ -0,0 +1,47 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Sets the given channel variable to the given value. + /// + public class SetVariableCommand : AGICommand + { + /// The name of the variable to set. + private string varName; + /// The value to set. + private string varValue; + + /// + /// Get/Set the name of the variable to set. + /// + public string Variable + { + get { return varName; } + set { this.varName = value; } + } + /// + /// Get/Set the value to set. + /// + public string Value + { + get { return varValue; } + set { this.varValue = value; } + } + + /// + /// Creates a new GetVariableCommand. + /// + /// the name of the variable to set. + /// the value to set. + public SetVariableCommand(string name, string value) + { + this.varName = name; + this.varValue = value; + } + + public override string BuildCommand() + { + return "SET VARIABLE " + EscapeAndQuote(varName) + " " + EscapeAndQuote(varValue); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/StreamFileCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/StreamFileCommand.cs new file mode 100644 index 0000000..505e735 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/StreamFileCommand.cs @@ -0,0 +1,122 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + + /// + /// Plays the given file, allowing playback to be interrupted by the given digits, if any.
+ /// If offset is provided then the audio will seek to sample offset before play starts.
+ /// 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.
+ /// Remember, the file extension must not be included in the filename. + ///
+ public class StreamFileCommand : AGICommand + { + #region File + /// + /// Get/Set the name of the file to stream. + /// The name of the file to stream, must not include extension. + /// + public string File + { + get + { + return file; + } + set + { + this.file = value; + } + } + #endregion + + #region EscapeDigits + /// + /// Get/Set the digits that allow the user to interrupt this command. + /// + public string EscapeDigits + { + get + { + return escapeDigits; + } + + set + { + this.escapeDigits = value; + } + } + #endregion + + #region Offset + /// + /// Get/Set the offset samples to skip before streaming. + /// + public int Offset + { + get + { + return offset; + } + + set + { + this.offset = value; + } + } + #endregion + + /// The name of the file to stream. + private string file; + + /// When one of these digits is pressed while streaming the command returns. + private string escapeDigits; + + /// The offset samples to skip before streaming. + private int offset; + + /// Creates a new StreamFileCommand, streaming from the beginning. + /// + /// + /// the name of the file to stream, must not include extension. + /// + public StreamFileCommand(string file) + { + this.file = file; + this.escapeDigits = null; + this.offset = - 1; + } + + /// + /// Creates a new StreamFileCommand, streaming from the beginning. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + public StreamFileCommand(string file, string escapeDigits) + { + this.file = file; + this.escapeDigits = escapeDigits; + this.offset = - 1; + } + + /// + /// Creates a new StreamFileCommand, streaming from the given offset. + /// + /// the name of the file to stream, must not include extension. + /// contains the digits that allow the user to interrupt this command. + /// Maybe null if you don't want the user to interrupt. + /// + /// the offset samples to skip before streaming. + 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); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/TDDModeCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/TDDModeCommand.cs new file mode 100644 index 0000000..5a4bd97 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/TDDModeCommand.cs @@ -0,0 +1,40 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Enable/Disable TDD transmission/reception on a channel.
+ /// Returns 1 if successful, or 0 if channel is not TDD-capable. + ///
+ public class TDDModeCommand : AGICommand + { + private string mode; + + /// + /// Get the mode to set. + /// + public string Mode + { + get { return mode; } + } + /// + /// Sets the mode to set. The mode to set, this can be one of "on", "off", "mate" or "tdd". + /// + public string Timeout + { + set { this.mode = value; } + } + + /// + /// Creates a new TDDModeCommand. The mode to set, this can be one of "on", "off", "mate" or "tdd". + /// + public TDDModeCommand(string mode) + { + this.mode = mode; + } + + public override string BuildCommand() + { + return "TDD MODE " + EscapeAndQuote(mode); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/VerboseCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/VerboseCommand.cs new file mode 100644 index 0000000..4adeb6c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/VerboseCommand.cs @@ -0,0 +1,71 @@ +using System; +namespace Asterisk.NET.FastAGI.Command +{ + #region Class - VerboseCommand + /// + /// Sends a message to the console via the verbose message system.
+ /// Always returns 1. + ///
+ public class VerboseCommand : AGICommand + { + #region Variables + /// The message to send. + private string message; + /// The verbosity level to use.
+ /// Must be in [1..4] + ///
+ private int level; + #endregion + + #region Message + /// + /// Get/Set the message to send. + /// + public string Message + { + get { return message; } + set { this.message = value; } + } + #endregion + #region Level + /// + /// Get/Set the level to use. + /// + /// IllegalArgumentException if level is not in [1..4] + 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) + /// + /// Creates a new VerboseCommand. + /// + /// the message to send. + /// the verbosity level to use. Must be in [1..4] + /// IllegalArgumentException if level is not in [1..4] + 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 +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Command/WaitForDigitCommand.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Command/WaitForDigitCommand.cs new file mode 100644 index 0000000..84758a0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Command/WaitForDigitCommand.cs @@ -0,0 +1,45 @@ +namespace Asterisk.NET.FastAGI.Command +{ + /// + /// Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.
+ /// 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. + ///
+ public class WaitForDigitCommand : AGICommand + { + /// The milliseconds to wait for the channel to receive a DTMF digit. + private long timeout; + + /// + /// Get/Set the milliseconds to wait for the channel to receive a DTMF digit. + /// + public long Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + + /// + /// Creates a new WaitForDigitCommand with a default timeout of -1 which blocks the channel indefinitely. + /// + public WaitForDigitCommand() + { + this.timeout = - 1; + } + + /// + /// Creates a new WaitForDigitCommand. + /// + /// the milliseconds to wait for the channel to receive a DTMF digit. + public WaitForDigitCommand(int timeout) + { + this.timeout = timeout; + } + + public override string BuildCommand() + { + return "WAIT FOR DIGIT " + timeout.ToString(); + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIException.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIException.cs new file mode 100644 index 0000000..5ac095f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIException.cs @@ -0,0 +1,33 @@ +using System; +namespace Asterisk.NET.FastAGI +{ + /// + /// Abstract base class for all AGI specific exceptions. + /// + public class AGIException : Exception + { + public AGIException() + : base() + { + } + + /// + /// Creates a new AGIExeption with the given message. + /// + /// a message decribing the AGIException. + public AGIException(string message) + : base(message) + { + } + + /// + /// Creates a new AGIExeption with the given message and cause. + /// + /// a message decribing the AGIException. + /// the throwable that caused this exception to be thrown. + public AGIException(string message, Exception cause) + : base(message, cause) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIHangupException.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIHangupException.cs new file mode 100644 index 0000000..b78d280 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGIHangupException.cs @@ -0,0 +1,14 @@ +using System; +namespace Asterisk.NET.FastAGI +{ + /// + /// The AGIHangupException is thrown if the channel has been hang up while processing the AGIRequest. + /// + public class AGIHangupException : AGIException + { + public AGIHangupException() + : base("Channel was hung up.") + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGINetworkException.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGINetworkException.cs new file mode 100644 index 0000000..847b041 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/AGINetworkException.cs @@ -0,0 +1,14 @@ +using System; +namespace Asterisk.NET.FastAGI +{ + /// + /// The AGINetworkException usally wraps an IOException denoting a network problem when talking to the Asterisk server. + /// + public class AGINetworkException : AGIException + { + public AGINetworkException(string message, Exception cause) + : base(message, cause) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidCommandSyntaxException.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidCommandSyntaxException.cs new file mode 100644 index 0000000..675d15f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidCommandSyntaxException.cs @@ -0,0 +1,41 @@ +using System; +namespace Asterisk.NET.FastAGI +{ + /// + /// An InvalidCommandSyntaxException is thrown when the reader receives a reply with status code 520. + /// + public class InvalidCommandSyntaxException : AGIException + { + /// + /// Returns the synopsis of the command that was called with invalid syntax. + /// + /// the synopsis of the command that was called with invalid syntax. + public string Synopsis + { + get { return synopsis; } + } + /// + /// Returns a description of the command that was called with invalid syntax. + /// + /// a description of the command that was called with invalid syntax. + public string Usage + { + get { return usage; } + } + + private string synopsis; + private string usage; + + /// + /// Creates a new InvalidCommandSyntaxException with the given synopsis and usage. + /// + /// the synopsis of the command. + /// the usage of the command. + public InvalidCommandSyntaxException(string synopsis, string usage) + : base("Invalid command syntax: " + synopsis) + { + this.synopsis = synopsis; + this.usage = usage; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidOrUnknownCommandException.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidOrUnknownCommandException.cs new file mode 100644 index 0000000..e1a5b34 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Exceptions/InvalidOrUnknownCommandException.cs @@ -0,0 +1,19 @@ +using System; +namespace Asterisk.NET.FastAGI +{ + /// + /// An InvalidOrUnknownCommandException is thrown when the reader receives a reply with status code 510. + /// + public class InvalidOrUnknownCommandException : AGIException + { + + /// + /// Creates a new InvalidOrUnknownCommandException. + /// + /// the invalid or unknown command. + public InvalidOrUnknownCommandException(string command) + : base("Invalid or unknown command: " + command) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs new file mode 100644 index 0000000..3687670 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/MappingStrategy.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using System.Resources; +using System.Reflection; + +namespace Asterisk.NET.FastAGI +{ + /// + /// A MappingStrategy that is configured via a resource bundle.
+ /// The resource bundle contains the script part of the url as key and the fully + /// qualified class name of the corresponding AGIScript as value.
+ /// Example: + ///
+	/// noopcommand = Asterisk.NET.FastAGI.Command.NoopCommand
+	/// 
+ /// NoopCommand must implement the AGIScript interface and have a default constructor with no parameters.
+ ///
+ 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; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/FastAGI/Script/AGINoAction.cs b/Asterisk.2013/Asterisk.NET/FastAGI/Script/AGINoAction.cs new file mode 100644 index 0000000..518c8bb --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/FastAGI/Script/AGINoAction.cs @@ -0,0 +1,10 @@ +namespace Asterisk.NET.FastAGI.Scripts +{ + class AGINoAction : AGIScript + { + public override void Service(AGIRequest request, AGIChannel channel) + { + base.Hangup(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Helper.cs b/Asterisk.2013/Asterisk.NET/Helper.cs new file mode 100644 index 0000000..740778b --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Helper.cs @@ -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[]) + /// The hex digits used to build a hex string representation of a byte array. + internal static readonly char[] hexChar = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /// + /// Converts a byte array to a hex string representing it. The hex digits are lower case. + /// + /// the byte array to convert + /// the hex representation of b + 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) + /// + /// Checks if a String represents true or false according to Asterisk's logic.
+ /// The original implementation is util.c is as follows: + ///
+ /// the String to check for true. + /// + /// true if s represents true, + /// false otherwise. + /// + 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(...) + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + internal static int URShift(int number, int bits) + { + if (number >= 0) + return number >> bits; + else + return (number >> bits) + (2 << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + internal static int URShift(int number, long bits) + { + return URShift(number, (int)bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + internal static long URShift(long number, int bits) + { + if (number >= 0) + return number >> bits; + else + return (number >> bits) + (2L << ~bits); + } + + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + internal static long URShift(long number, long bits) + { + return URShift(number, (int)bits); + } + #endregion + + #region ToArray(ICollection c, object[] objects) + /// + /// Obtains an array containing all the elements of the collection. + /// + /// The array into which the elements of the collection will be stored. + /// The array containing all the elements of the collection. + 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 dictionary, string variables, char[] delim) + /// + /// Parse variable(s) string to dictionary. + /// + /// + /// variable(a) string + /// variable pairs delimiter + /// + internal static Dictionary ParseVariables(Dictionary dictionary, string variables, char[] delim) + { + if (dictionary == null) + dictionary = new Dictionary(); + 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) + /// + /// Join variables dictionary to string. + /// + /// + /// + /// + 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) + /// + /// Returns a Map of getter methods of the given class.
+ /// 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. + ///
+ /// the class to return the getters for + /// a Map of attributes and their accessor methods (getters) + internal static Dictionary GetGetters(Type clazz) + { + string name; + string methodName; + MethodInfo method; + + Dictionary accessors = new Dictionary(); + 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) + /// + /// Returns a Map of setter methods of the given class.
+ /// 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. + ///
+ /// the class to return the setters for + /// a Map of attributes and their accessor methods (setters) + 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) + /// + /// Convert object with all properties to string + /// + /// + /// + 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 arrays = new List(); + // 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 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 == "") + list[name] = null; + else + list[name] = val; + } + } + #endregion + + #region stripIllegalCharacters(string s) + /// + /// Strips all illegal charaters from the given lower case string. + /// + /// the original string + /// the string with all illegal characters stripped + 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) + /// + /// Constructs an instance of ManagerResponse based on a map of attributes. + /// + /// the attributes and their values. The keys of this map must be all lower case. + /// the response with the given attributes. + internal static ManagerResponse BuildResponse(Dictionary 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) + /// + /// Builds the event based on the given map of attributes and the registered event classes. + /// + /// source attribute for the event + /// map containing event attributes + /// a concrete instance of ManagerEvent or null if no event class was registered for the event type. + internal static ManagerEvent BuildEvent(IDictionary list, ManagerConnection source, Dictionary 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) + /// + /// Register buildin Event classes + /// + /// + internal static void RegisterBuiltinEventClasses(Dictionary 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 list, Type clazz) + internal static void RegisterEventClass(Dictionary 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 list, int index, Type eventType) + internal static void RegisterEventHandler(Dictionary 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 + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/IO/ServerSocket.cs b/Asterisk.2013/Asterisk.NET/IO/ServerSocket.cs new file mode 100644 index 0000000..2fa996e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/IO/ServerSocket.cs @@ -0,0 +1,38 @@ +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Asterisk.NET.IO +{ + /// + /// ServerSocket using standard socket classes. + /// + 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(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/IO/SocketConnection.cs b/Asterisk.2013/Asterisk.NET/IO/SocketConnection.cs new file mode 100644 index 0000000..b69e757 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/IO/SocketConnection.cs @@ -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) + /// + /// Consructor + /// + /// client host + /// client port + /// encoding + 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) + /// + /// Constructor + /// + /// TCP client from Listener + /// encoding + 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 + /// + /// Returns the connection state of the socket. + /// + 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() + /// + /// 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. + /// + /// the line of text received excluding any newline character + /// IOException if the connection has been closed. + public string ReadLine() + { + string line = null; + try + { + line = reader.ReadLine(); + } + catch + { + line = null; + } + return line; + } + #endregion + + + #region Write(string s) + /// + /// Sends a given String to the socket connection. + /// + /// the String to send. + /// IOException if the String cannot be sent, maybe because the + /// connection has already been closed. + public void Write(string s) + { + writer.Write(s); + } + #endregion + + #region Write(string msg) + /// + /// Sends a given String to the socket connection. + /// + /// the String to send. + /// IOException if the String cannot be sent, maybe because the + /// connection has already been closed. + 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 + /// + /// Closes the socket connection including its input and output stream and + /// frees all associated ressources.
+ /// When calling close() any Thread currently blocked by a call to readLine() + /// will be unblocked and receive an IOException. + ///
+ /// IOException if the socket connection cannot be closed. + public void Close() + { + try + { + tcpClient.Client.Shutdown(SocketShutdown.Both); + tcpClient.Client.Close(); + tcpClient.Close(); + } + catch { } + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/IParseSupport.cs b/Asterisk.2013/Asterisk.NET/IParseSupport.cs new file mode 100644 index 0000000..356ad8e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/IParseSupport.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET +{ + internal interface IParseSupport + { + Dictionary Attributes + { + get; + } + bool Parse(string key, string value); + Dictionary ParseSpecial(Dictionary attributes); + } +} diff --git a/Asterisk.2013/Asterisk.NET/Logger.cs b/Asterisk.2013/Asterisk.NET/Logger.cs new file mode 100644 index 0000000..0810ced --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Logger.cs @@ -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 + /// + /// Facade to hide details of the underlying logging system. + /// + public sealed class Logger + { + private static Logger logger; + + /// + /// Returns an instance of Log suitable for logging from the given class. + /// + /// the created logger. + public static Logger Instance() + { + if(logger == null) + logger = new Logger(); + return logger; + } + + public enum MessageLevel + { + Info, + Warning, + Error, + Debug + } + + /// + /// Creates a new CommonsLoggingLog obtained from commons-logging's LogFactory for the given class. + /// + 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; + + /// + /// Get visibility for message level of class:method + /// + /// messageType:class:method + /// + 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; + } + + /// + /// Set visibility for message level of class:method + /// + /// visible + /// message level + /// class:method + 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; + } + } + + /// + /// Set visibility for message level of class:method + /// + /// visible + /// message level + /// class:method + 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 +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/AbsoluteTimeoutAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/AbsoluteTimeoutAction.cs new file mode 100644 index 0000000..a0eb871 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/AbsoluteTimeoutAction.cs @@ -0,0 +1,68 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The AbsoluteTimeoutAction sets the absolute maximum amount of time permitted for a call on a given channel.
+ /// Note that the timeout is set from the current time forward, not counting the number of seconds the call has already been up.
+ /// When setting a new timeout all previous absolute timeouts are cancelled.
+ /// 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).
+ /// This action corresponds the the AbsoluteTimeout command used in the dialplan. + ///
+ public class AbsoluteTimeoutAction : ManagerAction + { + private string channel; + private int timeout; + + #region AbsoluteTimeoutAction() + /// + /// Creates a new empty AbsoluteTimeoutAction. + /// + public AbsoluteTimeoutAction() + { + } + #endregion + #region AbsoluteTimeoutAction(channel, timeout) + /// + /// Creates a new AbsoluteTimeoutAction with the given channel and timeout. + /// + /// the name of the channel + /// the timeout in seconds or 0 to cancel the AbsoluteTimeout + public AbsoluteTimeoutAction(string channel, int timeout) + { + this.channel = channel; + this.timeout = timeout; + } + #endregion + + #region Action + /// + /// Get the name of this action, i.e. "AbsoluteTimeout". + /// + override public string Action + { + get { return "AbsoluteTimeout"; } + } + #endregion + #region Channel + /// + /// Get/Set the name of the channel. + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + #endregion + #region Timeout + /// + /// Get/Set the timeout (in seconds) to set. + /// + public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/AgentCallbackLoginAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentCallbackLoginAction.cs new file mode 100644 index 0000000..f5a0955 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentCallbackLoginAction.cs @@ -0,0 +1,107 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The AgentCallbackLoginAction sets an agent as logged in with callback.
+ /// You can pass an extentsion (and optionally a context) to specify the + /// destination of the callback.
+ /// 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.
+ /// Available since Asterisk 1.2 + ///
+ public class AgentCallbackLoginAction : ManagerAction + { + private string agent; + private string exten; + private string context; + private bool ackCall; + private long wrapupTime; + + /// + /// Get the name of this action, i.e. "AgentCallbackLogin". + /// + override public string Action + { + get { return "AgentCallbackLogin"; } + } + /// + /// Get/Set the name of the agent to log in, for example "1002".
+ /// This is property is mandatory. + ///
+ public string Agent + { + get { return this.agent; } + set { this.agent = value; } + } + /// + /// Get/Set the extension to use for callback.
+ /// This is property is mandatory. + ///
+ public string Exten + { + get { return this.exten; } + set { this.exten = value; } + } + /// + /// Get/Set the context of the extension to use for callback. + /// + public string Context + { + get { return this.context; } + set { this.context = value; } + } + /// + /// Get/Set if an acknowledgement is needed when agent is called back.
+ /// true if acknowledgement by '#' is required when agent is called back, false otherwise. + /// This property is optional, it allows you to override the defaults defined in Asterisk's configuration. + ///
+ public bool AckCall + { + get { return this.ackCall; } + set { this.ackCall = value; } + } + /// + /// Returns the minimum amount of time (in milliseconds) after disconnecting before the caller can receive a new call.
+ /// This property is optional, it allows you to override the defaults defined in Asterisk's configuration. + ///
+ public long WrapupTime + { + get { return this.wrapupTime; } + set { this.wrapupTime = value; } + } + + /// + /// Creates a new empty AgentCallbackLoginAction. + /// + public AgentCallbackLoginAction() + { + } + + /// + /// Creates a new AgentCallbackLoginAction, that logs in the given agent at + /// the given callback extension. + /// + /// the name of the agent to log in + /// the extension that is called to connect a queue member with this agent + public AgentCallbackLoginAction(string agent, string exten) + { + this.agent = agent; + this.exten = exten; + } + + /// + /// Creates a new AgentCallbackLoginAction, that logs in the given agent at + /// the given callback extension in the given context. + /// + /// the name of the agent to log in + /// the extension that is called to connect a queue member with this agent + /// the context of the extension to use for callback + public AgentCallbackLoginAction(string agent, string exten, string context) + : this(agent, exten) + { + this.context = context; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/AgentLogoffAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentLogoffAction.cs new file mode 100644 index 0000000..880e257 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentLogoffAction.cs @@ -0,0 +1,94 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The AgentLogoffAction sets an agent as no longer logged in. + /// + public class AgentLogoffAction : ManagerAction + { + private string agent; + private bool soft; + + #region Action + /// + /// Returns the name of this action, i.e. "AgentLogoff". + /// + /// the name of this action + override public string Action + { + get + { + return "AgentLogoff"; + } + } + #endregion + + #region Agent + /// + /// Returns the name of the agent to log off, for example "1002". + /// + /// the name of the agent to log off + /// Sets the name of the agent to log off, for example "1002".
+ /// This is property is mandatory. + ///
+ /// the name of the agent to log off + public string Agent + { + get + { + return agent; + } + + set + { + this.agent = value; + } + + } + #endregion + + #region Soft + /// + /// Get/Set whether to hangup existing calls or not.
+ /// Default is to hangup existing calls on logoff. + ///
+ /// true if existing calls should not be hung up, false otherwise.
+ /// null if default should be used. + ///
+ public bool Soft + { + get { return soft; } + set { this.soft = value; } + } + #endregion + + #region Constructors - AgentLogoffAction() + /// Creates a new empty AgentLogoffAction. + public AgentLogoffAction() + { + } + /// + /// Creates a new AgentLogoffAction that logs off the given agent + /// + /// the name of the agent to log off. + public AgentLogoffAction(string agent) + { + this.agent = agent; + } + #endregion + + #region Constructors - AgentLogoffAction(string agent, bool soft) + /// + /// Creates a new AgentLogoffAction that logs off the given agent + /// + /// the name of the agent to log off. + /// true if exisiting calls should not be hung up on logout. + public AgentLogoffAction(string agent, bool soft) + : this(agent) + { + this.soft = soft; + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/AgentsAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentsAction.cs new file mode 100644 index 0000000..3843b2f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/AgentsAction.cs @@ -0,0 +1,41 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The AgentsAction requests the state of all agents.
+ /// For each agent an AgentsEvent is generated. + /// After the state of all agents has been reported an AgentsCompleteEvent is generated.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// + public class AgentsAction : ManagerActionEvent + { + #region Action + /// + /// Get the name of this action, i.e. "Agents". + /// + override public string Action + { + get { return "Agents"; } + } + #endregion + + #region ActionCompleteEventClass + public override Type ActionCompleteEventClass() + { + return typeof(Event.AgentsCompleteEvent); + } + #endregion + + #region AgentsAction() + /// + /// Creates a new AgentsAction. + /// + public AgentsAction() + { + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ChallengeAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ChallengeAction.cs new file mode 100644 index 0000000..b3e4fad --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ChallengeAction.cs @@ -0,0 +1,50 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// 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. + /// + /// + /// + public class ChallengeAction : ManagerAction + { + private string authType; + + /// + /// Get the name of this action, i.e. "Challenge". + /// + override public string Action + { + get { return "Challenge"; } + } + /// + /// Get/Set the digest alogrithm to use. Currently asterisk only supports "MD5". + /// + public string AuthType + { + get { return this.authType; } + set { this.authType = value; } + } + + /// + /// Creates a new empty ChallengeAction with MD5 algorithm + /// + public ChallengeAction() + { + this.authType = "MD5"; + } + + /// + /// Creates a new ChallengeAction that requests a new login challenge for use + /// with the given digest algorithm. + /// + /// the digest alogrithm to use. + public ChallengeAction(string authType) + { + this.authType = authType; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ChangeMonitorAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ChangeMonitorAction.cs new file mode 100644 index 0000000..6f4dbcf --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ChangeMonitorAction.cs @@ -0,0 +1,69 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ChangeMonitorAction changes the monitoring filename of a channel. + /// It has no effect if the channel is not monitored.
+ /// It is implemented in res/res_monitor.c + ///
+ public class ChangeMonitorAction : ManagerAction + { + private string channel; + private string file; + + #region Action + /// + /// Get the name of this action, i.e. "ChangeMonitor". + /// + override public string Action + { + get { return "ChangeMonitor"; } + } + #endregion + #region Channel + /// + /// Get/Set the name of the monitored channel.
+ /// This property is mandatory. + ///
+ public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + #endregion + #region File + /// + /// Get/Set the name of the file to which the voice data is written.
+ /// This property is mandatory. + ///
+ public string File + { + get { return this.file; } + set { this.file = value; } + } + #endregion + + #region ChangeMonitorAction() + /// + /// Creates a new empty ChangeMonitorAction. + /// + public ChangeMonitorAction() + { + } + #endregion + #region ChangeMonitorAction(string channel, string file) + /// + /// Creates a new ChangeMonitorAction that causes monitoring data for the + /// given channel to be written to the given file(s). + /// + /// the name of the channel that is monitored + /// the (base) name of the file(s) to which the voice data is written + public ChangeMonitorAction(string channel, string file) + { + this.channel = channel; + this.file = file; + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/CommandAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/CommandAction.cs new file mode 100644 index 0000000..fc80a35 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/CommandAction.cs @@ -0,0 +1,45 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The CommandAction sends a command line interface (CLI) command to the asterisk server.
+ /// For a list of supported commands type help on asterisk's command line. + ///
+ public class CommandAction : ManagerAction + { + protected internal string command; + + /// + /// Get the name of this action, i.e. "Command". + /// + override public string Action + { + get { return "Command"; } + } + /// + /// Get/Set the CLI command to send to the asterisk server. + /// + public string Command + { + get { return this.command; } + set { this.command = value; } + } + + /// + /// Creates a new CommandAction. + /// + public CommandAction() + { + } + + /// + /// Creates a new CommandAction with the given command. + /// + /// the CLI command to execute. + public CommandAction(string command) + { + this.command = command; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/DBGetAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/DBGetAction.cs new file mode 100644 index 0000000..407fb2f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/DBGetAction.cs @@ -0,0 +1,67 @@ +using System; +namespace Asterisk.NET.Manager.Action +{ + + /// + /// Retrieves an entry in the Asterisk database for a given family and key.
+ /// If an entry is found a DBGetResponseEvent is sent by Asterisk containing the + /// value, otherwise a ManagerError indicates that no entry matches. + ///
+ /// + public class DBGetAction : ManagerActionEvent + { + private string family; + private string key; + + public override string Action + { + get { return "DBGet"; } + } + /// Returns the family of the key. + /// + /// + /// the family of the key. + /// + /// Sets the family of the key. + /// + /// + /// the family of the key. + /// + public string Family + { + get { return family; } + set { this.family = value; } + } + /// + /// Get/Set the the key of the entry to retrieve. + /// + public string Key + { + get { return key; } + set { this.key = value; } + } + public override Type ActionCompleteEventClass() + { + return typeof(Event.DBGetResponseEvent); + } + + /// + /// Creates a new empty DBGetAction. + /// + public DBGetAction() + { + } + + /// + /// Creates a new DBGetAction that retrieves the value of the database entry + /// with the given key in the given family. + /// + /// the family of the key + /// the key of the entry to retrieve + public DBGetAction(string family, string key) + { + this.family = family; + this.key = key; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/DBPutAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/DBPutAction.cs new file mode 100644 index 0000000..4ec033d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/DBPutAction.cs @@ -0,0 +1,64 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// Adds or updates an entry in the Asterisk database for a given family, key, and value.
+ /// Available since Asterisk 1.2 + ///
+ public class DBPutAction : ManagerAction + { + private string family; + private string key; + private string val; + + override public string Action + { + get { return "DBPut"; } + } + /// + /// Get/Set the family of the key to set. + /// + public string Family + { + get { return this.family; } + set { this.family = value; } + } + /// + /// Get/Set the the key to set. + /// + public string Key + { + get { return this.key; } + set { this.key = value; } + } + /// + /// Get/Set the value to set. + /// + public string Val + { + get { return val; } + set { this.val = value; } + } + + /// + /// Creates a new empty DBPutAction. + /// + public DBPutAction() + { + } + + /// + /// Creates a new DBPutAction that sets the value of the database entry with the given key in the given family. + /// + /// the family of the key + /// the key of the entry to set + /// the value to set + public DBPutAction(string family, string key, string val) + { + this.family = family; + this.key = key; + this.val = val; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/EventsAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/EventsAction.cs new file mode 100644 index 0000000..a837c4a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/EventsAction.cs @@ -0,0 +1,50 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// With the EventsAction you can specify what kind of events should be sent to this manager connection. + /// + public class EventsAction : ManagerAction + { + private string eventMask; + + /// + /// Get the name of this action, i.e. "Events". + /// + override public string Action + { + get { return "Events"; } + } + /// + /// Get/Set the event mask.
+ /// 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. + ///
+ public string EventMask + { + get { return this.eventMask; } + set { this.eventMask = value; } + } + + /// + /// Creates a new empty EventsAction. + /// + public EventsAction() + { + } + + /// + /// Creates a new EventsAction that applies the given event mask to the current manager connection. + /// + /// the event mask.
+ /// 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. + /// + public EventsAction(string eventMask) + { + this.eventMask = eventMask; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ExtensionStateAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ExtensionStateAction.cs new file mode 100644 index 0000000..7548b35 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ExtensionStateAction.cs @@ -0,0 +1,43 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ExtensionStateAction queries the state of an extension in a given context. + /// + public class ExtensionStateAction : ManagerAction + { + private string exten; + private string context; + + #region Action + /// + /// Get the name of this action, i.e. "ExtensionState". + /// + override public string Action + { + get { return "ExtensionState"; } + } + #endregion + #region Exten + /// + /// Get/Set the extension to query. + /// + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + #endregion + #region Context + /// + /// Get/Set the name of the context that contains the extension to query. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/GetConfigAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/GetConfigAction.cs new file mode 100644 index 0000000..b47c5ff --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/GetConfigAction.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Asterisk.NET.Manager.Response; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The GetConfigAction sends a GetConfig command to the asterisk server. + /// + public class GetConfigAction : ManagerActionResponse + { + private string filename; + + /// + /// Creates a new GetConfigAction. + /// + public GetConfigAction() + { + } + + /// + /// Get the name of this action. + /// + public override string Action + { + get { return "GetConfig"; } + } + + /// + /// Get the name of this action. + /// + /// the configuration filename. + /// + public GetConfigAction(string filename) + { + this.filename = filename; + } + + /// + /// Get/Set the configuration filename. + /// + public string Filename + { + get { return this.filename; } + set { this.filename = value; } + } + + public override object ActionCompleteResponseClass() + { + return new GetConfigResponse(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/GetVarAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/GetVarAction.cs new file mode 100644 index 0000000..f3fd85c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/GetVarAction.cs @@ -0,0 +1,65 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The GetVarAction queries for a channel variable. + /// + public class GetVarAction : ManagerAction + { + private string channel; + private string varName; + + /// + /// Creates a new empty GetVarAction. + /// + public GetVarAction() + { + } + + /// + /// Creates a new GetVarAction that queries for the given global variable. + /// + /// the name of the global variable to query. + public GetVarAction(string variable) + { + this.varName = variable; + } + + /// + /// Creates a new GetVarAction that queries for the given local channel variable. + /// + /// the name of the channel, for example "SIP/1234-9cd". + /// the name of the variable to query. + public GetVarAction(string channel, string variable) + { + this.channel = channel; + this.varName = variable; + } + + /// + /// Get the name of this action, i.e. "GetVar". + /// + override public string Action + { + get { return "GetVar"; } + } + /// + /// Get/Set the name of the channel, if you query for a local channel variable. + /// Leave empty to query for a global variable. + /// + public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + /// + /// Get/Set the name of the variable to query. + /// + public string Variable + { + get { return this.varName; } + set { this.varName = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/HangupAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/HangupAction.cs new file mode 100644 index 0000000..15e8538 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/HangupAction.cs @@ -0,0 +1,44 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The HangupAction causes the pbx to hang up a given channel. + /// + public class HangupAction : ManagerAction + { + private string channel; + + /// + /// Creates a new empty HangupAction. + /// + public HangupAction() + { + } + + /// + /// Creates a new HangupAction that hangs up the given channel. + /// + /// the name of the channel to hangup. + public HangupAction(string channel) + { + this.channel = channel; + } + + /// + /// Get the name of this action, i.e. "Hangup". + /// + override public string Action + { + get { return "Hangup"; } + } + /// + /// Get/Set the name of the channel to hangup. + /// + public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/LoginAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/LoginAction.cs new file mode 100644 index 0000000..390d307 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/LoginAction.cs @@ -0,0 +1,128 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The LoginAction authenticates the connection.
+ /// A successful login is the precondition for sending any other action except + /// for the ChallengeAction.
+ /// 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. + ///
+ /// + /// + public class LoginAction : ManagerAction + { + private string username; + private string secret; + private string authType; + private string key; + private string events; + + /// + /// Get the name of this action, i.e. "Login". + /// + override public string Action + { + get { return "Login"; } + } + /// + /// Get/Set the username as configured in asterik's manager.conf. + public string Username + { + get { return this.username; } + set { this.username = value; } + } + /// + /// Get/Set the secret to use when using cleartext login.
+ /// The secret contains the user's password as configured in Asterisk's manager.conf.
+ /// The secret and key properties are mutually exclusive. + ///
+ public string Secret + { + get { return this.secret; } + set { this.secret = value; } + } + /// + /// Get/Set the digest alogrithm when using challenge/response.
+ /// The digest algorithm is used to create the key based on the challenge and + /// the user's password.
+ /// Currently Asterisk supports only "MD5". + ///
+ public string AuthType + { + get { return this.authType; } + set { this.authType = value; } + } + /// + /// Get/Set the key. + /// + public string Key + { + get { return this.key; } + set { this.key = value; } + } + /// + /// Get/Set the event mask.
+ /// 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. + ///
+ public string Events + { + get { return this.events; } + set { this.events = value; } + } + + /// + /// Creates a new empty LoginAction. + /// + public LoginAction() + { + } + + /// + /// Creates a new LoginAction that performs a cleartext login.
+ /// You should not use cleartext login if you are concerned about security and login with a password hash instead. + ///
+ /// the username as configured in Asterisk's manager.conf + /// the user's password as configured in Asterisk's manager.conf + /// + public LoginAction(string username, string secret) + { + this.username = username; + this.secret = secret; + } + + /// + /// Creates a new LoginAction that performs a login via challenge/response. + /// + /// the username as configured in Asterisk's manager.conf + /// the digest alogrithm, must match the digest algorithm that was used with the corresponding ChallengeAction. + /// the hash of the user's password and the challenge + public LoginAction(string username, string authType, string key) + { + this.username = username; + this.authType = authType; + this.key = key; + } + + /// + /// Creates a new LoginAction that performs a login via challenge/response. + /// + /// the username as configured in Asterisk's manager.conf + /// the digest alogrithm, must match the digest algorithm that was used with the corresponding ChallengeAction. + /// the hash of the user's password and the challenge + /// the event mask.
+ /// 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. + /// + public LoginAction(string username, string authType, string key, string events) + { + this.username = username; + this.authType = authType; + this.key = key; + this.events = events; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/LogoffAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/LogoffAction.cs new file mode 100644 index 0000000..8effecc --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/LogoffAction.cs @@ -0,0 +1,25 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The LogoffAction causes the server to close the connection. + /// + public class LogoffAction : ManagerAction + { + /// + /// Get the name of this action, i.e. "Logoff". + /// + override public string Action + { + get { return "Logoff"; } + } + + /// + /// Creates a new LogoffAction. + /// + public LogoffAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxCountAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxCountAction.cs new file mode 100644 index 0000000..25b3a92 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxCountAction.cs @@ -0,0 +1,54 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The MailboxCountAction queries the number of unread and read messages in a mailbox.
+ /// The MailboxCountAction returns a MailboxStatusResponse. + ///
+ /// + public class MailboxCountAction : ManagerAction + { + private string mailbox; + + /// + /// Get the name of this action, i.e. "MailboxCount". + /// + override public string Action + { + get { return "MailboxCount"; } + } + /// + /// Get/Set the name of the mailbox to query.
+ /// 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.
+ /// This property is mandatory. + ///
+ public string Mailbox + { + get { return this.mailbox; } + set { this.mailbox = value; } + } + + /// + /// Creates a new empty MailboxCountAction. + /// + public MailboxCountAction() + { + } + + /// + /// Creates a new MailboxCountAction that queries the number of unread and + /// read messages in the given mailbox. + /// + /// the name of the mailbox to query.
+ /// 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. + /// + public MailboxCountAction(string mailbox) + { + this.mailbox = mailbox; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxStatusAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxStatusAction.cs new file mode 100644 index 0000000..746338e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/MailboxStatusAction.cs @@ -0,0 +1,57 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The MailboxStatusAction checks if a mailbox contains waiting messages.
+ /// The MailboxStatusAction returns a MailboxStatusResponse. + ///
+ /// + public class MailboxStatusAction : ManagerAction + { + private string mailbox; + + /// + /// Get the name of this action, i.e. "MailboxStatus". + /// + override public string Action + { + get { return "MailboxStatus"; } + } + /// + /// Get/Set the name of the mailbox to query.
+ /// 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.
+ /// Multiple mailboxes may be given, separated by ','. In this case the + /// action checks whether at least one of the given mailboxes has waiting + /// messages.
+ /// This property is mandatory.
+ /// Example: "1234,1235@mycontext" + ///
+ public string Mailbox + { + get { return this.mailbox; } + set { this.mailbox = value; } + } + + /// + /// Creates a new empty MailboxStatusAction. + /// + public MailboxStatusAction() + { + } + + /// + /// Creates a new MailboxStatusAction that checks for waiting messages in the given mailbox. + /// + /// the name of the mailbox to check.
+ /// 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. + /// + public MailboxStatusAction(string mailbox) + { + this.mailbox = mailbox; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerAction.cs new file mode 100644 index 0000000..8d27a53 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerAction.cs @@ -0,0 +1,66 @@ +using System.Text; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// This class implements the ManagerAction interface + /// and can serve as base class for your concrete Action implementations. + /// + public abstract class ManagerAction + { + private string actionId; + private string server; + private string proxyKey; + + /// + /// Manager API Action key. Also use as ProxyAction key to ProxyAction actions. + /// + public abstract string Action + { + get; + } + + #region ActionId + public string ActionId + { + get { return this.actionId; } + set { this.actionId = value; } + } + #endregion + + #region Server + /// + /// Specify a server to which to send your commands (x.x.x.x or hostname).
+ /// 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. + ///
+ public string Server + { + get { return this.server; } + set { this.server = value; } + } + #endregion + + #region ProxyKey + /// + /// You can use this as a simple authentication mechanism.
+ /// Rather than have to login with a username & password, + /// you can specify a ProxyKey that must be passed from + /// a client before requests are processed.
+ /// This is helpful in situations where you would like to authenticate and + /// execute an action in a single step. + ///
+ public virtual string ProxyKey + { + get { return this.proxyKey; } + set { this.proxyKey = value; } + } + #endregion + + public override string ToString() + { + return Helper.ToString(this); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionEvent.cs new file mode 100644 index 0000000..09b524d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionEvent.cs @@ -0,0 +1,20 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ManagerActionEvent is implemented by ManagerActions that + /// return their result not in a ManagerResponse but by sending a series of events.
+ /// The event type that indicates that Asterisk is finished is returned by the + /// ActionCompleteEventClass property. + ///
+ /// + public abstract class ManagerActionEvent : ManagerAction + { + /// + /// Returns the event type that indicates that Asterisk is finished sending response events for this action. + /// + /// + public abstract Type ActionCompleteEventClass(); + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionResponse.cs new file mode 100644 index 0000000..477280e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ManagerActionResponse.cs @@ -0,0 +1,21 @@ +using System; +using Asterisk.NET.Manager.Response; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ManagerActionResponse is implemented by ManagerActions that + /// return their result in a custom ManagerResponse
+ /// The response type that indicates that Asterisk is finished is returned by the + /// ActionCompleteResponseClass property. + ///
+ /// + public abstract class ManagerActionResponse : ManagerAction + { + /// + /// Returns the response type that indicates that Asterisk is finished sending response for this action. + /// + /// + public abstract object ActionCompleteResponseClass(); + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/MonitorAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/MonitorAction.cs new file mode 100644 index 0000000..1cf3751 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/MonitorAction.cs @@ -0,0 +1,130 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The MonitorAction starts monitoring (recording) a channel.
+ /// It is implemented in res/res_monitor.c + ///
+ public class MonitorAction : ManagerAction + { + private string channel; + private string file; + private string format; + private bool mix; + + #region Action + /// + /// Get the name of this action, i.e. "Monitor". + /// + override public string Action + { + get { return "Monitor"; } + } + #endregion + + #region Channel + /// + /// Get/Set the name of the channel to monitor.
+ /// This property is mandatory. + ///
+ public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + #endregion + + #region File + /// + /// Get/Set the name of the file to which the voice data is written.
+ /// If this property is not set it defaults to to the channel name as per CLI with the '/' replaced by '-'. + ///
+ public string File + { + get { return this.file; } + set { this.file = value; } + } + #endregion + + #region Format + /// + /// Get/Set the format to use for encoding the voice files.
+ /// If this property is not set it defaults to "wav". + ///
+ public string Format + { + get { return this.format; } + set { this.format = value; } + } + #endregion + + #region Mix + /// + /// Returns true if the two voice files should be joined at the end of the call. + /// + public bool Mix + { + get { return this.mix; } + set { this.mix = value; } + } + #endregion + + #region MonitorAction() + /// + /// Creates a new empty MonitorAction. + /// + public MonitorAction() + { + } + #endregion + + #region MonitorAction(string channel, string file) + /// + /// Creates a new MonitorAction that starts monitoring the given channel and + /// writes voice data to the given file(s). + /// + /// the name of the channel to monitor + /// the (base) name of the file(s) to which the voice data is written + public MonitorAction(string channel, string file) + { + this.channel = channel; + this.file = file; + } + #endregion + + #region MonitorAction(string channel, string file) + /// + /// Creates a new MonitorAction that starts monitoring the given channel and + /// writes voice data to the given file(s). + /// + /// the name of the channel to monitor + /// the (base) name of the file(s) to which the voice data is written + /// the format to use for encoding the voice files + 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) + /// + /// Creates a new MonitorAction that starts monitoring the given channel and + /// writes voice data to the given file(s). + /// + /// the name of the channel to monitor + /// the (base) name of the file(s) to which the voice data is written + /// the format to use for encoding the voice files + /// true if the two voice files should be joined at the end of the call + public MonitorAction(string channel, string file, string format, bool mix) + { + this.channel = channel; + this.file = file; + this.format = format; + this.mix = mix; + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/OriginateAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/OriginateAction.cs new file mode 100644 index 0000000..de2e883 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/OriginateAction.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// 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.
+ /// 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.
+ /// 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.
+ /// If you set async to true Asterisk reports an OriginateSuccess- + /// and OriginateFailureEvents. The action id of these events equals the action + /// id of this OriginateAction. + ///
+ /// + /// + public class OriginateAction : ManagerActionEvent + { + private string channel; + private string exten; + private string context; + private int priority; + private int timeout; + private string callerId; + private Dictionary variables; + private string account; + private string application; + private string data; + private bool async; + + #region Action + /// + /// Get the name of this action, i.e. "Originate". + /// + override public string Action + { + get { return "Originate"; } + } + #endregion + + #region Account + /// + /// 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. + /// + public string Account + { + get { return account; } + set { this.account = value; } + } + #endregion + + #region CallerId + /// + /// Get/Set the caller id to set on the outgoing channel. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + #endregion + + #region Channel + /// + /// Get/Set Channel on which to originate the call (The same as you specify in the Dial application command)
+ /// This property is required. + ///
+ public string Channel + { + get { return channel; } + set { this.channel = value; } + } + #endregion + + #region Context + /// + /// 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. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + #endregion + + #region Exten + /// + /// Get/Ser the extension to connect to. + /// If you set the extension you also have to set the context and priority properties. + /// + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + #endregion + + #region Priority + /// + /// 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. + /// + public int Priority + { + get { return priority; } + set { this.priority = value; } + } + #endregion + + #region Application + /// + /// Get/Set Application to use on connect (use Data for parameters) + /// + public string Application + { + get { return application; } + set { this.application = value; } + } + #endregion + + #region Data + /// + /// Get/Set the parameters to pass to the application. + /// Data if Application parameter is user + /// + /// Sets the parameters to pass to the application. + public string Data + { + get { return data; } + set { this.data = value; } + } + #endregion + + #region Async + /// + /// Get/Set true if this is a fast origination.
+ /// For the origination to be asynchronous (allows multiple calls to be generated without waiting for a response).
+ /// Will send OriginateSuccess- and OriginateFailureEvents. + ///
+ public bool Async + { + get { return async; } + set { this.async = value; } + } + #endregion + + #region ActionCompleteEventClass + public override Type ActionCompleteEventClass() + { + return typeof(Event.OriginateResponseEvent); + } + #endregion + + #region Timeout + /// + /// Get/Set the timeout for the origination in seconds.
+ /// The channel must be answered within this time, otherwise the origination + /// is considered to have failed and an OriginateFailureEvent is generated.
+ /// If not set, Asterisk assumes a default value of 30000 meaning 30 seconds. + ///
+ public int Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + #endregion + + #region Variable + /// + /// Get/Set the variables to set on the originated call.
+ /// Variable assignments are of the form "VARNAME=VALUE". You can specify + /// multiple variable assignments separated by the '|' character.
+ /// Example: "VAR1=abc|VAR2=def" sets the channel variables VAR1 to "abc" and VAR2 to "def". + ///
+ public string Variable + { + get { return Helper.JoinVariables(variables, Common.VAR_DELIMITER, "="); } + set { variables = Helper.ParseVariables(variables, value, Common.VAR_DELIMITER); } + } + #endregion + + #region GetVariables() + /// + /// Get the variables dictionary to set on the originated call. + /// + public Dictionary GetVariables() + { + return variables; + } + #endregion + + #region SetVariables(IDictionary vars) + /// + /// Set the variables dictionary to set on the originated call. + /// + public void SetVariables(Dictionary vars) + { + this.variables = vars; + } + #endregion + + #region GetVariable(string name, string val) + /// + /// Gets a variable on the originated call. Replaces any existing variable with the same name. + /// + public string GetVariable(string key) + { + if (variables == null) + return string.Empty; + return variables[key]; + } + #endregion + + #region SetVariable(string name, string val) + /// + /// Sets a variable dictionary on the originated call. Replaces any existing variable with the same name. + /// + public void SetVariable(string key, string value) + { + if (variables == null) + variables = new Dictionary(); + if (variables.ContainsKey(key)) + variables[key] = value; + else + variables.Add(key, value); + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ParkedCallsAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ParkedCallsAction.cs new file mode 100644 index 0000000..43118e5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ParkedCallsAction.cs @@ -0,0 +1,32 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ParkedCallsAction requests a list of all currently parked calls.
+ /// For each active channel a ParkedCallEvent is generated. After all parked + /// calls have been reported a ParkedCallsCompleteEvent is generated. + ///
+ /// + /// + public class ParkedCallsAction : ManagerActionEvent + { + /// Get the name of this action, i.e. "ParkedCalls". + public override string Action + { + get { return "ParkedCalls"; } + } + + public override Type ActionCompleteEventClass() + { + return typeof(Event.ParkedCallsCompleteEvent); + } + + /// + /// Creates a new ParkedCallsAction. + /// + public ParkedCallsAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/PingAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/PingAction.cs new file mode 100644 index 0000000..25223f0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/PingAction.cs @@ -0,0 +1,26 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The PingAction will ellicit a 'Pong' response, it is used to keep the manager + /// connection open and performs no operation. + /// + public class PingAction : ManagerAction + { + /// + /// Get the name of this action, i.e. "Ping". + /// + override public string Action + { + get { return "Ping"; } + } + + /// + /// Creates a new PingAction. + /// + public PingAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ProxyAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ProxyAction.cs new file mode 100644 index 0000000..9759dd8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ProxyAction.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Action +{ + public abstract class ProxyAction : ManagerAction + { + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/QueueAddAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueAddAction.cs new file mode 100644 index 0000000..4cd9065 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueAddAction.cs @@ -0,0 +1,120 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The QueueAddAction adds a new member to a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ public class QueueAddAction : ManagerAction + { + private string queue; + private string iface; + private string memberName; + private int penalty; + private bool paused; + + /// + /// Get the name of this action, i.e. "QueueAdd". + /// + override public string Action + { + get { return "QueueAdd"; } + } + /// + /// Get/Set the name of the queue the new member will be added to.
+ /// This property is mandatory. + ///
+ public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + /// + /// Get/Set the interface to add. To add a specific channel just use the channel name, e.g. "SIP/1234".
+ /// This property is mandatory. + ///
+ public string Interface + { + get { return this.iface; } + set { this.iface = value; } + } + /// + /// Get/Set the member to add. + /// + public string MemberName + { + get { return this.memberName; } + set { this.memberName = value; } + } + /// + /// Get/Set the penalty for this member.
+ /// The penalty must be a positive integer or 0 for no penalty. If it is not set 0 is assumed.
+ /// When calls are distributed members with higher penalties are considered last. + ///
+ public int Penalty + { + get { return this.penalty; } + set { this.penalty = value; } + } + /// + /// Get/Set if the queue member should be paused when added.
+ /// true if the queue member should be paused when added. + ///
+ public bool Paused + { + get { return this.paused; } + set { this.paused = value; } + } + + /// + /// Creates a new empty QueueAddAction. + /// + public QueueAddAction() + { + } + + /// + /// Creates a new QueueAddAction that adds a new member on the given interface to the given queue. + /// + /// the name of the queue the new member will be added to + /// Sets the interface to add. To add a specific channel just use the channel name, e.g. "SIP/1234". + public QueueAddAction(string queue, string iface) + { + this.queue = queue; + this.iface = iface; + } + + /// + /// Creates a new QueueAddAction that adds a new member on the given interface to the given queue. + /// + /// the name of the queue the new member will be added to + /// Sets the interface to add. To add a specific channel just use the channel name, e.g. "SIP/1234". + /// the name of the the new member will be added to + public QueueAddAction(string queue, string iface, string memberName) + { + this.queue = queue; + this.iface = iface; + this.memberName = memberName; + } + + /// + /// Creates a new QueueAddAction that adds a new member on the given + /// interface to the given queue with the given penalty. + /// + /// the name of the queue the new member will be added to + /// Sets the interface to add. To add a specific channel just use the channel name, e.g. "SIP/1234". + /// the name of the the new member will be added to + /// the penalty for this member.
+ /// The penalty must be a positive integer or 0 for no penalty. When calls are + /// distributed members with higher penalties are considered last. + /// + public QueueAddAction(string queue, string iface, string memberName, int penalty) + { + this.queue = queue; + this.iface = iface; + this.memberName = memberName; + this.penalty = penalty; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/QueuePauseAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/QueuePauseAction.cs new file mode 100644 index 0000000..360c765 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/QueuePauseAction.cs @@ -0,0 +1,112 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The QueuePauseAction makes a queue member temporarily unavailabe (or available again).
+ /// It is implemented in apps/app_queue.c
+ /// Available since Asterisk 1.2. + ///
+ public class QueuePauseAction : ManagerAction + { + private string iface; + private bool paused; + private string queue; + + /// + /// Get the name of this action, i.e. "QueuePause". + /// + override public string Action + { + get + { + return "QueuePause"; + } + + } + /// + /// Get/Set the interface of the member to make available or unavailable.
+ /// This property is mandatory. + ///
+ public string Interface + { + get { return this.iface; } + set { this.iface = value; } + } + /// + /// Get/Set Returns the name of the queue the member is made available or unavailable on. + /// + public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + /// + /// Get/Set if the member is made available or unavailable.
+ /// true to make the member unavailbale,
+ /// false make the member available + ///
+ public bool Paused + { + get { return this.paused; } + set { this.paused = value; } + } + + /// + /// Creates a new empty QueuePauseAction. + /// + public QueuePauseAction() + { + } + + /// + /// Creates a new QueuePauseAction that makes the member on the given + /// interface unavailable on all queues. + /// + /// the interface of the member to make unavailable + public QueuePauseAction(string iface) + { + this.iface = iface; + this.paused = true; + } + + /// + /// Creates a new QueuePauseAction that makes the member on the given + /// interface unavailable on the given queue. + /// + /// the interface of the member to make unavailable + /// the queue the member is made unvailable on + public QueuePauseAction(string iface, string queue) + { + this.iface = iface; + this.queue = queue; + this.paused = true; + } + + /// + /// Creates a new QueuePauseAction that makes the member on the given + /// interface available or unavailable on all queues. + /// + /// the interface of the member to make unavailable + /// true to make the member unavailbale, false to make the member available + public QueuePauseAction(string iface, bool paused) + { + this.iface = iface; + this.paused = paused; + } + + /// + /// Creates a new QueuePauseAction that makes the member on the given + /// interface unavailable on the given queue. + /// + /// the interface of the member to make unavailable + /// the queue the member is made unvailable on + /// true to make the member unavailbale, false to make the member available + public QueuePauseAction(string iface, string queue, bool paused) + { + this.iface = iface; + this.queue = queue; + this.paused = paused; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/QueueRemoveAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueRemoveAction.cs new file mode 100644 index 0000000..c695d56 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueRemoveAction.cs @@ -0,0 +1,62 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The QueueRemoveAction removes a member from a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ public class QueueRemoveAction : ManagerAction + { + /// The name of the queue the member will be removed from. + private string queue; + private string iface; + + /// + /// Get the name of this action, i.e. "QueueRemove". + /// + override public string Action + { + get + { + return "QueueRemove"; + } + + } + /// + /// Get/Set the name of the queue the member will be removed from. + /// + public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + /// + /// Get/Set the interface to remove.
+ /// This property is mandatory. + ///
+ public string Interface + { + get { return this.iface; } + set { this.iface = value; } + } + + /// + /// Creates a new empty QueueRemoveAction. + /// + public QueueRemoveAction() + { + } + + /// + /// Creates a new QueueRemoveAction that removes the member on the given interface from the given queue. + /// + /// the name of the queue the member will be removed from + /// the interface of the member to remove + public QueueRemoveAction(string queue, string iface) + { + this.queue = queue; + this.iface = iface; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/QueueStatusAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueStatusAction.cs new file mode 100644 index 0000000..d0a1dc8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/QueueStatusAction.cs @@ -0,0 +1,65 @@ +using System; +namespace Asterisk.NET.Manager.Action +{ + /// + /// The QueueStatusAction requests the state of all defined queues their members (agents) and entries (callers).
+ /// For each queue a QueueParamsEvent is generated, followed by a + /// QueueMemberEvent for each member of that queue and a QueueEntryEvent for each + /// entry in the queue.
+ /// Since Asterisk 1.2 a QueueStatusCompleteEvent is sent to denote the end of the generated dump.
+ /// This action is implemented in apps/app_queue.c + ///
+ /// + /// + /// + /// + public class QueueStatusAction : ManagerActionEvent + { + private string queue; + private string member; + + #region Action + /// + /// Get the name of this action, i.e. "QueueStatus". + /// + public override string Action + { + get { return "QueueStatus"; } + } + #endregion + #region Queue + /// + /// Get/Set the queue filter. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + #endregion + #region Member + /// + /// Get/Set the member filter. + /// + public string Member + { + get { return member; } + set { this.member = value; } + } + #endregion + + #region ActionCompleteEventClass + public override Type ActionCompleteEventClass() + { + return typeof(Event.QueueStatusCompleteEvent); + } + #endregion + + #region QueueStatusAction() + /// Creates a new QueueStatusAction. + public QueueStatusAction() + { + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/RedirectAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/RedirectAction.cs new file mode 100644 index 0000000..0b024c0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/RedirectAction.cs @@ -0,0 +1,102 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// Redirects a given channel (and an optional additional channel) to a new extension. + /// + public class RedirectAction : ManagerAction + { + private string channel; + private string extraChannel; + private string exten; + private string context; + private int priority; + + /// + /// Get the name of this action, i.e. "Redirect". + /// + override public string Action + { + get { return "Redirect"; } + } + /// + /// Get/Set name of the channel to redirect. + public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + /// + /// Get/Set the name of the additional channel to redirect. + /// + public string ExtraChannel + { + get { return this.extraChannel; } + set { this.extraChannel = value; } + } + /// + /// Get/Set the destination context. + /// + public string Context + { + get { return this.context; } + set { this.context = value; } + } + /// + /// Get/Set the destination extension. + /// + public string Exten + { + get { return this.exten; } + set { this.exten = value; } + } + /// + /// Get/Set the destination priority. + /// + public int Priority + { + get { return this.priority; } + set { this.priority = value; } + } + + /// + /// Creates a new empty RedirectAction. + /// + public RedirectAction() + { + } + + /// + /// Creates a new RedirectAction that redirects the given channel to the given context, extension, priority triple. + /// + /// the name of the channel to redirect + /// the destination context + /// the destination extension + /// the destination priority + public RedirectAction(string channel, string context, string exten, int priority) + { + this.channel = channel; + this.context = context; + this.exten = exten; + this.priority = priority; + } + + /// + /// Creates a new RedirectAction that redirects the given channels to the given context, extension, priority triple. + /// + /// the name of the first channel to redirect + /// the name of the second channel to redirect + /// the destination context + /// the destination extension + /// the destination priority + public RedirectAction(string channel, string extraChannel, string context, string exten, int priority) + { + this.channel = channel; + this.extraChannel = extraChannel; + this.context = context; + this.exten = exten; + this.priority = priority; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/SIPPeersAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/SIPPeersAction.cs new file mode 100644 index 0000000..af2b64e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/SIPPeersAction.cs @@ -0,0 +1,31 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// Retrieves a list of all defined SIP peers.
+ /// For each peer that is found a PeerEntryEvent is sent by Asterisk containing + /// the details. When all peers have been reported a PeerlistCompleteEvent is sent.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// + public class SIPPeersAction : ManagerActionEvent + { + public override string Action + { + get { return "SIPPeers"; } + } + public override Type ActionCompleteEventClass() + { + return typeof(Event.PeerlistCompleteEvent); + } + + /// + /// Creates a new SIPPeersAction. + /// + public SIPPeersAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/SIPShowPeerAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/SIPShowPeerAction.cs new file mode 100644 index 0000000..ec8be08 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/SIPShowPeerAction.cs @@ -0,0 +1,48 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// Retrieves a the details about a given SIP peer.
+ /// For a PeerEntryEvent is sent by Asterisk containing the details of the peer + /// followed by a PeerlistCompleteEvent.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// + public class SIPShowPeerAction : ManagerActionEvent + { + private string peer; + + override public string Action + { + get { return "SIPShowPeer"; } + } + /// + /// Get/Set the name of the peer to retrieve.
+ /// This parameter is mandatory. + ///
+ public string Peer + { + get { return this.peer; } + set { this.peer = value; } + } + public override Type ActionCompleteEventClass() + { + return typeof(Event.PeerlistCompleteEvent); + } + + /// Creates a new empty SIPShowPeerAction. + public SIPShowPeerAction() + { + } + + /// + /// Creates a new SIPShowPeerAction that requests the details about the given SIP peer. + /// + public SIPShowPeerAction(string peer) + { + this.peer = peer; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/SetCDRUserFieldAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/SetCDRUserFieldAction.cs new file mode 100644 index 0000000..fe8e052 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/SetCDRUserFieldAction.cs @@ -0,0 +1,81 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The SetCDRUserFieldAction causes the user field of the call detail record for the given channel to be changed.
+ /// Depending on the value of the append property the value is appended or overwritten.
+ /// The SetCDRUserFieldAction is implemented in apps/app_setcdruserfield.c + ///
+ public class SetCDRUserFieldAction : ManagerAction + { + private string channel; + private string userField; + private bool append; + + /// + /// Get the name of the action, i.e. "SetCDRUserField". + /// + override public string Action + { + get { return "SetCDRUserField"; } + } + /// + /// Get/Set the name of the channel to set the cdr user field on.
+ /// This property is mandatory. + ///
+ public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + /// + /// Get/Set the value of the cdr user field to set or append.
+ /// This property is mandatory. + ///
+ public string UserField + { + get { return this.userField; } + set { this.userField = value; } + } + /// + /// Get/Set if the value of the cdr user field is appended or overwritten.
+ /// true to append the value to the cdr user field or false to overwrite. + ///
+ public bool Append + { + get { return this.append; } + set { this.append = value; } + } + /// + /// Creates a new empty SetCDRUserFieldAction. + /// + public SetCDRUserFieldAction() + { + } + + /// + /// Creates a new SetCDRUserFieldAction that sets the user field of the call detail record for the given channel to the given value. + /// + /// the name of the channel + /// the new value of the userfield + public SetCDRUserFieldAction(string channel, string userField) + { + this.channel = channel; + this.userField = userField; + } + + /// + /// Creates a new SetCDRUserFieldAction that sets the user field of the call detail record for the given channel to the given value. + /// + /// the name of the channel + /// the new value of the userfield + /// true to append the value to the cdr user field or false to overwrite + public SetCDRUserFieldAction(string channel, string userField, bool append) + { + this.channel = channel; + this.userField = userField; + this.append = append; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/SetVarAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/SetVarAction.cs new file mode 100644 index 0000000..8fb9520 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/SetVarAction.cs @@ -0,0 +1,81 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The SetVar action sets the value of a channel variable for a given channel. + /// + public class SetVarAction : ManagerAction + { + /// The channel on which to set the variable. + public string channel; + /// The name of the variable to set. + public string varName; + /// The value to store. + public string varValue; + + /// + /// Get the name of this action, i.e. "SetVar". + /// + override public string Action + { + get { return "SetVar"; } + } + /// + /// Get/Set the name of the channel. + /// + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + /// + /// Get/Set the name of the variable to set. + /// + public string Variable + { + get { return this.varName; } + set { this.varName = value; } + } + /// + /// Get/Set the value to store. + /// + public string Value + { + get { return this.varValue; } + set { this.varValue = value; } + } + + /// + /// Creates a new empty SetVarAction. + /// + public SetVarAction() + { + } + + /// + /// Creates a new SetVarAction that sets the given global variable to a new value. + /// + /// the name of the global variable to set + /// the new value + public SetVarAction(string variable, string value) + { + this.varName = variable; + this.varValue = value; + } + + /// + /// Creates a new SetVarAction that sets the given channel variable of the + /// given channel to a new value. + /// + /// the name of the channel to set the variable on + /// the name of the channel variable + /// the new value + public SetVarAction(string channel, string variable, string value) + { + this.channel = channel; + this.varName = variable; + this.varValue = value; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/StatusAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/StatusAction.cs new file mode 100644 index 0000000..a571bd2 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/StatusAction.cs @@ -0,0 +1,33 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The StatusAction requests the state of all active channels.
+ /// For each active channel a StatusEvent is generated. After the state of all + /// channels has been reported a StatusCompleteEvent is generated. + ///
+ /// + /// + public class StatusAction : ManagerActionEvent + { + /// + /// Get the name of this action, i.e. "Status". + /// + public override string Action + { + get { return "Status"; } + } + public override Type ActionCompleteEventClass() + { + return typeof(Event.StatusCompleteEvent); + } + + /// + /// Creates a new StatusAction. + /// + public StatusAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/StopMonitorAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/StopMonitorAction.cs new file mode 100644 index 0000000..13c9b90 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/StopMonitorAction.cs @@ -0,0 +1,53 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The StopMonitorAction ends monitoring (recording) a channel.
+ /// It is implemented in res/res_monitor.c + ///
+ public class StopMonitorAction : ManagerAction + { + /// The name of the channel to end monitoring. + private string channel; + + #region Action + /// + /// Get the name of this action, i.e. "StopMonitor". + /// + override public string Action + { + get { return "StopMonitor"; } + } + #endregion + #region Channel + /// + /// Get/Set the name of the channel to end monitoring.
+ /// This property is mandatory. + ///
+ public string Channel + { + get { return this.channel; } + set { this.channel = value; } + } + #endregion + + #region StopMonitorAction() + /// + /// Creates a new empty StopMonitorAction. + /// + public StopMonitorAction() + { + } + #endregion + #region StopMonitorAction(string channel) + /// + /// Creates a new StopMonitorAction that ends monitoring of the given channel. + /// + public StopMonitorAction(string channel) + { + this.channel = channel; + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/UpdateConfigAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/UpdateConfigAction.cs new file mode 100644 index 0000000..50e7ac6 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/UpdateConfigAction.cs @@ -0,0 +1,197 @@ +using System; +using Asterisk.NET.Manager.Response; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The UpdateConfigAction sends an UpdateConfig command to the asterisk server. + /// Please take note that unlike the manager documentation, this command does not + /// dump back the config file upon success -- it only tells you it succeeded. You + /// should use the handy addCommand method this class provides for specifying + /// what actions you would like to take on the configuration file. It will + /// generate appropriate sequence numbers for the command. You may use the static + /// ACTION_* fields provided by this action to specify what action you would like + /// to take, while avoiding handling the strings required. Plain fields:
+ /// SrcFilename: Configuration filename to read(e.g. foo.conf)
+ /// DstFilename: Configuration filename to write(e.g. foo.conf)
+ /// Reload: Whether or not a reload should take place (or name of specific module)
+ /// Repeatable fields:
+ /// Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)
+ /// Cat-XXXXXX: Category to operate on
+ /// Var-XXXXXX: Variable to work on
+ /// Value-XXXXXX: Value to work on
+ /// Match-XXXXXX: Extra match required to match line + ///
+ public class UpdateConfigAction : ManagerActionResponse + { + public const string ACTION_NEWCAT = "newcat"; + public const string ACTION_RENAMECAT = "renamecat"; + public const string ACTION_DELCAT = "delcat"; + public const string ACTION_UPDATE = "update"; + public const string ACTION_DELETE = "delete"; + public const string ACTION_APPEND = "append"; + + private string reload; + private string srcFileName; + private string dstFileName; + private int actionCounter; + private Dictionary actions; + + /// + /// Creates a new UpdateConfigAction. + /// + public UpdateConfigAction() + : base() + { + actionCounter = 0; + actions = new Dictionary(); + } + + /// + /// Creates a new UpdateConfigAction. + /// + public UpdateConfigAction(string srcFilename, string dstFilename, string reload) + : this() + { + this.srcFileName = srcFilename; + this.dstFileName = dstFilename; + this.reload = reload; + } + + /// + /// Creates a new UpdateConfigAction. + /// + public UpdateConfigAction(string srcFilename, string dstFilename, bool reload) + : this() + { + this.srcFileName = srcFilename; + this.dstFileName = dstFilename; + this.reload = (reload ? "true" : ""); + } + + /// + /// Creates a new UpdateConfigAction. + /// + public UpdateConfigAction(string srcFilename, string dstFilename) + : this() + { + this.srcFileName = srcFilename; + this.dstFileName = dstFilename; + this.reload = ""; + } + + /// + /// Get/Set the destination filename. + /// + public string DstFileName + { + get { return dstFileName; } + set { dstFileName = value; } + } + + /// + /// Get/Set the source filename. + /// + public string SrcFileName + { + get { return srcFileName; } + set { srcFileName = value; } + } + + /// + /// Get/Set the reload behavior of this action (yes), or sets a specific module (name) to be reloaded.
+ /// Set to empty string to update without reload. + ///
+ public string Reload + { + get { return reload; } + set { reload = value; } + } + + /// + /// Get the name of this action. + /// + public override string Action + { + get { return "UpdateConfig"; } + } + + #region AddCommand(...) + /// + /// Adds a command to update a config file while sparing you the details of + /// the Manager's required syntax. If you want to omit one of the command's + /// sections, provide a null value to this method. The command index will be + /// incremented even if you supply a null for all parameters, though the action + /// will be unaffected. + /// + /// Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append) + /// Category to operate on + /// Variable to work on + /// Value to work on + /// Extra match required to match line + public void AddCommand(string action, string category, string variable, string value, string match) + { + int i = actionCounter++; + + if (!string.IsNullOrEmpty(action)) + actions.Add("Action-" + i, action); + + if (!string.IsNullOrEmpty(category)) + actions.Add("Cat-" + i, category); + + if (!string.IsNullOrEmpty(variable)) + actions.Add("Var-" + i, variable); + + if (!string.IsNullOrEmpty(value)) + actions.Add("Value-" + i, value); + + if (!string.IsNullOrEmpty(match)) + actions.Add("Match-" + i, match); + } + + public void AddCommand(string action, string category, string variable, string value) + { + AddCommand(action, category, variable, value, null); + } + + public void AddCommand(string action, string category, string variable) + { + AddCommand(action, category, variable, null, null); + } + + public void AddCommand(string action, string category) + { + AddCommand(action, category, null, null, null); + } + + public void AddCommand(string action) + { + AddCommand(action, null, null, null, null); + } + + public void AddCommand() + { + AddCommand(null, null, null, null, null); + } + #endregion + + #region Actions + /// + /// Dictionary of the action's desired operations where Map keys contain:
+ /// action,cat,var,value,match pairs followed by -XXXXXX, and the values contain the values for those keys. + /// This method will typically only be used by the ActionBuilder to generate the actual strings to be sent to the manager interface. + ///
+ public Dictionary Actions + { + get { return actions; } + } + #endregion + + public override object ActionCompleteResponseClass() + { + return new ManagerResponse(); + } + + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOffAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOffAction.cs new file mode 100644 index 0000000..833061a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOffAction.cs @@ -0,0 +1,42 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapDNDOnAction switches a zap channel "Do Not Disturb" status off. + /// + public class ZapDNDOffAction : ManagerAction + { + private int zapChannel; + + /// + /// Get the name of this action, i.e. "ZapDNDOff". + /// + override public string Action + { + get { return "ZapDNDOff"; } + } + /// + /// Get/Set the number of the zap channel to switch to dnd off.
+ /// This property is mandatory. + ///
+ public int ZapChannel + { + get { return this.zapChannel; } + set { this.zapChannel = value; } + } + /// + /// Creates a new empty ZapDNDOffAction. + /// + public ZapDNDOffAction() + { + } + /// + /// Creates a new ZapDNDOffAction that disables "Do Not Disturb" status for the given zap channel. + /// + public ZapDNDOffAction(int zapChannel) + { + this.zapChannel = zapChannel; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOnAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOnAction.cs new file mode 100644 index 0000000..34a2735 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDNDOnAction.cs @@ -0,0 +1,43 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapDNDOnAction switches a zap channel "Do Not Disturb" status on. + /// + public class ZapDNDOnAction : ManagerAction + { + private int zapChannel; + + /// + /// Get the name of this action, i.e. "ZapDNDOn". + /// + override public string Action + { + get { return "ZapDNDOn"; } + } + /// + /// Get/Set the number of the zap channel to switch to dnd on.
+ /// This property is mandatory. + ///
+ public int ZapChannel + { + get { return this.zapChannel; } + set { this.zapChannel = value; } + } + /// + /// Creates a new empty ZapDNDOnAction. + /// + public ZapDNDOnAction() + { + } + + /// + /// Creates a new ZapDNDOnAction that enables "Do Not Disturb" status for the given zap channel. + /// + public ZapDNDOnAction(int zapChannel) + { + this.zapChannel = zapChannel; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDialOffhookAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDialOffhookAction.cs new file mode 100644 index 0000000..f77e868 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapDialOffhookAction.cs @@ -0,0 +1,53 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapDialOffhookAction dials a number on a zap channel while offhook. + /// + public class ZapDialOffhookAction : ManagerAction + { + private int zapChannel; + private string number; + + /// + /// Get the name of this action, i.e. "ZapDialOffhook". + /// + override public string Action + { + get { return "ZapDialOffhook"; } + } + /// + /// Get/Set the number of the zap channel.
+ /// This property is mandatory. + ///
+ public int ZapChannel + { + get { return this.zapChannel; } + set { this.zapChannel = value; } + } + /// + /// Get/Set the number to dial.
+ /// This property is mandatory. + ///
+ public string Number + { + get { return this.number; } + set { this.number = value; } + } + + /// Creates a new empty ZapDialOffhookAction. + public ZapDialOffhookAction() + { + } + + /// + /// Creates a new ZapDialOffhookAction that dials the given number on the given zap channel. + /// + public ZapDialOffhookAction(int zapChannel, string number) + { + this.zapChannel = zapChannel; + this.number = number; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapHangupAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapHangupAction.cs new file mode 100644 index 0000000..ddd6241 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapHangupAction.cs @@ -0,0 +1,44 @@ +using System; + +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapHangupAction hangs up a zap channel. + /// + public class ZapHangupAction : ManagerAction + { + private int zapChannel; + + /// + /// Get the name of this action, i.e. "ZapHangup". + /// + override public string Action + { + get { return "ZapHangup"; } + } + /// + /// Get/Set the number of the zap channel to hangup.
+ /// This property is mandatory. + ///
+ public int ZapChannel + { + get { return this.zapChannel; } + set { this.zapChannel = value; } + } + + /// + /// Creates a new empty ZapHangupAction. + /// + public ZapHangupAction() + { + } + + /// + /// Creates a new ZapHangupAction that hangs up the given zap channel (the number of the zap channel to hang up). + /// + public ZapHangupAction(int zapChannel) + { + this.zapChannel = zapChannel; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapShowChannelsAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapShowChannelsAction.cs new file mode 100644 index 0000000..4ef0e34 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapShowChannelsAction.cs @@ -0,0 +1,32 @@ +using System; +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapShowChannelsAction requests the state of all zap channels.
+ /// For each zap channel a ZapShowChannelsEvent is generated. After all zap + /// channels have been listed a ZapShowChannelsCompleteEvent is generated. + ///
+ /// + /// + public class ZapShowChannelsAction : ManagerActionEvent + { + /// + /// Get the name of this action, i.e. "ZapShowChannels". + /// + public override string Action + { + get { return "ZapShowChannels"; } + } + public override Type ActionCompleteEventClass() + { + return typeof(Event.ZapShowChannelsCompleteEvent); + } + + /// + /// Creates a new ZapShowChannelsAction. + /// + public ZapShowChannelsAction() + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Action/ZapTransferAction.cs b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapTransferAction.cs new file mode 100644 index 0000000..7a0bffa --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Action/ZapTransferAction.cs @@ -0,0 +1,28 @@ +using System; +namespace Asterisk.NET.Manager.Action +{ + /// + /// The ZapTransferAction transfers a zap channel. + /// + public class ZapTransferAction : ManagerAction + { + private int zapChannel; + + /// + /// Get the name of this action, i.e. "ZapTransfer". + /// + override public string Action + { + get { return "ZapTransfer"; } + } + /// + /// Get/Set the number of the zap channel to transfer.
+ /// This property is mandatory. + ///
+ public int ZapChannel + { + get { return this.zapChannel; } + set { this.zapChannel = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/AsteriskVersion.cs b/Asterisk.2013/Asterisk.NET/Manager/AsteriskVersion.cs new file mode 100644 index 0000000..929df6c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/AsteriskVersion.cs @@ -0,0 +1,12 @@ +using System; + +namespace Asterisk.NET.Manager +{ + public enum AsteriskVersion + { + ASTERISK_1_0 = 10, + ASTERISK_1_2 = 12, + ASTERISK_1_4 = 14, + ASTERISK_1_6 = 16 + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AGIExecEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AGIExecEvent.cs new file mode 100644 index 0000000..b297761 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AGIExecEvent.cs @@ -0,0 +1,51 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + /// + /// AgiExecEvents are triggered when an AGI command is executed.
+ /// For each command two events are triggered: one before excution ("Start") and one after execution ("End"). + ///
+ public class AGIExecEvent : ManagerEvent + { + private string subEvent; + private long commandId; + private string command; + private int resultCode; + private string result; + + /// + /// Creates a new AGIExecEvent. + /// + public AGIExecEvent(ManagerConnection source) + : base(source) + { + } + + public long CommandId + { + get { return commandId; } + set { this.commandId = value; } + } + public string Command + { + get { return command; } + set { this.command = value; } + } + public string SubEvent + { + get { return subEvent; } + set { this.subEvent = value; } + } + public string Result + { + get { return result; } + set { this.result = value; } + } + public int ResultCode + { + get { return resultCode; } + set { this.resultCode = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentEvent.cs new file mode 100644 index 0000000..205d76a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentEvent.cs @@ -0,0 +1,42 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for several agent related events. + /// + public abstract class AbstractAgentEvent : AbstractAgentVariables + { + private string queue; + private string member; + private string memberName; + + /// + /// Get/Set the name of the queue. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + /// + /// Get/Set the name of the member's interface. + /// + public string Member + { + get { return member; } + set { this.member = value; } + } + + /// + /// Get/Set the name of the member's interface. + /// + public string MemberName + { + get { return memberName; } + set { this.memberName = value; } + } + + public AbstractAgentEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentVariables.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentVariables.cs new file mode 100644 index 0000000..d06e75f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractAgentVariables.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; + +namespace Asterisk.NET.Manager.Event +{ + public abstract class AbstractAgentVariables : ManagerEvent + { + private Dictionary variables; + + public AbstractAgentVariables(ManagerConnection source) + : base(source) + { } + + #region Variable + /// + /// Get/Set the variables to set on the queue call in native asterisk format.
+ /// Example: "VAR1=abc|VAR2=def". + ///
+ public string Variable + { + get { return Helper.JoinVariables(variables, Common.VAR_DELIMITER, "="); } + set { variables = Helper.ParseVariables(variables, value, Common.VAR_DELIMITER); } + } + #endregion + + #region GetVariables() + /// + /// Get the variables dictionary to set on the originated call. + /// + public IDictionary GetVariables() + { + return variables; + } + #endregion + + #region SetVariables(IDictionary vars) + /// + /// Set the variables dictionary to set on the originated call. + /// + public void SetVariables(Dictionary vars) + { + this.variables = vars; + } + #endregion + + #region SetVariable(string name, string val) + /// + /// Sets a variable dictionary on the originated call. Replaces any existing variable with the same name. + /// + public void SetVariable(string key, string val) + { + if (variables == null) + variables = new Dictionary(); + if (variables.ContainsKey(key)) + variables[key] = val; + else + variables.Add(key, val); + } + #endregion + + #region GetVariable(string name) + /// + /// Gets a variable on the originated call. Replaces any existing variable with the same name. + /// + public string GetVariable(string key) + { + if (variables == null) + return string.Empty; + return variables[key]; + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractChannelEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractChannelEvent.cs new file mode 100644 index 0000000..13ff0d1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractChannelEvent.cs @@ -0,0 +1,91 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class providing common properties for HangupEvent, NewChannelEvent and NewStateEvent. + /// + public abstract class AbstractChannelEvent : ManagerEvent + { + private string channelState; + private string channelStateDesc; + private string callerId; + private string callerIdNum; + private string callerIdName; + private string accountCode; + private string state; + + /// + /// Get/Set Channel State + /// + public string ChannelState + { + get { return this.channelState; } + set { this.channelState = value; } + } + + /// + /// Get/Set Channel State Description + /// + public string ChannelStateDesc + { + get { return this.channelStateDesc; } + set { this.channelStateDesc = value; } + } + + /// + /// Get/Set the Caller*ID of the channel if set or <unknown> if none has been set. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID of the channel if set or <unknown> if none has been set. + /// + public string CallerIdNum + { + get { return callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get/Set the Caller*ID Name of the channel if set or ≶unknown> if none has been set. + /// + public string CallerIdName + { + get { return callerIdName; } + set { this.callerIdName = value; } + } + /// + /// Get/Set the (new) state of the channel.
+ /// The following states are used:
+ ///
    + ///
  • Down
  • + ///
  • OffHook
  • + ///
  • Dialing
  • + ///
  • Ring
  • + ///
  • Ringing
  • + ///
  • Up
  • + ///
  • Busy
  • + ///
      + ///
+ public string State + { + get { return this.state; } + set { this.state = value; } + } + + /// + /// Get/Set channel AccountCode + /// + public string AccountCode + { + get { return this.accountCode; } + set { this.accountCode = value; } + } + + public AbstractChannelEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractMeetmeEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractMeetmeEvent.cs new file mode 100644 index 0000000..2eb87f4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractMeetmeEvent.cs @@ -0,0 +1,30 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class providing common properties for meet me (asterisk's conference system) events. + /// + public abstract class AbstractMeetmeEvent : ManagerEvent + { + private string meetMe; + private int userNum; + + /// + /// Get/Set the conference number. + /// + public string Meetme + { + get { return meetMe; } + set { this.meetMe = value; } + } + public int Usernum + { + get { return userNum; } + set { this.userNum = value; } + } + + public AbstractMeetmeEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractParkedCallEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractParkedCallEvent.cs new file mode 100644 index 0000000..9cbd3e0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractParkedCallEvent.cs @@ -0,0 +1,50 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for several call parking related events. + /// + public abstract class AbstractParkedCallEvent : ManagerEvent + { + private string exten; + private string callerId; + private string callerIdNum; + private string callerIdName; + + /// + /// Get/Set the extension the channel is or was parked at. + /// + public string Exten + { + get { return this.exten; } + set { this.exten = value; } + } + /// + /// Get/Set the Caller*ID number of the parked channel. + /// + public string CallerId + { + get { return this.callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID number of the parked channel. + /// + public string CallerIdNum + { + get { return this.callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get/Set the Caller*ID name of the parked channel. + /// + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + + public AbstractParkedCallEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractQueueMemberEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractQueueMemberEvent.cs new file mode 100644 index 0000000..8334e5d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AbstractQueueMemberEvent.cs @@ -0,0 +1,33 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for several queue member related events. + /// + public abstract class AbstractQueueMemberEvent : ManagerEvent + { + private string queue; + private string location; + + /// + /// Returns the name of the queue. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + /// + /// Returns the name of the member's interface.
+ /// E.g. the channel name or agent group. + ///
+ public string Location + { + get { return location; } + set { this.location = value; } + } + + public AbstractQueueMemberEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLoginEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLoginEvent.cs new file mode 100644 index 0000000..c572fc5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLoginEvent.cs @@ -0,0 +1,31 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentCallbackLoginEvent is triggered when an agent is successfully logged in using AgentCallbackLogin.
+ /// It is implemented in channels/chan_agent.c + ///
+ /// + public class AgentCallbackLoginEvent : ManagerEvent + { + private string agent; + private string loginChan; + + /// + /// Get/Set the name of the agent that logged in. + /// + public string Agent + { + get { return agent; } + set { this.agent = value; } + } + public string LoginChan + { + get { return loginChan; } + set { this.loginChan = value; } + } + + public AgentCallbackLoginEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLogoffEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLogoffEvent.cs new file mode 100644 index 0000000..c4b15c8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCallbackLogoffEvent.cs @@ -0,0 +1,92 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentCallbackLogoffEvent is triggered when an agent that previously logged in using + /// AgentCallbackLogin is logged of.
+ /// It is implemented in channels/chan_agent.c + ///
+ /// + public class AgentCallbackLogoffEvent : ManagerEvent + { + private string agent; + private string loginChan; + private string loginTime; + private string reason; + + #region Agent + /// Returns the name of the agent that logged off. + /// Sets the name of the agent that logged off. + public string Agent + { + get + { + return agent; + } + + set + { + this.agent = value; + } + + } + #endregion + + #region LoginChan + public string LoginChan + { + get + { + return loginChan; + } + + set + { + this.loginChan = value; + } + + } + #endregion + + #region LoginTime + public string LoginTime + { + get + { + return loginTime; + } + + set + { + this.loginTime = value; + } + + } + #endregion + + #region Reason + /// + /// Returns the reason for the logoff. The reason is set to Autologoff if the agent has been + /// logged off due to not answering the phone in time. Autologoff is configured by setting + /// autologoff to the appropriate number of seconds in agents.conf. + /// + /// Sets the reason for the logoff. + public string Reason + { + get + { + return reason; + } + + set + { + this.reason = value; + } + + } + #endregion + + public AgentCallbackLogoffEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCalledEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCalledEvent.cs new file mode 100644 index 0000000..f550ce8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCalledEvent.cs @@ -0,0 +1,89 @@ +using System.Collections; +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentCalledEvent is triggered when an agent is rung.
+ /// To enable AgentCalledEvents you have to set eventwhencalled = yes in queues.conf.
+ /// This event is implemented in apps/app_queue.c + ///
+ public class AgentCalledEvent : AbstractAgentVariables + { + private string agentCalled; + private string agentName; + private string callerId; + private string callerIdName; + private string callerIdNum; + private string channelCalling; + private string context; + private string destinationChannel; + private string extension; + private string priority; + private string queue; + + public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + public string AgentName + { + get { return agentName; } + set { this.agentName = value; } + } + public string AgentCalled + { + get { return agentCalled; } + set { this.agentCalled = value; } + } + public string ChannelCalling + { + get { return channelCalling; } + set { this.channelCalling = value; } + } + public string DestinationChannel + { + get { return this.destinationChannel; } + set { this.destinationChannel = value; } + } + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID number of the calling channel. + /// + public string CallerIdNum + { + get { return callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get/Set the Caller*ID name of the calling channel. + /// + public string CallerIdName + { + get { return callerIdName; } + set { this.callerIdName = value; } + } + public string Context + { + get { return context; } + set { this.context = value; } + } + public string Extension + { + get { return extension; } + set { this.extension = value; } + } + public string Priority + { + get { return priority; } + set { this.priority = value; } + } + + public AgentCalledEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCompleteEvent.cs new file mode 100644 index 0000000..492e676 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentCompleteEvent.cs @@ -0,0 +1,41 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentCompleteEvent is triggered when at the end of a call if the caller was connected to an agent. + /// + public class AgentCompleteEvent : AbstractAgentEvent + { + private long holdTime; + private string reason; + private long talkTime; + + /// + /// Get/Set the amount of time the caller was on hold. + /// + public long HoldTime + { + get { return holdTime; } + set { this.holdTime = value; } + } + /// + /// Get/Set the amount of time the caller talked to the agent. + /// + public long TalkTime + { + get { return talkTime; } + set { this.talkTime = value; } + } + /// + /// Get/Set if the agent or the caller terminated the call. + /// + public string Reason + { + get { return reason; } + set { this.reason = value; } + } + + public AgentCompleteEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentConnectEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentConnectEvent.cs new file mode 100644 index 0000000..7669cf4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentConnectEvent.cs @@ -0,0 +1,44 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentConnectEvent is triggered when a caller is connected to an agent. + /// + public class AgentConnectEvent : AbstractAgentEvent + { + private string bridgedChannel; + private long holdTime; + private long ringTime; + + /// + /// Get/Set the amount of time the caller was on hold. + /// + public long HoldTime + { + get { return holdTime; } + set { this.holdTime = value; } + } + + /// + /// Get/Set bridged channel. + /// + public string BridgedChannel + { + get { return this.bridgedChannel; } + set { this.bridgedChannel = value; } + } + + /// + /// Get/Set the amount of time the caller was on ring. + /// + public long RingTime + { + get { return ringTime; } + set { this.ringTime = value; } + } + + public AgentConnectEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentDumpEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentDumpEvent.cs new file mode 100644 index 0000000..bdba710 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentDumpEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentDumpEvent is triggered when an agent dumps the caller while listening + /// to the queue announcement. + /// + public class AgentDumpEvent : AbstractAgentEvent + { + public AgentDumpEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLoginEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLoginEvent.cs new file mode 100644 index 0000000..2254aa4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLoginEvent.cs @@ -0,0 +1,32 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentLoginEvent is triggered when an agent is successfully logged in using AgentLogin.
+ /// It is implemented in channels/chan_agent.c + ///
+ /// + public class AgentLoginEvent : ManagerEvent + { + private string agent; + private string loginChan; + + /// + /// Get/Set the name of the agent that logged in. + /// + public string Agent + { + get { return agent; } + set { this.agent = value; } + } + public string LoginChan + { + get { return loginChan; } + set { this.loginChan = value; } + } + + public AgentLoginEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLogoffEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLogoffEvent.cs new file mode 100644 index 0000000..d636699 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentLogoffEvent.cs @@ -0,0 +1,32 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentCallbackLogoffEvent is triggered when an agent that previously logged in using AgentLogin is logged of.
+ /// It is implemented in channels/chan_agent.c + ///
+ /// + public class AgentLogoffEvent : ManagerEvent + { + private string agent; + private string loginTime; + + /// + /// Get/Set the name of the agent that logged off. + /// + public string Agent + { + get { return agent; } + set { this.agent = value; } + } + public string LoginTime + { + get { return loginTime; } + set { this.loginTime = value; } + } + + public AgentLogoffEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsCompleteEvent.cs new file mode 100644 index 0000000..f3014a7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsCompleteEvent.cs @@ -0,0 +1,16 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentsCompleteEvent is triggered after the state of all agents has been + /// reported in response to an AgentsAction.
+ /// Available since Asterisk 1.2 + ///
+ /// + public class AgentsCompleteEvent : ResponseEvent + { + public AgentsCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsEvent.cs new file mode 100644 index 0000000..7a91daa --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AgentsEvent.cs @@ -0,0 +1,82 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AgentsEvent is triggered for each agent in response to an AgentsAction.
+ /// Available since Asterisk 1.2 + ///
+ /// + public class AgentsEvent : ResponseEvent + { + private string agent; + private string name; + private string status; + private string loggedInChan; + private string talkingTo; + private long loggedInTime; + + /// + /// Get/Set the agentid. + /// + public string Agent + { + get { return this.agent; } + set { this.agent = value; } + } + /// + /// Get/Set the name of this agent. + /// + public string Name + { + get { return this.name; } + set { this.name = value; } + } + /// + /// Get/Set the status of this agent.
+ /// This is one of + ///
+ ///
"AGENT_LOGGEDOFF"
+ ///
Agent isn't logged in
+ ///
"AGENT_IDLE"
+ ///
Agent is logged in, and waiting for call
+ ///
"AGENT_ONCALL"
+ ///
Agent is logged in, and on a call
+ ///
"AGENT_UNKNOWN"
+ ///
Don't know anything about agent. Shouldn't ever get this.
+ ///
+ ///
+ public string Status + { + get { return this.status; } + set { this.status = value; } + } + /// + /// Get/Set the name of channel this agent logged in from or "n/a" if the agent is not logged in. + /// + public string LoggedInChan + { + get { return this.loggedInChan; } + set { this.loggedInChan = value; } + } + /// + /// Get/Set the time (in seconds since 01/01/1970) when the agent logged in or 0 if the user is not logged. + /// + public long LoggedInTime + { + get { return this.loggedInTime; } + set { this.loggedInTime = value; } + } + /// + /// Get/Set the numerical Caller*ID of the channel this agent is talking toor "n/a" if this agent is talking to nobody. + /// + public string TalkingTo + { + get { return this.talkingTo; } + set { this.talkingTo = value; } + } + + public AgentsEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmClearEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmClearEvent.cs new file mode 100644 index 0000000..641a74a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmClearEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AlarmEvent is triggered when a Zap channel leaves alarm state.
+ /// It is implemented in channels/chan_zap.c + ///
+ public class AlarmClearEvent : ManagerEvent + { + public AlarmClearEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmEvent.cs new file mode 100644 index 0000000..ee9d6c9 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AlarmEvent.cs @@ -0,0 +1,34 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An AlarmEvent is triggered when a Zap channel enters or changes alarm state.
+ /// It is implemented in channels/chan_zap.c + ///
+ public class AlarmEvent : ManagerEvent + { + private string alarm; + + /// + /// Get/Set the kind of alarm that happened.
+ /// This may be one of + ///
    + ///
  • Red Alarm
  • + ///
  • Yellow Alarm
  • + ///
  • Blue Alarm
  • + ///
  • Recovering
  • + ///
  • Loopback
  • + ///
  • Not Open
  • + ///
+ ///
+ public string Alarm + { + get { return alarm; } + set { this.alarm = value; } + } + + public AlarmEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/AsyncAGIEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/AsyncAGIEvent.cs new file mode 100644 index 0000000..bca95f1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/AsyncAGIEvent.cs @@ -0,0 +1,40 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class AsyncAGIEvent : ManagerEvent + { + private string subEvent; + private string env; + private string result; + private string commandId; + + public string Result + { + get { return result; } + set { result = value; } + } + public string CommandId + { + get { return commandId; } + set { commandId = value; } + } + public string SubEvent + { + get { return subEvent; } + set { subEvent = value; } + } + public string Env + { + get { return env; } + set { env = value; } + } + + #region Constructor - AsyncAGIEvent(ManagerConnection source) + public AsyncAGIEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/BridgeEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/BridgeEvent.cs new file mode 100644 index 0000000..c2e04d5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/BridgeEvent.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Event +{ + /// + /// A BridgeEvent is triggered when a link between two voice + /// channels is established ("Link") or discontinued ("Unlink"). + /// As of Asterisk 1.6 the Bridge event is reported directly by Asterisk. + /// Asterisk versions up to 1.4 report individual events: LinkEvent and UnlinkEvent. + /// For maximum compatibily do not use the Link and Unlink events in your code. + /// Just use the Bridge event and check for isLink() and isUnlink(). + /// + public class BridgeEvent : ManagerEvent + { + public enum BridgeStates + { + Unknown, + BRIDGE_STATE_LINK, + BRIDGE_STATE_UNLINK + } + + public enum BridgeTypes + { + Unknnown, + /// A channel.c bridge + BRIDGE_TYPE_CORE, + /// An RTP peer-2-peer bridge (NAT support only). + BRIDGE_TYPE_RTP_DIRECT, + /// An RTP native bridge. + BRIDGE_TYPE_RTP_NATIVE, + /// A remote (re-invite) bridge + BRIDGE_TYPE_RTP_REMOTE + } + + internal BridgeStates bridgeState; + internal bool islink; + internal bool isunlink; + + private BridgeTypes bridgeType; + private string response; + private string reason; + private string channel1; + private string channel2; + private string uniqueId1; + private string uniqueId2; + private string callerId1; + private string callerId2; + + #region Constructors + public BridgeEvent(ManagerConnection source) + : base(source) + { + } + #endregion + + #region ParseSpecial(Dictionary attributes) + /// + /// Unknown properties parser + /// + /// dictionary + /// updated dictionary + public override Dictionary ParseSpecial(Dictionary attributes) + { + if (attributes == null) + return null; + + Dictionary updated = new Dictionary(attributes.Count); + foreach(KeyValuePair pair in attributes) + { + string value = pair.Value.ToLower(Helper.CultureInfo); + switch (pair.Key.ToLower(Helper.CultureInfo)) + { + case "bridgestate": + switch(value) + { + case "link": + this.BridgeState = BridgeStates.BRIDGE_STATE_LINK; + break; + case "unlink": + this.BridgeState = BridgeStates.BRIDGE_STATE_UNLINK; + break; + } + break; + + case "bridgetype": + switch (value) + { + case "rtp-native": + this.BridgeType = BridgeTypes.BRIDGE_TYPE_RTP_NATIVE; + break; + case "rtp-direct": + this.BridgeType = BridgeTypes.BRIDGE_TYPE_RTP_DIRECT; + break; + case "rtp-remote": + this.BridgeType = BridgeTypes.BRIDGE_TYPE_RTP_REMOTE; + break; + case "core": + this.BridgeType = BridgeTypes.BRIDGE_TYPE_CORE; + break; + } + break; + + default: + updated.Add(pair.Key, pair.Value); + break; + } + } + + return updated; + } + #endregion + + public string Response + { + get { return this.response; } + set { this.response = value; } + } + public string Reason + { + get { return this.reason; } + set { this.reason = value; } + } + /// + /// Link if the two channels have been linked, + /// Unlink if they have been unlinked. + /// + public BridgeStates BridgeState + { + get { return this.bridgeState; } + set { this.bridgeState = value; } + } + public BridgeTypes BridgeType + { + get { return this.bridgeType; } + set { this.bridgeType = value; } + } + public string Channel1 + { + get { return this.channel1; } + set { this.channel1 = value; } + } + public string Channel2 + { + get { return this.channel2; } + set { this.channel2 = value; } + } + public string UniqueId1 + { + get { return this.uniqueId1; } + set { this.uniqueId1 = value; } + } + public string UniqueId2 + { + get { return this.uniqueId2; } + set { this.uniqueId2 = value; } + } + public string CallerId1 + { + get { return this.callerId1; } + set { this.callerId1 = value; } + } + public string CallerId2 + { + get { return this.callerId2; } + set { this.callerId2 = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/CdrEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/CdrEvent.cs new file mode 100644 index 0000000..b3765d2 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/CdrEvent.cs @@ -0,0 +1,114 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A CdrEvent is triggered when a call detail record is generated, usually at the end of a call.
+ /// To enable CdrEvents you have to add enabled = yes to the general section in + /// cdr_manager.conf.
+ /// This event is implemented in cdr/cdr_manager.c + ///
+ public class CdrEvent : ManagerEvent + { + private string accountCode; + private string src; + private string destination; + private string destinationContext; + private string callerId; + private string destinationChannel; + private string lastApplication; + private string lastData; + private string startTime; + private string answerTime; + private string endTime; + private long duration; + private long billableSeconds; + private string disposition; + private string amaFlags; + private string userField; + + public CdrEvent(ManagerConnection source) + : base(source) + { + } + + public string AccountCode + { + get { return accountCode; } + set { this.accountCode = value; } + } + public string Src + { + get { return src; } + set { this.src = value; } + } + public string Destination + { + get { return destination; } + set { this.destination = value; } + } + public string DestinationContext + { + get { return destinationContext; } + set { this.destinationContext = value; } + } + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + public string DestinationChannel + { + get { return destinationChannel; } + set { this.destinationChannel = value; } + } + public string LastApplication + { + get { return lastApplication; } + set { this.lastApplication = value; } + } + public string LastData + { + get { return lastData; } + set { this.lastData = value; } + } + public string StartTime + { + get { return startTime; } + set { this.startTime = value; } + } + public string AnswerTime + { + get { return answerTime; } + set { this.answerTime = value; } + } + public string EndTime + { + get { return endTime; } + set { this.endTime = value; } + } + public long Duration + { + get { return duration; } + set { this.duration = value; } + } + public long BillableSeconds + { + get { return billableSeconds; } + set { this.billableSeconds = value; } + } + public string Disposition + { + get { return disposition; } + set { this.disposition = value; } + } + public string AmaFlags + { + get { return amaFlags; } + set { this.amaFlags = value; } + } + public string UserField + { + get { return userField; } + set { this.userField = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelReloadEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelReloadEvent.cs new file mode 100644 index 0000000..70e38f5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelReloadEvent.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class ChannelReloadEvent : ManagerEvent + { + private string channelType; + private string reloadreason; + private int registryCount; + private int userCount; + private int peerCount; + + /// + /// For SIP peers this is "SIP". + /// + public string ChannelType + { + get { return channelType; } + set { this.channelType = value; } + } + + /// + /// Get/Set the name of the channel. + /// + public string ReloadReason + { + get { return reloadreason; } + set { this.reloadreason = value; } + } + + public int UserCount + { + get { return userCount; } + set { this.userCount = value; } + } + + public int PeerCount + { + get { return peerCount; } + set { this.peerCount = value; } + } + + public int RegistryCount + { + get { return registryCount; } + set { this.registryCount = value; } + } + + public ChannelReloadEvent(ManagerConnection source) + : base(source) + { + } + + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelUpdateEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelUpdateEvent.cs new file mode 100644 index 0000000..0619252 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ChannelUpdateEvent.cs @@ -0,0 +1,75 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class ChannelUpdateEvent : ManagerEvent + { + private string channelType; + private string sipCallId; + private string sipFullContact; + private string peerName; + private string iax2callnoLocal; + private string iax2callnoRemote; + private string iax2peer; + private string gtalkSID; + + /// + /// Get/Set channel type + /// "SIP", + /// "IAX2", + /// "GTALK" + /// + public string ChannelType + { + get { return this.channelType; } + set { this.channelType = value; } + } + + public string SipCallId + { + get { return this.sipCallId; } + set { this.sipCallId = value; } + } + + public string SipFullContact + { + get { return this.sipFullContact; } + set { this.sipFullContact = value; } + } + + public string PeerName + { + get { return this.peerName; } + set { this.peerName = value; } + } + + public string IAX2CallnoLocal + { + get { return this.iax2callnoLocal; } + set { this.iax2callnoLocal = value; } + } + public string IAX2CallnoRemote + { + get { return this.iax2callnoRemote; } + set { this.iax2callnoRemote = value; } + } + public string IAX2Peer + { + get { return this.iax2peer; } + set { this.iax2peer = value; } + } + public string GTalkSID + { + get { return this.gtalkSID; } + set { this.gtalkSID = value; } + } + + /// + /// Creates a new ChannelUpdateEvent. + /// + public ChannelUpdateEvent(ManagerConnection source) + : base(source) + { + } +} +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectEvent.cs new file mode 100644 index 0000000..de91ff0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectEvent.cs @@ -0,0 +1,25 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ConnectEvent is triggered after successful login to the asterisk server.
+ /// It is a pseudo event not directly related to an asterisk generated event. + ///
+ public class ConnectEvent : ConnectionStateEvent + { + /// The version of the manager/proxy protocol. + private string protocolIdentifier; + public ConnectEvent(ManagerConnection source) + : base(source) + { + } + + /// + /// Get/Set the version of the protocol. + /// + public string ProtocolIdentifier + { + get { return protocolIdentifier; } + set { this.protocolIdentifier = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectionStateEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectionStateEvent.cs new file mode 100644 index 0000000..6965173 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ConnectionStateEvent.cs @@ -0,0 +1,22 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for several agent related events. + /// + public abstract class ConnectionStateEvent : ManagerEvent + { + private bool reconnect = false; + /// + /// Get/Set reconnect status. + /// + public bool Reconnect + { + get { return this.reconnect; } + set { this.reconnect = true; } + } + + public ConnectionStateEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/DBGetResponseEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/DBGetResponseEvent.cs new file mode 100644 index 0000000..1910aa7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/DBGetResponseEvent.cs @@ -0,0 +1,44 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A DBGetResponseEvent is sent in response to a DBGetAction and contains the entry that was queried.
+ /// Available since Asterisk 1.2 + ///
+ /// + public class DBGetResponseEvent : ResponseEvent + { + private string family; + private string key; + private string val; + + /// + /// Get/Set the family of the database entry that was queried. + /// + public string Family + { + get { return this.family; } + set { this.family = value; } + } + /// + /// Get/Set the key of the database entry that was queried. + /// + public string Key + { + get { return this.key; } + set { this.key = value; } + } + /// + /// Get/Set the value of the database entry that was queried. + /// + public string Val + { + get { return val; } + set { this.val = value; } + } + + public DBGetResponseEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/DNDStateEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/DNDStateEvent.cs new file mode 100644 index 0000000..819d720 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/DNDStateEvent.cs @@ -0,0 +1,40 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A DNDStateEvent is triggered by the Zap channel driver when a channel enters + /// or leaves DND (do not disturb) state.
+ /// It is implemented in channels/chan_zap.c.
+ /// Available since Asterisk 1.2 + ///
+ public class DNDStateEvent : ManagerEvent + { + private string state; + private string status; + + /// + /// Get/Set DND state of the channel. "enabled" if do not disturb is on, "disabled" if it is off. + /// + public string State + { + get { return this.state; } + set { this.state = value; } + } + + /// + /// Get/Set DND state of the channel. "enabled" if do not disturb is on, "disabled" if it is off. + /// + public string Status + { + get { return this.status; } + set { this.status = value; } + } + + /// + /// Creates a new DNDStateEvent. + /// + public DNDStateEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/DTMFEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/DTMFEvent.cs new file mode 100644 index 0000000..6769f98 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/DTMFEvent.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + class DTMFEvent : ManagerEvent + { + private string digit; + private string direction; + private bool begin; + private bool end; + + public string Direction + { + get { return direction; } + set { this.direction = value; } + } + public string Digit + { + get { return digit; } + set { this.digit = value; } + } + public bool Begin + { + get { return begin; } + set { this.begin = value; } + } + public bool End + { + get { return end; } + set { this.end = value; } + } + + /// + /// Creates a new DialEvent. + /// + public DTMFEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/DialEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/DialEvent.cs new file mode 100644 index 0000000..b645236 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/DialEvent.cs @@ -0,0 +1,102 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A dial event is triggered whenever a phone attempts to dial someone.
+ /// This event is implemented in apps/app_dial.c.
+ /// Available since Asterisk 1.2. + ///
+ public class DialEvent : ManagerEvent + { + private string subEvent; + private string destination; + private string callerId; + private string callerIdNum; + private string callerIdName; + private string srcUniqueId; + private string destUniqueId; + private string dialString; + private string dialStatus; + private string src; + + + /// + /// Creates a new DialEvent. + /// + public DialEvent(ManagerConnection source) + : base(source) + { + } + + public string DialString + { + get { return this.dialString; } + set { this.dialString = value; } + } + public string SubEvent + { + get { return this.subEvent; } + set { this.subEvent = value; } + } + public string DialStatus + { + get { return this.dialStatus; } + set { this.dialStatus = value; } + } + /// + /// Returns the name of the source channel. + /// + public string Src + { + get { return src; } + set { this.src = value; } + } + /// + /// Get/Set the name of the destination channel. + /// + public string Destination + { + get { return destination; } + set { this.destination = value; } + } + /// + /// Get/Set the Caller*ID. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID Name. + /// + public string CallerIdName + { + get { return callerIdName; } + set { this.callerIdName = value; } + } + /// + /// Get/Set the Caller*ID Number. + /// + public string CallerIdNum + { + get { return callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get/Set the unique ID of the source channel. + /// + public string SrcUniqueId + { + get { return srcUniqueId; } + set { this.srcUniqueId = value; } + } + /// + /// Get/Set the unique ID of the distination channel. + /// + public string DestUniqueId + { + get { return destUniqueId; } + set { this.destUniqueId = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/DisconnectEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/DisconnectEvent.cs new file mode 100644 index 0000000..1433ab4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/DisconnectEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A DisconnectEvent is triggered when the connection to the asterisk server is lost.
+ /// It is a pseudo event not directly related to an asterisk generated event. + ///
+ public class DisconnectEvent : ConnectionStateEvent + { + public DisconnectEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ExtensionStatusEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ExtensionStatusEvent.cs new file mode 100644 index 0000000..52c767e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ExtensionStatusEvent.cs @@ -0,0 +1,52 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An ExtensionStatusEvent is triggered when the state of an extension changes.
+ /// It is implemented in manager.c + ///
+ public class ExtensionStatusEvent : ManagerEvent + { + private string exten; + private string context; + private string hint; + private int status; + + /// + /// Get/Set the extension hint. + /// + public string Hint + { + get { return this.hint; } + set { this.hint = value; } + } + /// + /// Get/Set the extension. + /// + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + /// + /// Get/Set the context of the extension. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + /// + /// Get/Set the state of the extension. + /// + public int Status + { + get { return status; } + set { this.status = value; } + } + + public ExtensionStatusEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/FaxReceivedEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/FaxReceivedEvent.cs new file mode 100644 index 0000000..5bbfe9e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/FaxReceivedEvent.cs @@ -0,0 +1,98 @@ +using Asterisk.NET.Manager.Event; +namespace Asterisk.NET.Manager.Event +{ + /// + /// A FaxReceivedEvent is triggered by spandsp after a new fax has been received.
+ /// It is only available if you installed the spandsp patches to Asterisk.
+ /// See http://soft-switch.org/installing-spandsp.html for details.
+ /// Implemented in apps/app_rxfax.c. + ///
+ public class FaxReceivedEvent : AbstractAgentEvent + { + private string exten; + private string callerId; + private string remoteStationId; + private string localStationId; + private int pagesTransferred; + private int resolution; + private int transferRate; + private string filename; + + public FaxReceivedEvent(ManagerConnection source) + : base(source) + { + } + + /// + /// Get/Set the extension in Asterisk's dialplan the fax was received + /// + public string Exten + { + get { return exten; } + set { exten = value; } + } + + /// + /// Get/Set the Caller*ID of the calling party or an empty string if none is + /// + public string CallerId + { + get { return callerId; } + set { callerId = value; } + } + + /// + /// Get/Set the identifier of the remote fax station. + /// + public string RemoteStationId + { + get { return remoteStationId; } + set { remoteStationId = value; } + } + + /// + /// Get/Set the identifier of the local fax station. + /// + public string LocalStationId + { + get { return localStationId; } + set { localStationId = value; } + } + + /// + /// Get/Set the number of pages transferred. + /// + public int PagesTransferred + { + get { return pagesTransferred; } + set { pagesTransferred = value; } + } + + /// + /// Get/Set the row resolution of the received fax. + /// + public int Resolution + { + get { return resolution; } + set { resolution = value; } + } + + /// + /// Get/Set the transfer rate in bits/s. + /// + public int TransferRate + { + get { return transferRate; } + set { transferRate = value; } + } + + /// + /// Get/Set the filename of the received fax including its full path on the Asterisk server. + /// + public string Filename + { + get { return filename; } + set { filename = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/HangupEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/HangupEvent.cs new file mode 100644 index 0000000..f0d0296 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/HangupEvent.cs @@ -0,0 +1,34 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A HangupEvent is triggered when a channel is hung up.
+ /// It is implemented in channel.c + ///
+ public class HangupEvent : AbstractChannelEvent + { + private int cause; + private string causeTxt; + + /// + /// Get/Set the cause of the hangup. + /// + public int Cause + { + get { return cause; } + set { this.cause = value; } + } + /// + /// Get/Set the textual representation of the hangup cause. + /// + public string CauseTxt + { + get { return causeTxt; } + set { this.causeTxt = value; } + } + + public HangupEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/HoldEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/HoldEvent.cs new file mode 100644 index 0000000..4c585d3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/HoldEvent.cs @@ -0,0 +1,24 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A HoldEvent is triggered by the SIP channel driver when a channel is put on hold.
+ /// It is implemented in channels/chan_sip.c.
+ /// Available since Asterisk 1.2 + ///
+ /// + public class HoldEvent : ManagerEvent + { + private string status; + + public string Status + { + get { return this.status; } + set { this.status = value; } + } + + public HoldEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/HoldedCallEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/HoldedCallEvent.cs new file mode 100644 index 0000000..d0f1717 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/HoldedCallEvent.cs @@ -0,0 +1,52 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A HoldedCallEvent is triggered when a channel is put on hold.
+ /// It is implemented in res/res_features.c + ///
+ public class HoldedCallEvent : ManagerEvent + { + private string uniqueId1; + private string uniqueId2; + private string channel1; + private string channel2; + + /// + /// Get/Set the unique id of the channel that put the other channel on hold. + /// + public string UniqueId1 + { + get { return uniqueId1; } + set { this.uniqueId1 = value; } + } + /// + /// Get/Set the unique id of the channel that has been put on hold. + /// + public string UniqueId2 + { + get { return uniqueId2; } + set { this.uniqueId2 = value; } + } + /// + /// Get/Set the name of the channel that put the other channel on hold. + /// + public string Channel1 + { + get { return channel1; } + set { this.channel1 = value; } + } + /// + /// Get/Set the name of the channel that has been put on hold. + /// + public string Channel2 + { + get { return channel2; } + set { this.channel2 = value; } + } + + public HoldedCallEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/JabberEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/JabberEvent.cs new file mode 100644 index 0000000..89627a4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/JabberEvent.cs @@ -0,0 +1,28 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class JabberEvent : ManagerEvent + { + private string account; + private string packet; + + public string Account + { + get { return account; } + set { account = value; } + } + public string Packet + { + get { return packet; } + set { packet = value; } + } + + #region Constructor - JabberEvent(ManagerConnection source) + public JabberEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/JitterBufStatsEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/JitterBufStatsEvent.cs new file mode 100644 index 0000000..93b76b7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/JitterBufStatsEvent.cs @@ -0,0 +1,110 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class JitterBufStatsEvent : ManagerEvent + { + private string owner; + private int ping; + private int localJitter; + private int localJBDelay; + private int localTotalLost; + private int localLossPercent; + private int localDropped; + private int localooo; + private int localReceived; + private int remoteJitter; + private int remoteJBDelay; + private int remoteTotalLost; + private int remoteLossPercent; + private int remoteDropped; + private int remoteooo; + private int remoteReceived; + + public string Owner + { + get { return this.owner; } + set { this.owner = value; } + } + public int Ping + { + get { return this.ping; } + set { this.ping = value; } + } + public int LocalJitter + { + get { return this.localJitter; } + set { this.localJitter = value; } + } + public int LocalJBDelay + { + get { return this.localJBDelay; } + set { this.localJBDelay = value; } + } + public int LocalTotalLost + { + get { return this.localTotalLost; } + set { this.localTotalLost = value; } + } + public int LocalLossPercent + { + get { return this.localLossPercent; } + set { this.localLossPercent = value; } + } + public int LocalDropped + { + get { return this.localDropped; } + set { this.localDropped = value; } + } + public int Localooo + { + get { return this.localooo; } + set { this.localooo = value; } + } + public int LocalReceived + { + get { return this.localReceived; } + set { this.localReceived = value; } + } + public int RemoteJitter + { + get { return this.remoteJitter; } + set { this.remoteJitter = value; } + } + public int RemoteJBDelay + { + get { return this.remoteJBDelay; } + set { this.remoteJBDelay = value; } + } + public int RemoteTotalLost + { + get { return this.remoteTotalLost; } + set { this.remoteTotalLost = value; } + } + public int RemoteLossPercent + { + get { return this.remoteLossPercent; } + set { this.remoteLossPercent = value; } + } + public int RemoteDropped + { + get { return this.remoteDropped; } + set { this.remoteDropped = value; } + } + public int Remoteooo + { + get { return this.remoteooo; } + set { this.remoteooo = value; } + } + public int RemoteReceived + { + get { return this.remoteReceived; } + set { this.remoteReceived = value; } + } + + public JitterBufStatsEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/JoinEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/JoinEvent.cs new file mode 100644 index 0000000..576438f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/JoinEvent.cs @@ -0,0 +1,45 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A JoinEvent is triggered when a channel joines a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ public class JoinEvent : QueueEvent + { + private string callerId; + private string callerIdName; + private int position; + + /// + /// Get/Set the Caller*ID number of the channel that joined the queue if set. + /// If the channel has no caller id set "unknown" is returned. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID name of the channel that joined the queue if set. + /// If the channel has no caller id set "unknown" is returned. + /// + public string CallerIdName + { + get { return callerIdName; } + set { this.callerIdName = value; } + } + /// + /// Get/Set the position of the joined channel in the queue. + /// + public int Position + { + get { return position; } + set { this.position = value; } + } + + public JoinEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/LeaveEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/LeaveEvent.cs new file mode 100644 index 0000000..9138f7d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/LeaveEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A LeaveEvent is triggered when a channel leaves a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ public class LeaveEvent : QueueEvent + { + public LeaveEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/LinkEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/LinkEvent.cs new file mode 100644 index 0000000..09b3dae --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/LinkEvent.cs @@ -0,0 +1,18 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A LinkEvent is triggered when two voice channels are linked together and voice data exchange commences.
+ /// Several Link events may be seen for a single call. + /// This can occur when Asterisk fails to setup a native bridge for the call. + /// This is when Asterisk must sit between two telephones and perform CODEC conversion on their behalf. + ///
+ public class LinkEvent : BridgeEvent + { + public LinkEvent(ManagerConnection source) + : base(source) + { + islink = true; + bridgeState = BridgeStates.BRIDGE_STATE_LINK; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/LogChannelEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/LogChannelEvent.cs new file mode 100644 index 0000000..b8b4bf8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/LogChannelEvent.cs @@ -0,0 +1,59 @@ +using System; +namespace Asterisk.NET.Manager.Event +{ + /// + /// A LogChannelEvent is triggered when logging is turned on or off.
+ /// It is implemented in logger.c
+ ///
+ public class LogChannelEvent : ManagerEvent + { + private bool enabled; + private string reason; + private int reasonCode; + + /// + /// Get/Set if logging has been enabled or disabled. + /// + public bool Enabled + { + get { return enabled; } + set { this.enabled = value; } + } + /// + /// Get the textual representation of the reason for disabling logging. + /// + public string Reason + { + get { return this.reason; } + set + { + reason = ""; + reasonCode = 0; + + if (string.IsNullOrEmpty(value)) + return; + + int spaceIdx; + + if ((spaceIdx = value.IndexOf(' ')) <= 0) + spaceIdx = value.Length; + int.TryParse(value.Substring(0, spaceIdx), out this.reasonCode); + if (value.Length > spaceIdx + 3) + this.reason = value.Substring(spaceIdx + 3, value.Length - spaceIdx + 3); + } + } + + /// + /// Get the reason code for disabling logging. + /// + public int ReasonCode + { + get { return this.reasonCode; } + } + + public LogChannelEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ManagerEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ManagerEvent.cs new file mode 100644 index 0000000..4152fdd --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ManagerEvent.cs @@ -0,0 +1,178 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for all Events that can be received from the Asterisk server.
+ /// Events contain data pertaining to an event generated from within the Asterisk + /// core or an extension module.
+ /// There is one conrete subclass of ManagerEvent per each supported Asterisk + /// Event. + ///
+ public abstract class ManagerEvent : EventArgs, IParseSupport + { + private DateTime dateReceived; + private string privilege; + private string server; + private double timestamp; + + private string uniqueId; + private string channel; + private ManagerConnection src; + protected Dictionary attributes; + + #region Constructors + public ManagerEvent() + { + this.dateReceived = DateTime.Now; + } + + public ManagerEvent(ManagerConnection source) + : this() + { + this.src = source as ManagerConnection; + } + #endregion + + + #region Attributes + /// + /// Store all unknown (without setter) keys from manager event.
+ /// Use in default Parse method . + ///
+ public Dictionary Attributes + { + get { return attributes; } + } + #endregion + + #region Server + /// + /// Specify a server to which to send your commands (x.x.x.x or hostname).
+ /// 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. + ///
+ public string Server + { + get { return server; } + set { server = value; } + } + #endregion + + #region Timestamp + /// + /// Returns the timestamp for this event.
+ /// The timestamp property is available in Asterisk since 1.4 + /// if enabled in manager.conf by setting timestampevents = yes. + /// In contains the time the event was generated in seconds since the epoch. + ///
+ public double Timestamp + { + get { return this.timestamp; } + set { this.timestamp = value; } + } + #endregion + + #region DateReceived + /// + /// Get/Set the point in time this event was received from the Asterisk server.
+ /// Pseudo events that are not directly received from the asterisk server + /// (for example ConnectEvent and DisconnectEvent) may return null. + ///
+ public DateTime DateReceived + { + get { return this.dateReceived; } + set { this.dateReceived = value; } + } + #endregion + + #region Privilege + /// + /// Get/Set the AMI authorization class of this event.
+ /// This is one or more of system, call, log, verbose, command, agent or user. + /// Multiple privileges are separated by comma.
+ /// Note: This property is not available from Asterisk 1.0 servers. + ///
+ public string Privilege + { + get { return privilege; } + set { this.privilege = value; } + } + #endregion + + #region Source + /// + /// Event source. + /// + public ManagerConnection Source + { + get { return this.src; } + } + #endregion + + #region UniqueId + /// + /// Get/Set the unique id of the channel. + /// + public string UniqueId + { + get { return uniqueId; } + set { this.uniqueId = value; } + } + #endregion + + #region Channel + /// + /// Get/Set the name of the channel. + /// + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + #endregion + + + #region Parse(string key, string value) + /// + /// Unknown properties parser + /// + /// key name + /// key value + /// true - value parsed, false - can't parse value + public virtual bool Parse(string key, string value) + { + if (attributes == null) + attributes = new Dictionary(); + + if (attributes.ContainsKey(key)) + // Key already presents, add with delimiter + attributes[key] += string.Concat(Common.LINE_SEPARATOR, value); + else + attributes.Add(key, value); + return true; + } + #endregion + + #region ParseSpecial(Dictionary attributes) + /// + /// Unknown properties parser + /// + /// dictionary + /// updated dictionary + public virtual Dictionary ParseSpecial(Dictionary attributes) + { + return attributes; + } + #endregion + + #region ToString() + public override string ToString() + { + return Helper.ToString(this); + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MasqueradeEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MasqueradeEvent.cs new file mode 100644 index 0000000..4b75e4a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MasqueradeEvent.cs @@ -0,0 +1,43 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + class MasqueradeEvent : ManagerEvent + { + private string clone; + private string cloneState; + private string original; + private string originalState; + + public string Clone + { + get { return this.clone; } + set { this.clone = value; } + } + + public string CloneState + { + get { return this.cloneState; } + set { this.cloneState = value; } + } + + public string Original + { + get { return this.original; } + set { this.original = value; } + } + + public string OriginalState + { + get { return this.originalState; } + set { this.originalState = value; } + } + + #region Constructor - MasqueradeEvent(ManagerConnection source) + public MasqueradeEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeEndEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeEndEvent.cs new file mode 100644 index 0000000..25adba7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeEndEvent.cs @@ -0,0 +1,11 @@ +namespace Asterisk.NET.Manager.Event +{ + public class MeetmeEndEvent : AbstractMeetmeEvent + { + + public MeetmeEndEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeJoinEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeJoinEvent.cs new file mode 100644 index 0000000..9a7b6ee --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeJoinEvent.cs @@ -0,0 +1,28 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A MeetMeJoinEvent is triggered if a channel joins a meet me conference.
+ /// It is implemented in apps/app_meetme.c + ///
+ public class MeetmeJoinEvent : AbstractMeetmeEvent + { + private string callerIdNum; + private string callerIdName; + + public string CallerIdNum + { + get { return this.callerIdNum; } + set { this.callerIdNum = value; } + } + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + + public MeetmeJoinEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeLeaveEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeLeaveEvent.cs new file mode 100644 index 0000000..fed069a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeLeaveEvent.cs @@ -0,0 +1,35 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A MeetMeLeaveEvent is triggered if a channel leaves a meet me conference.
+ /// It is implemented in apps/app_meetme.c + ///
+ public class MeetmeLeaveEvent : AbstractMeetmeEvent + { + private string callerIdNum; + private string callerIdName; + private long duration; + + public string CallerIdNum + { + get { return this.callerIdNum; } + set { this.callerIdNum = value; } + } + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + + public long Duration + { + get { return this.duration; } + set { this.duration = value; } + } + + public MeetmeLeaveEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeMuteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeMuteEvent.cs new file mode 100644 index 0000000..5e292f5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeMuteEvent.cs @@ -0,0 +1,18 @@ +namespace Asterisk.NET.Manager.Event +{ + public class MeetmeMuteEvent : AbstractMeetmeEvent + { + private bool status; + + public bool Status + { + get { return this.status; } + set { this.status = value; } + } + + public MeetmeMuteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeStopTalkingEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeStopTalkingEvent.cs new file mode 100644 index 0000000..c1a72a3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeStopTalkingEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A MeetMeStopTalkingEvent is triggered when a user ends talking in a meet me conference.
+ /// It is implemented in apps/app_meetme.c + ///
+ public class MeetmeStopTalkingEvent : AbstractMeetmeEvent + { + public MeetmeStopTalkingEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkRequestEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkRequestEvent.cs new file mode 100644 index 0000000..f59a101 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkRequestEvent.cs @@ -0,0 +1,18 @@ +namespace Asterisk.NET.Manager.Event +{ + public class MeetmeTalkRequestEvent : AbstractMeetmeEvent + { + private bool status; + + public bool Status + { + get { return this.status; } + set { this.status = value; } + } + + public MeetmeTalkRequestEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkingEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkingEvent.cs new file mode 100644 index 0000000..fc27e42 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MeetmeTalkingEvent.cs @@ -0,0 +1,21 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A MeetMeTalkingEvent is triggered when a user starts talking in a meet me conference. + /// + public class MeetmeTalkingEvent : AbstractMeetmeEvent + { + private bool status; + + public bool Status + { + get { return this.status; } + set { this.status = value; } + } + + public MeetmeTalkingEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MessageWaitingEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MessageWaitingEvent.cs new file mode 100644 index 0000000..21563d2 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MessageWaitingEvent.cs @@ -0,0 +1,53 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A MessageWaitingEvent is triggered when someone leaves voicemail.
+ /// It is implemented in apps/app_voicemail.c + ///
+ public class MessageWaitingEvent : ManagerEvent + { + private string mailbox; + private int waiting; + private int newMessages; + private int oldMessages; + + /// + /// Get/Set the name of the mailbox that has waiting messages.
+ /// The name of the mailbox is of the form numberOfMailbox@context, e.g. 1234@default. + ///
+ public string Mailbox + { + get { return this.mailbox; } + set { this.mailbox = value; } + } + /// + /// Get/Set the number of new messages in the mailbox. + /// + public int Waiting + { + get { return this.waiting; } + set { this.waiting = value; } + } + /// + /// Get/Set the number of new messages in this mailbox. + /// + public int New + { + get { return this.newMessages; } + set { this.newMessages = value; } + } + /// + /// Get/Set the number of old messages in this mailbox. + /// + public int Old + { + get { return this.oldMessages; } + set { this.oldMessages = value; } + } + + public MessageWaitingEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MobileStatusEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MobileStatusEvent.cs new file mode 100644 index 0000000..889e058 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MobileStatusEvent.cs @@ -0,0 +1,27 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class MobileStatusEvent : ManagerEvent + { + private string status; + private string device; + public string Status + { + get { return status; } + set { status = value; } + } + public string Device + { + get { return device; } + set { device = value; } + } + + #region Constructor - MobileStatus(ManagerConnection source) + public MobileStatusEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ModuleLoadReportEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ModuleLoadReportEvent.cs new file mode 100644 index 0000000..0af4c23 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ModuleLoadReportEvent.cs @@ -0,0 +1,32 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class ModuleLoadReportEvent : ManagerEvent + { + private string moduleLoadStatus; + private string moduleSelection; + private int moduleCount; + + public string ModuleLoadStatus + { + get { return this.moduleLoadStatus; } + set { this.moduleLoadStatus = value; } + } + public string ModuleSelection + { + get { return this.moduleSelection; } + set { this.moduleSelection = value; } + } + public int ModuleCount + { + get { return this.moduleCount; } + set { this.moduleCount = value; } + } + + public ModuleLoadReportEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStartEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStartEvent.cs new file mode 100644 index 0000000..7cbbaef --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStartEvent.cs @@ -0,0 +1,14 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class MonitorStartEvent : ManagerEvent + { + #region Constructor - MonitorStart(ManagerConnection source) + public MonitorStartEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStopEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStopEvent.cs new file mode 100644 index 0000000..978186d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/MonitorStopEvent.cs @@ -0,0 +1,14 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class MonitorStopEvent : ManagerEvent + { + #region Constructor - MonitorStop(ManagerConnection source) + public MonitorStopEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/NewAccountCodeEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/NewAccountCodeEvent.cs new file mode 100644 index 0000000..627ca62 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/NewAccountCodeEvent.cs @@ -0,0 +1,27 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class NewAccountCodeEvent : ManagerEvent + { + private string accountCode; + private string oldAccountCode; + + public string AccountCode + { + get { return this.accountCode; } + set { this.accountCode = value; } + } + + public string OldAccountCode + { + get { return this.oldAccountCode; } + set { this.oldAccountCode = value; } + } + + public NewAccountCodeEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/NewCallerIdEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/NewCallerIdEvent.cs new file mode 100644 index 0000000..43412f3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/NewCallerIdEvent.cs @@ -0,0 +1,84 @@ +using System; +namespace Asterisk.NET.Manager.Event +{ + /// + /// A NewCallerIdEvent is triggered when the caller id of a channel changes.
+ /// It is implemented in channel.c + ///
+ public class NewCallerIdEvent : ManagerEvent + { + private string callerId; + private string callerIdNum; + private string callerIdName; + private string cidCallingPresTxt; + private int cidCallingPres; + + public NewCallerIdEvent(ManagerConnection source) + : base(source) + { + } + + /// + /// Get/Set the new caller id. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the new Caller*ID Name if set or "≶Unknown>" if none has been set. + /// + public string CallerIdName + { + get { return callerIdName; } + set { this.callerIdName = value; } + } + /// + /// Get/Set the new Caller*ID Numb. + /// + public string CallerIdNum + { + get { return callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get the CallerId presentation/screening. + /// + public int CidCallingPresNumeric + { + get { return cidCallingPres; } + } + + /// + /// Get/Sets the CallerId presentation/screening in the form "%d (%s)". + /// + public string CidCallingPres + { + get + { + return cidCallingPres.ToString() + " (" + cidCallingPresTxt + ")"; + } + set + { + string s = value; + if (s == null || s.Length == 0) + return; + + int spaceIdx = s.IndexOf(' '); + if (spaceIdx <= 0) + spaceIdx = s.Length; + try + { + this.cidCallingPres = int.Parse(s.Substring(0, spaceIdx)); + } + catch (FormatException) + { + return; + } + if (s.Length > spaceIdx + 3) + this.cidCallingPresTxt = s.Substring(spaceIdx + 2, (s.Length - 1) - (spaceIdx + 2)); + } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/NewChannelEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/NewChannelEvent.cs new file mode 100644 index 0000000..7c129b1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/NewChannelEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A NewChannelEvent is triggered when a new channel is created.
+ /// It is implemented in channel.c + ///
+ public class NewChannelEvent : AbstractChannelEvent + { + public NewChannelEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/NewExtenEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/NewExtenEvent.cs new file mode 100644 index 0000000..e4632da --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/NewExtenEvent.cs @@ -0,0 +1,68 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A NewExtenEvent is triggered when a channel is connected to a new extension.
+ /// It is implemented in pbx.c + ///
+ public class NewExtenEvent : ManagerEvent + { + private string context; + private string extension; + private int priority; + private string application; + private string appData; + private string appdEvent; + + public string AppdEvent + { + get { return this.appdEvent; } + set { this.appdEvent = value; } + } + + /// + /// Get/Set the name of the application that is executed. + /// + public string Application + { + get { return this.application; } + set { this.application = value; } + } + /// + /// Get/Set the parameters passed to the application that is executed. The parameters are separated by a '|' character. + /// + public string AppData + { + get { return this.appData; } + set { this.appData = value; } + } + /// + /// Get/Set the name of the context of the connected extension. + /// + public string Context + { + get { return this.context; } + set { this.context = value; } + } + /// + /// Get/Set the extension. + /// + public string Extension + { + get { return this.extension; } + set { this.extension = value; } + } + /// + /// Get/Set the priority. + /// + public int Priority + { + get { return this.priority; } + set { this.priority = value; } + } + + public NewExtenEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/NewStateEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/NewStateEvent.cs new file mode 100644 index 0000000..4fb2132 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/NewStateEvent.cs @@ -0,0 +1,14 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A NewStateEvent is triggered when the state of a channel has changed.
+ /// It is implemented in channel.c + ///
+ public class NewStateEvent : AbstractChannelEvent + { + public NewStateEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/OriginateResponseEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/OriginateResponseEvent.cs new file mode 100644 index 0000000..e065af2 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/OriginateResponseEvent.cs @@ -0,0 +1,57 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class OriginateResponseEvent : ResponseEvent + { + private string response; + private string context; + private string exten; + private int reason; + private string callerIdNum; + private string callerIdName; + private string callerId; + + public string Response + { + get { return this.response; } + set { this.response = value; } + } + public string Context + { + get { return this.context; } + set { this.context = value; } + } + public string Exten + { + get { return this.exten; } + set { this.exten = value; } + } + public int Reason + { + get { return this.reason; } + set { this.reason = value; } + } + public string CallerId + { + get { return callerId; } + set { callerId = value; } + } + public string CallerIdNum + { + get { return this.callerIdNum; } + set { this.callerIdNum = value; } + } + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + + public OriginateResponseEvent(ManagerConnection source) + : base(source) + { + } + + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/PRIEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/PRIEvent.cs new file mode 100644 index 0000000..aa676f5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/PRIEvent.cs @@ -0,0 +1,41 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class PRIEvent : ManagerEvent + { + private string priEvent; + private string priEventCode; + private string dChannel; + private string span; + + public string PriEvent + { + get { return this.priEvent; } + set { this.priEvent = value; } + } + public string PriEventCode + { + get { return this.priEventCode; } + set { this.priEventCode = value; } + } + public string DChannel + { + get { return this.dChannel; } + set { this.dChannel = value; } + } + public string Span + { + get { return this.span; } + set { this.span = value; } + } + + /// + /// Creates a new DNDStateEvent. + /// + public PRIEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallEvent.cs new file mode 100644 index 0000000..6d1e2d9 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallEvent.cs @@ -0,0 +1,70 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ParkedCallEvent is triggered when a channel is parked (in this case no + /// action id is set) and in response to a ParkedCallsAction.
+ /// It is implemented in res/res_features.c + ///
+ /// + public class ParkedCallEvent : ResponseEvent + { + private string exten; + private string from; + private long timeout; + private string callerId; + private string callerIdNum; + private string callerIdName; + + /// Get/Set the extension the channel is parked at. + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + /// Get/Set the name of the channel that parked the call. + public string From + { + get { return from; } + set { this.from = value; } + } + /// + /// Get/Set the number of seconds this call will be parked.
+ /// This corresponds to the parkingtime option in + /// features.conf. + ///
+ public long Timeout + { + get { return timeout; } + set { this.timeout = value; } + } + /// + /// Get/Set the Caller*ID number of the parked channel. + /// + public string CallerId + { + get { return this.callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID number of the parked channel. + /// + public string CallerIdNum + { + get { return this.callerIdNum; } + set { this.callerIdNum = value; } + } + /// + /// Get/Set the Caller*ID name of the parked channel. + /// + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + + public ParkedCallEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallGiveUpEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallGiveUpEvent.cs new file mode 100644 index 0000000..7618cb3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallGiveUpEvent.cs @@ -0,0 +1,15 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ParkedCallGiveUpEvent is triggered when a channel that has been parked is hung up.
+ /// It is implemented in res/res_features.c
+ /// Available since Asterisk 1.2 + ///
+ public class ParkedCallGiveUpEvent : AbstractParkedCallEvent + { + public ParkedCallGiveUpEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallTimeOutEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallTimeOutEvent.cs new file mode 100644 index 0000000..e135425 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallTimeOutEvent.cs @@ -0,0 +1,15 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ParkedCallTimeOutEvent is triggered when call parking times out for a given channel.
+ /// It is implemented in res/res_features.c
+ /// Available since Asterisk 1.2 + ///
+ public class ParkedCallTimeOutEvent : AbstractParkedCallEvent + { + public ParkedCallTimeOutEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallsCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallsCompleteEvent.cs new file mode 100644 index 0000000..9ea4566 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ParkedCallsCompleteEvent.cs @@ -0,0 +1,15 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ParkedCallsCompleteEvent is triggered after all parked calls have been reported in response to a ParkedCallsAction. + /// + /// + /// + public class ParkedCallsCompleteEvent : ResponseEvent + { + public ParkedCallsCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/PeerEntryEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerEntryEvent.cs new file mode 100644 index 0000000..c9b81e5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerEntryEvent.cs @@ -0,0 +1,118 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A PeerEntryEvent is triggered in response to a SIPPeersAction or SIPShowPeerAction and contains information about a peer.
+ /// It is implemented in channels/chan_sip.c + ///
+ public class PeerEntryEvent : ResponseEvent + { + private string channelType; + private string objectName; + private string chanObjectType; + private string ipAddress; + private int ipPort; + private bool dynamic; + private bool natSupport; + private bool videoSupport; + private bool textSupport; + private bool acl; + private string status; + private bool realtimedevice; + + /// + /// For SIP peers this is "SIP". + /// + public string ChannelType + { + get { return this.channelType; } + set { this.channelType = value; } + } + + public string ObjectName + { + get { return this.objectName; } + set { this.objectName = value; } + } + /// + /// For SIP peers this is either "peer" or "user". + /// + public string ChanObjectType + { + get { return this.chanObjectType; } + set { this.chanObjectType = value; } + } + /// + /// Get/Set the IP address of the peer. + /// + public string IpAddress + { + get { return this.ipAddress; } + set { this.ipAddress = value; } + } + public int IpPort + { + get { return this.ipPort; } + set { this.ipPort = value; } + } + public bool Dynamic + { + get { return this.dynamic; } + set { this.dynamic = value; } + } + public bool NatSupport + { + get { return this.natSupport; } + set { this.natSupport = value; } + } + public bool VideoSupport + { + get { return this.videoSupport; } + set { this.videoSupport = value; } + } + public bool TextSupport + { + get { return this.textSupport; } + set { this.textSupport = value; } + } + public bool Acl + { + get { return this.acl; } + set { this.acl = value; } + } + /// + /// Get/Set the status of this peer.
+ /// For SIP peers this is one of: + ///
+ ///
"UNREACHABLE"
+ ///
+ ///
"LAGGED (%d ms)"
+ ///
+ ///
"OK (%d ms)"
+ ///
+ ///
"UNKNOWN"
+ ///
+ ///
"Unmonitored"
+ ///
+ ///
+ ///
+ public string Status + { + get { return this.status; } + set { this.status = value; } + } + + public bool RealtimeDevice + { + get { return this.realtimedevice; } + set { this.realtimedevice = value; } + } + + /// + /// Creates a new instance. + /// + public PeerEntryEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/PeerStatusEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerStatusEvent.cs new file mode 100644 index 0000000..291269a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerStatusEvent.cs @@ -0,0 +1,83 @@ +namespace Asterisk.NET.Manager.Event +{ + + /// + /// A PeerStatusEvent is triggered when a SIP or IAX client attempts to registrer at this asterisk server.
+ /// This event is implemented in channels/chan_iax2.c and channels/chan_sip.c + ///
+ public class PeerStatusEvent : ManagerEvent + { + private string channelType; + private string peer; + private string peerStatus; + private string cause; + private int time; + + public PeerStatusEvent(ManagerConnection source) + : base(source) + { + } + + /// + /// Channel type + /// "SIP", + /// "IAX2 + /// + public string ChannelType + { + get { return channelType; } + set { this.channelType = value; } + } + /// + /// Returns the name of the peer that registered. The peer's name starts with "IAX2/" if it is an + /// IAX client or "SIP/" if it is a SIP client. It is followed by the username that is used for + /// registration. + /// + /// Sets the name of the peer that registered. + public string Peer + { + get { return peer; } + set { this.peer = value; } + } + /// Returns the registration state.
+ /// This may be one of + ///
    + ///
  • Registered
  • + ///
  • Unregistered
  • + ///
  • Reachable
  • + ///
  • Lagged
  • + ///
  • Unreachable
  • + ///
  • Rejected (IAX only)
  • + ///
+ ///
+ /// Sets the registration state. + public string PeerStatus + { + get { return peerStatus; } + set { this.peerStatus = value; } + } + /// + /// Returns the cause of a rejection or unregistration.
+ /// For IAX peers this is set only if the status equals "Rejected".
+ /// For SIP peers this is set if the status equals "Unregistered" and the peer was unregistered + /// due to an expiration. In that case the cause is set to "Expired". + ///
+ /// Sets the cause of the rejection or unregistration. + public string Cause + { + get { return cause; } + set { this.cause = value; } + } + /// + /// Returns the ping time of the client if status equals "Reachable" or "Lagged"; if the status + /// equals "Unreachable" it returns how long the last response took (in ms) for IAX peers or -1 + /// for SIP peers. + /// + public int Time + { + get { return time; } + set { this.time = value; } + } + + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/PeerlistCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerlistCompleteEvent.cs new file mode 100644 index 0000000..6caca4e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/PeerlistCompleteEvent.cs @@ -0,0 +1,27 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A PeerlistCompleteEvent is triggered after the details of all peers has been reported in response to an SIPPeersAction or SIPShowPeerAction.
+ /// Available since Asterisk 1.2 + ///
+ /// + /// + /// + public class PeerlistCompleteEvent : ResponseEvent + { + private int listItems; + + /// + /// Get/Set the number of PeerEvents that have been reported. + public int ListItems + { + get { return listItems; } + set { this.listItems = value; } + } + + public PeerlistCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueCallerAbandonEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueCallerAbandonEvent.cs new file mode 100644 index 0000000..90f5a85 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueCallerAbandonEvent.cs @@ -0,0 +1,40 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class QueueCallerAbandonEvent : ManagerEvent + { + private string queue; + private int position; + private int originalPosition; + private int holdTime; + + public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + public int HoldTime + { + get { return this.holdTime; } + set { this.holdTime = value; } + } + public int Position + { + get { return this.position; } + set { this.position = value; } + } + public int OriginalPosition + { + get { return this.originalPosition; } + set { this.originalPosition = value; } + } + /// + /// Creates a new DNDStateEvent. + /// + public QueueCallerAbandonEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEntryEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEntryEvent.cs new file mode 100644 index 0000000..f0b88f0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEntryEvent.cs @@ -0,0 +1,62 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueEntryEvent is triggered in response to a QueueStatusAction and contains information about an entry in a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ /// + public class QueueEntryEvent : ResponseEvent + { + private string queue; + private int position; + private string callerId; + private string callerIdName; + private long wait; + + /// + /// Get/Set the name of the queue that contains this entry. + /// + public string Queue + { + get { return this.queue; } + set { this.queue = value; } + } + /// + /// Get/Set the position of this entry in the queue. + /// + public int Position + { + get { return this.position; } + set { this.position = value; } + } + /// + /// Get/Set the the Caller*ID number of this entry. + /// + public string CallerId + { + get { return this.callerId; } + set { this.callerId = value; } + } + /// + /// Get/Set the Caller*ID name of this entry. + /// + public string CallerIdName + { + get { return this.callerIdName; } + set { this.callerIdName = value; } + } + /// + /// Get/Set the number of seconds this entry has spent in the queue. + /// + public long Wait + { + get { return this.wait; } + set { this.wait = value; } + } + + public QueueEntryEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEvent.cs new file mode 100644 index 0000000..cee85a5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueEvent.cs @@ -0,0 +1,33 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class providing common properties for JoinEvent and LeaveEvent. + /// + public abstract class QueueEvent : ManagerEvent + { + private string queue; + private int count; + + /// + /// Get/Set the number of elements in the queue, i.e. the number of calls waiting to be answered by an agent. + /// + public int Count + { + get { return count; } + set { this.count = value; } + } + /// + /// Get/Set the name of the queue. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + + public QueueEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberAddedEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberAddedEvent.cs new file mode 100644 index 0000000..56997de --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberAddedEvent.cs @@ -0,0 +1,101 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberAddedEvent is triggered when a queue member is added to a queue.
+ /// It is implemented in apps/app_queue.c.
+ /// Available since Asterisk 1.2 + ///
+ public class QueueMemberAddedEvent : AbstractQueueMemberEvent + { + private string memberName; + private string membership; + private int penalty; + private int callsTaken; + private long lastCall; + private int status; + private bool paused; + + /// + /// Returns the name of the member's interface.
+ /// E.g. the channel name or agent group. + ///
+ public string MemberName + { + get { return this.memberName; } + set { this.memberName = value; } + } + + /// + /// Get/Set if the added member is a dynamic or static queue member. + /// "dynamic" if the added member is a dynamic queue member, + /// "static" if the added member is a static queue member. + /// + public string Membership + { + get { return membership; } + set { this.membership = value; } + } + /// + /// Get/Set the penalty for the added member. When calls are distributed + /// members with higher penalties are considered last. + /// + public int Penalty + { + get { return penalty; } + set { this.penalty = value; } + } + /// + /// Get/Set the number of calls answered by the member. + /// + public int CallsTaken + { + get { return callsTaken; } + set { this.callsTaken = value; } + } + /// + /// Get/Set the time (in seconds since 01/01/1970) the last successful call answered by the added member was hungup. + /// + public long LastCall + { + get { return lastCall; } + set { this.lastCall = value; } + } + /// + /// Get/Set the status of this queue member.
+ /// Valid status codes are:
+ ///
+ ///
AST_DEVICE_UNKNOWN (0)
+ ///
Queue member is available
+ ///
AST_DEVICE_NOT_INUSE (1)
+ ///
?
+ ///
AST_DEVICE_INUSE (2)
+ ///
?
+ ///
AST_DEVICE_BUSY (3)
+ ///
?
+ ///
AST_DEVICE_INVALID (4)
+ ///
?
+ ///
AST_DEVICE_UNAVAILABLE (5)
+ ///
?
+ ///
+ ///
+ public int Status + { + get { return status; } + set { this.status = value; } + } + /// + /// Get/Set value if this queue member is paused (not accepting calls).
+ /// true if this member has been paused or false if not. + ///
+ public bool Paused + { + get { return paused; } + set { this.paused = value; } + } + + public QueueMemberAddedEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberEvent.cs new file mode 100644 index 0000000..48bdbff --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberEvent.cs @@ -0,0 +1,131 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberEvent is triggered in response to a QueueStatusAction and contains information about a member of a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ /// + public class QueueMemberEvent : ResponseEvent + { + private string queue; + private string location; + private string memberName; + private string membership; + private int penalty; + private int callsTaken; + private long lastCall; + private int status; + private bool paused; + private string name; + + /// + /// Get/Set the name of the queue member. + /// + public string Name + { + get { return this.name; } + set { this.name = value; } + } + + /// + /// Get/Set the name of the queue member. + /// + public string MemberName + { + get { return this.memberName; } + set { this.memberName = value; } + } + + /// + /// Get/Set the name of the queue. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + /// + /// Get/Set the name of the member's interface.
+ /// E.g. the channel name or agent group. + ///
+ public string Location + { + get { return location; } + set { this.location = value; } + } + /// + /// Get/Set value if this member has been dynamically added by the QueueAdd command + /// (in the dialplan or via the Manager API) or if this member is has been + /// statically defined in queues.conf. + /// "dynamic" if the added member is a dynamic queue member, "static" if the added member is a static queue member. + /// + public string Membership + { + get { return membership; } + set { this.membership = value; } + } + /// + /// Get/Set the penalty for the added member. When calls are distributed members with higher penalties are considered last. + /// + public int Penalty + { + get { return this.penalty; } + set { this.penalty = value; } + } + /// + /// Get/Set the number of calls answered by the member. + /// + public int CallsTaken + { + get { return this.callsTaken; } + set { this.callsTaken = value; } + } + /// + /// Get/Set the time (in seconds since 01/01/1970) the last successful call answered by the added member was hungup. + /// + public long LastCall + { + get { return this.lastCall; } + set { this.lastCall = value; } + } + /// + /// Get/Set the status of this queue member.
+ /// Available since Asterisk 1.2
+ /// Valid status codes are: + ///
+ ///
AST_DEVICE_UNKNOWN (0)
+ ///
Queue member is available
+ ///
AST_DEVICE_NOT_INUSE (1)
+ ///
?
+ ///
AST_DEVICE_INUSE (2)
+ ///
?
+ ///
AST_DEVICE_BUSY (3)
+ ///
?
+ ///
AST_DEVICE_INVALID (4)
+ ///
?
+ ///
AST_DEVICE_UNAVAILABLE (5)
+ ///
?
+ ///
+ ///
+ public int Status + { + get { return this.status; } + set { this.status = value; } + } + /// + /// Is this queue member paused (not accepting calls)?
+ /// Available since Asterisk 1.2.
+ /// true if this member has been paused, + /// false if not + public bool Paused + { + get { return this.paused; } + set { this.paused = value; } + } + + public QueueMemberEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPausedEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPausedEvent.cs new file mode 100644 index 0000000..ca3cea0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPausedEvent.cs @@ -0,0 +1,45 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberPausedEvent is triggered when a queue member is paused or unpaused.
+ /// It is implemented in apps/app_queue.c.
+ /// Available since Asterisk 1.2 + ///
+ public class QueueMemberPausedEvent : AbstractQueueMemberEvent + { + private string memberName; + private bool paused; + private string reason; + + /// + /// Returns the name of the member's interface.
+ /// E.g. the channel name or agent group. + ///
+ public string MemberName + { + get { return this.memberName; } + set { this.memberName = value; } + } + + /// + /// Get/Set if this queue member is paused (not accepting calls).
+ /// true if this member has been paused or + /// false if not. + ///
+ public bool Paused + { + get { return this.paused; } + set { this.paused = value; } + } + + public string Reason + { + get { return this.reason; } + set { this.reason = value; } + } + public QueueMemberPausedEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPenaltyEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPenaltyEvent.cs new file mode 100644 index 0000000..c801cf0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberPenaltyEvent.cs @@ -0,0 +1,24 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberPenaltyEvent is triggered when a queue member is assigned a new penalty. + /// + public class QueueMemberPenaltyEvent : AbstractQueueMemberEvent + { + private int penalty; + + /// + /// Get/Set the penalty for the queue location. + /// + public int Penalty + { + get { return this.penalty; } + set { this.penalty = value; } + } + + public QueueMemberPenaltyEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberRemovedEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberRemovedEvent.cs new file mode 100644 index 0000000..0b29345 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberRemovedEvent.cs @@ -0,0 +1,27 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberRemovedEvent is triggered when a queue member is removed from a queue.
+ /// It is implemented in apps/app_queue.c.
+ /// Available since Asterisk 1.2 + ///
+ public class QueueMemberRemovedEvent : AbstractQueueMemberEvent + { + private string memberName; + + /// + /// Returns the name of the member's interface.
+ /// E.g. the channel name or agent group. + ///
+ public string MemberName + { + get { return this.memberName; } + set { this.memberName = value; } + } + + public QueueMemberRemovedEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberStatusEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberStatusEvent.cs new file mode 100644 index 0000000..b0e7cee --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueMemberStatusEvent.cs @@ -0,0 +1,13 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueMemberStatusEvent shows the status of a QueueMemberEvent + /// + public class QueueMemberStatusEvent : QueueMemberEvent + { + public QueueMemberStatusEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueParamsEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueParamsEvent.cs new file mode 100644 index 0000000..d2754e4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueParamsEvent.cs @@ -0,0 +1,114 @@ +using System; +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueParamsEvent is triggered in response to a QueueStatusAction and contains the parameters of a queue.
+ /// It is implemented in apps/app_queue.c + ///
+ /// + public class QueueParamsEvent : ResponseEvent + { + private string queue; + private int max; + private int calls; + private int holdtime; + private int completed; + private int abandoned; + private int serviceLevel; + private Double serviceLevelPerf; + private int weight; + private string strategy; + + /// + /// Get/Set queue strategy. + /// + public string Strategy + { + get { return strategy; } + set { this.strategy = value; } + } + /// + /// Get/Set the name of the queue. + /// + public string Queue + { + get { return queue; } + set { this.queue = value; } + } + /// Returns the maximum number of people waiting in the queue or 0 for unlimited.
+ /// This corresponds to the maxlen setting in queues.conf. + ///
+ /// Sets the maximum number of people waiting in the queue. + public int Max + { + get { return max; } + set { this.max = value; } + } + /// Returns the number of calls currently waiting in the queue. + /// Sets the number of calls currently waiting in the queue. + public int Calls + { + get { return calls; } + set { this.calls = value; } + } + /// Returns the current average holdtime for this queue (in seconds). + /// Sets the current average holdtime for this queue. + public int Holdtime + { + get { return holdtime; } + set { this.holdtime = value; } + } + /// Returns the number of completed calls. + /// Sets the number of completed calls. + public int Completed + { + get { return completed; } + set { this.completed = value; } + } + /// Returns the number of abandoned calls. + /// Sets the number of abandoned calls. + public int Abandoned + { + get { return abandoned; } + set { this.abandoned = value; } + } + /// Returns the service level (in seconds) as defined by the servicelevel setting + /// in queues.conf. + /// + /// Sets the service level. + public int ServiceLevel + { + get { return serviceLevel; } + set { this.serviceLevel = value; } + } + /// Returns the ratio of calls answered within the specified service level per total completed + /// calls (in percent). + /// + /// Sets the ratio of calls answered within the specified service level per total completed + /// calls. + /// + public Double ServiceLevelPerf + { + get { return serviceLevelPerf; } + set { this.serviceLevelPerf = value; } + } + + /// + /// Returns the weight of this queue.
+ /// A queues can be assigned a 'weight' to ensure calls waiting in a + /// higher priority queue will deliver its calls first. Only delays + /// the lower weight queue's call if the member is also in the + /// higher weight queue. + ///
+ public int Weight + { + get { return weight; } + set { this.weight = value; } + } + + public QueueParamsEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/QueueStatusCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueStatusCompleteEvent.cs new file mode 100644 index 0000000..6fbb9b3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/QueueStatusCompleteEvent.cs @@ -0,0 +1,15 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A QueueStatusCompleteEvent is triggered after the state of all queues has been reported in response to a QueueStatusAction.
+ /// Since Asterisk 1.2 + ///
+ /// + public class QueueStatusCompleteEvent : ResponseEvent + { + public QueueStatusCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPReceivedEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPReceivedEvent.cs new file mode 100644 index 0000000..3fa0cc1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPReceivedEvent.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class RTCPReceivedEvent : ManagerEvent + { + private string from; + private string pt; + private int receptionReports; + private long senderSSRC; + private long fractionLost; + private int packetsLost; + private long highestSequence; + private int sequenceNumberCycles; + private long iaJitter; + private string lastSR; + private string dlsr; + private string rtt; + + public string From + { + get { return this.from; } + set { this.from = value; } + } + + public int SequenceNumberCycles + { + get { return this.sequenceNumberCycles; } + set { this.sequenceNumberCycles = value; } + } + public string RTT + { + get { return this.rtt; } + set { this.rtt = value; } + } + + public long IAJitter + { + get { return this.iaJitter; } + set { this.iaJitter = value; } + } + public string PT + { + get { return this.pt; } + set { this.pt = value; } + } + public int ReceptionReports + { + get { return this.receptionReports; } + set { this.receptionReports = value; } + } + public string LastSR + { + get { return this.lastSR; } + set { this.lastSR = value; } + } + public string DLSR + { + get { return this.dlsr; } + set { this.dlsr = value; } + } + public long FractionLost + { + get { return this.fractionLost; } + set { this.fractionLost = value; } + } + public long SenderSSRC + { + get { return this.senderSSRC; } + set { this.senderSSRC = value; } + } + public long HighestSequence + { + get { return this.highestSequence; } + set { this.highestSequence = value; } + } + public int PacketsLost + { + get { return this.packetsLost; } + set { this.packetsLost = value; } + } + + #region Constructor - RTCPReceivedEvent(ManagerConnection source) + public RTCPReceivedEvent(ManagerConnection source) + : base(source) + { } + #endregion + + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPSentEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPSentEvent.cs new file mode 100644 index 0000000..1e6eea5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RTCPSentEvent.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class RTCPSentEvent : ManagerEvent + { + private string to; + private long ourSSRC; + private string sentNTP; + private long sentRTP; + private long sentPackets; + private long sentOctets; + private long reportBlock; + private long fractionLost; + private long cumulativeLoss; + private double iaJitter; + private long theirLastSr; + private string dlsr; + + public string To + { + get { return this.to; } + set { this.to = value; } + } + + public long CumulativeLoss + { + get { return this.cumulativeLoss; } + set { this.cumulativeLoss = value; } + } + public string SentNtp + { + get { return this.sentNTP; } + set { this.sentNTP = value; } + } + public long SentRtp + { + get { return this.sentRTP; } + set { this.sentRTP = value; } + } + public double IAJitter + { + get { return this.iaJitter; } + set { this.iaJitter = value; } + } + public long SentPackets + { + get { return this.sentPackets; } + set { this.sentPackets = value; } + } + public long SentOctets + { + get { return this.sentOctets; } + set { this.sentOctets = value; } + } + + public long ReportBlock + { + get { return this.reportBlock; } + set { this.reportBlock = value; } + } + + public long FractionLost + { + get { return this.fractionLost; } + set { this.fractionLost = value; } + } + public long OursSrc + { + get { return this.ourSSRC; } + set { this.ourSSRC = value; } + } + public string DlSr + { + get { return this.dlsr; } + set { this.dlsr = value; } + } + public long TheirLastSr + { + get { return this.theirLastSr; } + set { this.theirLastSr = value; } + } + + #region Constructor - RTCPSentEvent(ManagerConnection source) + public RTCPSentEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RTPReceiverStatEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RTPReceiverStatEvent.cs new file mode 100644 index 0000000..4917c42 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RTPReceiverStatEvent.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class RTPReceiverStatEvent : ManagerEvent + { + private long ssrc; + private long receivedPackets; + private long lostPackets; + private double jitter; + private double transit; + private long rrCount; + + public double Transit + { + get { return this.transit; } + set { this.transit = value; } + } + public long LostPackets + { + get { return this.lostPackets; } + set { this.lostPackets = value; } + } + public long RRCount + { + get { return this.rrCount; } + set { this.rrCount = value; } + } + public long ReceivedPackets + { + get { return this.receivedPackets; } + set { this.receivedPackets = value; } + } + public double Jitter + { + get { return this.jitter; } + set { this.jitter = value; } + } + public long SSRC + { + get { return this.ssrc; } + set { this.ssrc = value; } + } + + #region Constructor - RTPReceiverStatEvent(ManagerConnection source) + public RTPReceiverStatEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RTPSenderStatEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RTPSenderStatEvent.cs new file mode 100644 index 0000000..bdcccb8 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RTPSenderStatEvent.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class RTPSenderStatEvent : ManagerEvent + { + private long ssrc; + private long sentPackets; + private long lostPackets; + private long jitter; + private long srCount; + private double rtt; + + + public long SSRC + { + get { return this.ssrc; } + set { this.ssrc = value; } + } + public long SentPackets + { + get { return this.sentPackets; } + set { this.sentPackets = value; } + } + public long LostPackets + { + get { return this.lostPackets; } + set { this.lostPackets = value; } + } + public long SRCount + { + get { return this.srCount; } + set { this.srCount = value; } + } + public long Jitter + { + get { return this.jitter; } + set { this.jitter = value; } + } + public double RTT + { + get { return this.rtt; } + set { this.rtt = value; } + } + + #region Constructor - RTPSenderStatEvent(ManagerConnection source) + public RTPSenderStatEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RegistryEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RegistryEvent.cs new file mode 100644 index 0000000..b7dc533 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RegistryEvent.cs @@ -0,0 +1,95 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A RegistryEvent is triggered when this asterisk server attempts to register + /// as a client at another SIP or IAX server.
+ /// This event is implemented in channels/chan_iax2.c and + /// channels/chan_sip.c + ///
+ public class RegistryEvent : ManagerEvent + { + private string channelType; + private string domain; + private string username; + private string status; + private string cause; + + /// + /// Channel type + /// "SIP", + /// "IAX2" + /// + public string ChannelType + { + get { return this.channelType; } + set { this.channelType = value; } + } + /// + /// Get/Set the domain or host name of the SIP or IAX2 server.
+ /// This is the host part used in the register lines in + /// iax.conf and sip.conf. + ///
+ public string Domain + { + get { return domain; } + set { this.domain = value; } + } + /// + /// Get/Set the username used for registration.
+ /// SIP send the username in case of a registration timeout, IAX2 in case of + /// a registration failure. Otherwise the username is null. + ///
+ public string Username + { + get { return username; } + set { this.username = value; } + } + /// + /// Sets the username used for registration. + /// + /// Please do not use this method it is a workaround for Asterisk 1.0.x servers. See Asterisk bug 4916. + public string User + { + set { this.username = value; } + } + /// + /// Get/Set the registration state.
+ /// For sip this may be one of (not sure if all of these are exposed via the + /// manager api, at least "Registered" and "Timeout" are used though) + ///
    + ///
  • Registered
  • + ///
  • Unregistered
  • + ///
  • Request Sent
  • + ///
  • Auth. Sent
  • + ///
  • Rejected
  • + ///
  • Timeout
  • + ///
  • No Authentication
  • + ///
  • Unreachable
  • + ///
+ /// IAX2 only uses + ///
    + ///
  • Rejected
  • + ///
+ /// Successful IAX2 registrations do not use the this property at all. + ///
+ public string Status + { + get { return status; } + set { this.status = value; } + } + /// + /// Get/Set the cause of a rejected registration. + /// The cause of a rejected registration or "<unknown>" if the cause is unknown. + /// + public string Cause + { + get { return cause; } + set { this.cause = value; } + } + + public RegistryEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ReloadEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ReloadEvent.cs new file mode 100644 index 0000000..8f59e1c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ReloadEvent.cs @@ -0,0 +1,47 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ReloadEvent is triggerd when the reload console command is executed or the asterisk server is started.
+ /// It is implemented in manager.c + ///
+ public class ReloadEvent : ConnectionStateEvent + { + private string message; + private string module; + private string status; + + /// Reload event status. + public string Status + { + get { return this.status; } + set { this.status = value; } + } + /// Returns + /// "Manager" + /// "Enum" + /// "DNSmgr" + /// "CDR" + /// + public string Module + { + get { return this.module; } + set { this.module = value; } + } + /// Returns + /// "Reload Requested", + /// "ENUM reload Requested", + /// "DNSmgr reload Requested", + /// "CDR subsystem reload requested" + /// . + public string Message + { + get { return this.message; } + set { this.message = value; } + } + + public ReloadEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/RenameEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/RenameEvent.cs new file mode 100644 index 0000000..2b97514 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/RenameEvent.cs @@ -0,0 +1,34 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A RenameEvent is triggered when the name of a channel is changed.
+ /// It is implemented in channel.c + ///
+ public class RenameEvent : ManagerEvent + { + protected internal string oldName; + protected internal string newName; + + /// + /// Get/Set the new name of the channel. + /// + public string NewName + { + get { return newName; } + set { this.newName = value; } + } + /// + /// Get/Set the old name of the channel. + /// + public string OldName + { + get { return oldName; } + set { this.oldName = value; } + } + + public RenameEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ResponseEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ResponseEvent.cs new file mode 100644 index 0000000..bc83ed0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ResponseEvent.cs @@ -0,0 +1,35 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for events triggered in response to a ManagerAction.
+ /// All ResponseEvents contain an additional action id property that links the + /// event to the action that caused it. + ///
+ public abstract class ResponseEvent : ManagerEvent + { + private string actionId; + private string internalActionId; + + /// + /// Get/Set the action id of the ManagerAction that caused this event. + /// + public string ActionId + { + get { return actionId; } + set { this.actionId = value; } + } + /// + /// Get/Set the internal action id of the ManagerAction that caused this event. + /// + public string InternalActionId + { + get { return internalActionId; } + set { this.internalActionId = value; } + } + + public ResponseEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ShowDialPlanCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ShowDialPlanCompleteEvent.cs new file mode 100644 index 0000000..7c62b9d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ShowDialPlanCompleteEvent.cs @@ -0,0 +1,44 @@ +using System; + +namespace Asterisk.NET.Manager.Event +{ + public class ShowDialPlanCompleteEvent : ManagerEvent + { + private string eventList; + private int listItems; + private int listExtensions; + private int listPriorities; + private int listContexts; + + public string EventList + { + get { return this.eventList; } + set { this.eventList = value; } + } + public int ListItems + { + get { return this.listItems; } + set { this.listItems = value; } + } + public int ListExtensions + { + get { return this.listExtensions; } + set { this.listExtensions = value; } + } + public int ListPriorities + { + get { return this.listPriorities; } + set { this.listPriorities = value; } + } + public int ListContexts + { + get { return this.listContexts; } + set { this.listContexts = value; } + } + + public ShowDialPlanCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ShutdownEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ShutdownEvent.cs new file mode 100644 index 0000000..6370a24 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ShutdownEvent.cs @@ -0,0 +1,60 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ShutdownEvent is triggered when the asterisk server is shut down or restarted.
+ /// It is implemented in asterisk.c + ///
+ public class ShutdownEvent : ConnectionStateEvent + { + private string shutdown; + private bool restart = false; + private string file; + private string func; + private int line; + private int sequencenumber; + + /// + /// Get/Set the kind of shutdown or restart. Possible values are "Uncleanly" and "Cleanly". A + /// shutdown is considered unclean if there are any active channels when the system is shut down. + /// + public string Shutdown + { + get { return shutdown; } + set { this.shutdown = value; } + } + /// + /// Get/Set true if the server has been restarted; false if it has been halted. + /// + public bool Restart + { + get { return restart; } + set { this.restart = value; } + } + + public string File + { + get { return file; } + set { file = value; } + } + public string Func + { + get { return func; } + set { func = value; } + } + public int Line + { + get { return line; } + set { line = value; } + } + public int SequenceNumber + { + get { return sequencenumber; } + set { sequencenumber = value; } + } + + public ShutdownEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/StatusCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/StatusCompleteEvent.cs new file mode 100644 index 0000000..c07944b --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/StatusCompleteEvent.cs @@ -0,0 +1,24 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A StatusCompleteEvent is triggered after the state of all channels has been reported in response + /// to a StatusAction. + /// + /// + /// + public class StatusCompleteEvent : ResponseEvent + { + private int items; + + public int Items + { + get { return this.items; } + set { this.items = value; } + } + + public StatusCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/StatusEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/StatusEvent.cs new file mode 100644 index 0000000..d321edc --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/StatusEvent.cs @@ -0,0 +1,166 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A StatusEvent is triggered for each active channel in response to a StatusAction. + /// + /// + public class StatusEvent : ResponseEvent + { + /// Returns the Caller*ID of this channel. + /// Sets the Caller*ID of this channel. + public string CallerId + { + get + { + return callerId; + } + + set + { + this.callerId = value; + } + + } + /// Returns the Caller*ID Name of this channel. + /// Sets the Caller*ID Name of this channel. + public string CallerIdName + { + get + { + return callerIdName; + } + + set + { + this.callerIdName = value; + } + + } + /// Returns the Caller*ID Number of this channel. + /// Sets the Caller*ID Number of this channel. + public string CallerIdNum + { + get + { + return callerIdNum; + } + + set + { + this.callerIdNum = value; + } + + } + /// Returns the account code of this channel. + /// Sets the account code of this channel. + public string Account + { + get + { + return account; + } + + set + { + this.account = value; + } + + } + public string State + { + get + { + return state; + } + + set + { + this.state = value; + } + + } + public string Context + { + get + { + return context; + } + + set + { + this.context = value; + } + + } + public string Extension + { + get + { + return extension; + } + + set + { + this.extension = value; + } + + } + public int Priority + { + get + { + return priority; + } + + set + { + this.priority = value; + } + + } + /// Returns the number of elapsed seconds. + /// Sets the number of elapsed seconds. + public int Seconds + { + get + { + return seconds; + } + + set + { + this.seconds = value; + } + + } + /// Returns the name of the linked channel if this channel is bridged. + /// Sets the name of the linked channel. + public string Link + { + get + { + return link; + } + + set + { + this.link = value; + } + + } + private string callerId; + private string callerIdName; + private string callerIdNum; + private string account; + private string state; + private string context; + private string extension; + private int priority; + private int seconds = -1; + private string link; + + public StatusEvent(ManagerConnection source): base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/TransferEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/TransferEvent.cs new file mode 100644 index 0000000..b52c0ae --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/TransferEvent.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class TransferEvent : ManagerEvent + { + private string transferMethod; + private string transferExten; + private string transferType; + private string sipCallId; + private string targetChannel; + private string targetUniqueId; + private string transferContext; + private bool transfer2Parking; + + /// + /// SIP, + /// + public string TransferMethod + { + get { return this.transferMethod; } + set { this.transferMethod = value; } + } + public string TransferExten + { + get { return this.transferExten; } + set { this.transferExten = value; } + } + + /// + /// Blind, + /// Attended + /// + public string TransferType + { + get { return this.transferType; } + set { this.transferType = value; } + } + public string SipCallId + { + get { return this.sipCallId; } + set { this.sipCallId = value; } + } + public string TargetUniqueId + { + get { return this.targetUniqueId; } + set { this.targetUniqueId = value; } + } + public string TargetChannel + { + get { return this.targetChannel; } + set { this.targetChannel = value; } + } + public string TransferContext + { + get { return this.transferContext; } + set { this.transferContext = value; } + } + + public bool Transfer2Parking + { + get { return this.transfer2Parking; } + set { this.transfer2Parking = value; } + } + + #region Constructor - TransferEvent(ManagerConnection source) + public TransferEvent(ManagerConnection source) + : base(source) + { + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/UnholdEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/UnholdEvent.cs new file mode 100644 index 0000000..ef4a29a --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/UnholdEvent.cs @@ -0,0 +1,19 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An UnholdEvent is triggered by the SIP channel driver when a channel is no longer put on hold.
+ /// It is implemented in channels/chan_sip.c.
+ /// Available since Asterisk 1.2 + ///
+ /// + public class UnholdEvent : ManagerEvent + { + /// + /// Creates a new UnholdEvent. + /// + public UnholdEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/UnknownEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/UnknownEvent.cs new file mode 100644 index 0000000..2915a57 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/UnknownEvent.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + /// + /// A UnknownEvent is triggered on unknown event from manager/proxy. + /// + public class UnknownEvent : ManagerEvent + { + /// + /// Creates a new UnknownEvent. + /// + public UnknownEvent(ManagerConnection source) + : base(source) + { + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/UnlinkEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/UnlinkEvent.cs new file mode 100644 index 0000000..616b0f3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/UnlinkEvent.cs @@ -0,0 +1,16 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// An UnlinkEvent is triggered when a link between two voice channels is discontinued, + /// for example, just before call completion. + /// + public class UnlinkEvent : BridgeEvent + { + public UnlinkEvent(ManagerConnection source) + : base(source) + { + isunlink = true; + bridgeState = BridgeStates.BRIDGE_STATE_UNLINK; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/UnparkedCallEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/UnparkedCallEvent.cs new file mode 100644 index 0000000..ae977b0 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/UnparkedCallEvent.cs @@ -0,0 +1,26 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A UnparkedCallEvent is triggered when a channel that has been parked is resumed.
+ /// It is implemented in res/res_features.c
+ /// Available since Asterisk 1.2 + ///
+ public class UnparkedCallEvent : AbstractParkedCallEvent + { + private string from; + + /// + /// Get/Set the name of the channel that parked the call. + /// + public string From + { + get { return this.from; } + set { this.from = value; } + } + + public UnparkedCallEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/UserEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/UserEvent.cs new file mode 100644 index 0000000..2a00258 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/UserEvent.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Event +{ + /// + /// Abstract base class for user events.
+ /// You can send arbitrary user events via the UserEvent application provided with asterisk. + /// A user event by default has the attributes channel and uniqueId but you can add custom + /// attributes by specifying an event body.
+ /// To add your own user events you must subclass this class and name it corresponding to your event.
+ /// If you plan to send an event by UserEvent(VIPCall) you will create a new class + /// called VIPCallEvent that extends UserEvent. The name of this class is important: Just use the + /// name of the event you will send (VIPCall in this example) and append "Event".
+ /// To pass additional data create appropriate attributes with getter and setter methods in your new class.
+ /// Example: + ///
+	 /// public class VIPCallEvent : UserEvent
+	 /// {
+	/// 	 private string firstName;
+	/// 	 // Constructor
+	/// 	 public VIPCallEvent()
+	/// 	 {
+	/// 	 }
+	/// 	 // Property
+	/// 	 public string FirstName
+	/// 	 {
+	/// 		 get { return this.firstName; }
+	/// 		 set { this.firstName = value; }
+	/// 	 }
+	 ///  }
+	 /// 
+ /// To send this event use UserEvent(VIPCall|firstName: Jon) in your dialplan.
+ /// The UserEvent is implemented in apps/app_userevent.c.
+ /// Note that you must register your UserEvent with the ManagerConnection you are using in order to be recognized. + ///
+ /// + public class UserEvent : ManagerEvent + { + private string userEventName; + + public override bool Parse(string key, string value) + { + if (key == "userevent") + userEventName = value; + return base.Parse(key, value); + } + + public string UserEventName + { + get { return userEventName; } + set { userEventName = value; } + } + + public UserEvent(ManagerConnection source) + : base(source) + { } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/VarSet.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/VarSet.cs new file mode 100644 index 0000000..68a478c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/VarSet.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Event +{ + public class VarSetEvent : ManagerEvent + { + private string variable_name; + private string variable_value; + private string file; + private string func; + private int line; + private int sequencenumber; + + public VarSetEvent(ManagerConnection source) + : base(source) + { + } + + public string File + { + get { return file; } + set { file = value; } + } + public string Func + { + get { return func; } + set { func = value; } + } + public int Line + { + get { return line; } + set { line = value; } + } + public int SequenceNumber + { + get { return sequencenumber; } + set { sequencenumber = value; } + } + + /// + /// Get/Set the name of variable. + /// + public string Variable + { + get { return variable_name; } + set { variable_name = value; } + } + + /// + /// Get/Set value of variable. + /// + public string Value + { + get { return variable_value; } + set { variable_value = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsCompleteEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsCompleteEvent.cs new file mode 100644 index 0000000..aa7d311 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsCompleteEvent.cs @@ -0,0 +1,16 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ZapShowChannelsCompleteEvent is triggered after the state of all zap channels has been reported + /// in response to a ZapShowChannelsAction. + /// + /// + /// + public class ZapShowChannelsCompleteEvent : ResponseEvent + { + public ZapShowChannelsCompleteEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsEvent.cs b/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsEvent.cs new file mode 100644 index 0000000..6a86238 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Event/ZapShowChannelsEvent.cs @@ -0,0 +1,79 @@ +namespace Asterisk.NET.Manager.Event +{ + /// + /// A ZapShowChannelsEvent is triggered in response to a ZapShowChannelsAction and shows the state of a zap channel. + /// + /// + public class ZapShowChannelsEvent : ResponseEvent + { + private string signalling; + private string context; + private string alarm; + + /// + /// Get/Set the signalling of this zap channel.
+ /// Possible values are: + ///
    + ///
  • E & M Immediate
  • + ///
  • E & M Wink
  • + ///
  • E & M E1
  • + ///
  • Feature Group D (DTMF)
  • + ///
  • Feature Group D (MF)
  • + ///
  • Feature Group B (MF)
  • + ///
  • E911 (MF)
  • + ///
  • FXS Loopstart
  • + ///
  • FXS Groundstart
  • + ///
  • FXS Kewlstart
  • + ///
  • FXO Loopstart
  • + ///
  • FXO Groundstart
  • + ///
  • FXO Kewlstart
  • + ///
  • PRI Signalling
  • + ///
  • R2 Signalling
  • + ///
  • SF (Tone) Signalling Immediate
  • + ///
  • SF (Tone) Signalling Wink
  • + ///
  • SF (Tone) Signalling with Feature Group D (DTMF)
  • + ///
  • SF (Tone) Signalling with Feature Group D (MF)
  • + ///
  • SF (Tone) Signalling with Feature Group B (MF)
  • + ///
  • GR-303 Signalling with FXOKS
  • + ///
  • GR-303 Signalling with FXSKS
  • + ///
  • Pseudo Signalling
  • + ///
+ ///
+ public string Signalling + { + get { return signalling; } + set { this.signalling = value; } + } + /// + /// Get/Set the context of this zap channel as defined in zapata.conf. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + /// + /// Get/Set the alarm state of this zap channel.
+ /// This may be one of + ///
    + ///
  • Red Alarm
  • + ///
  • Yellow Alarm
  • + ///
  • Blue Alarm
  • + ///
  • Recovering
  • + ///
  • Loopback
  • + ///
  • Not Open
  • + ///
  • No Alarm
  • + ///
+ ///
+ public string Alarm + { + get { return alarm; } + set { this.alarm = value; } + } + + public ZapShowChannelsEvent(ManagerConnection source) + : base(source) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Exceptions/AuthenticationFailedException.cs b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/AuthenticationFailedException.cs new file mode 100644 index 0000000..9ca6f7f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/AuthenticationFailedException.cs @@ -0,0 +1,28 @@ +using System; +namespace Asterisk.NET.Manager +{ + /// + /// An AuthenticationFailedException is thrown when a login fails due to an incorrect username and/or password. + /// + public class AuthenticationFailedException : Exception + { + /// + /// Creates a new AuthenticationFailedException with the given message. + /// + /// message describing the authentication failure + public AuthenticationFailedException(string message) + : base(message) + { + } + + /// + /// Creates a new AuthenticationFailedException with the given message and cause. + /// + /// message describing the authentication failure + /// exception that caused the authentication failure + public AuthenticationFailedException(string message, Exception cause) + : base(message, cause) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Exceptions/EventTimeoutException.cs b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/EventTimeoutException.cs new file mode 100644 index 0000000..360c3f4 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/EventTimeoutException.cs @@ -0,0 +1,41 @@ +namespace Asterisk.NET.Manager +{ + /// + /// An EventTimeoutException is thrown if a ManagerResponse or some + /// ResponseEvents are not completely received within the expected time period.
+ /// This exception allows you to retrieve the partial result, that is the events + /// that have been successfully received before the timeout occured. + ///
+ public class EventTimeoutException:TimeoutException + { + /// + /// Returns the partial result that has been received before the timeout occured.
+ /// Note: Using the partial result in your application should be avoided + /// wherever possible. This is only a hack to handle those versions of + /// Asterisk that don't follow the Manager API conventions, for example by + /// not sending the correct ActionCompleteEvent. + ///
+ /// + /// the ResponseEvents object filled with the parts that have been + /// received before the timeout occured. Note: The response attribute + /// may be null when no response has been received. + /// + public ResponseEvents PartialResult + { + get { return partialResult; } + } + private ResponseEvents partialResult; + + /// + /// Creates a new EventTimeoutException with the given message and partial result. + /// + /// message with details about the timeout. + /// the ResponseEvents object filled with the parts that + /// have been received before the timeout occured. + /// + public EventTimeoutException(string message, ResponseEvents partialResult): base(message) + { + this.partialResult = partialResult; + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Exceptions/ManagerException.cs b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/ManagerException.cs new file mode 100644 index 0000000..340524f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/ManagerException.cs @@ -0,0 +1,28 @@ +using System; +namespace Asterisk.NET.Manager +{ + /// + /// An ManagerException is thrown when a Manager Error Response. + /// + public class ManagerException : Exception + { + /// + /// Creates a new ManagerException with the given message. + /// + /// message describing the manager exception + public ManagerException(string message) + : base(message) + { + } + + /// + /// Creates a new ManagerException with the given message and cause. + /// + /// message describing the manager exception + /// exception that caused the manager exception + public ManagerException(string message, Exception cause) + : base(message, cause) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Exceptions/TimeoutException.cs b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/TimeoutException.cs new file mode 100644 index 0000000..54d1194 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Exceptions/TimeoutException.cs @@ -0,0 +1,18 @@ +using System; +namespace Asterisk.NET.Manager +{ + /// + /// A TimeoutException is thrown if a ManagerResponse is not received within the expected time period. + /// + public class TimeoutException : Exception + { + /// + /// Creates a new TimeoutException with the given message. + /// + /// message with details about the timeout. + public TimeoutException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/IResponseHandler.cs b/Asterisk.2013/Asterisk.NET/Manager/IResponseHandler.cs new file mode 100644 index 0000000..2783ce3 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/IResponseHandler.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Asterisk.NET.Manager.Action; + +namespace Asterisk.NET.Manager +{ + /// + /// An Interface to handle responses received from an asterisk server. + /// + /// + public interface IResponseHandler + { + /// + /// This method is called when a response is received. + /// + /// the response received + void HandleResponse(Response.ManagerResponse response); + + void Free(); + + ManagerAction Action + { + get; + } + + int Hash + { + get; + set; + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs b/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs new file mode 100644 index 0000000..ab09ea7 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/ManagerConnection.cs @@ -0,0 +1,2217 @@ +using System; +using System.IO; +using System.Threading; +using System.Collections; +using Asterisk.NET.Manager.Action; +using Asterisk.NET.Manager.Event; +using Asterisk.NET.Manager.Response; +using System.Text.RegularExpressions; +using System.Text; +using System.Net.Sockets; +using System.Collections.Generic; +using System.Reflection; +using Asterisk.NET.IO; +using Asterisk.NET.Util; + +namespace Asterisk.NET.Manager +{ + #region Event delegate + + public delegate void ManagerEventHandler(object sender, ManagerEvent e); + public delegate void AgentCallbackLoginEventHandler(object sender, Event.AgentCallbackLoginEvent e); + public delegate void AgentCallbackLogoffEventHandler(object sender, Event.AgentCallbackLogoffEvent e); + public delegate void AgentCalledEventHandler(object sender, Event.AgentCalledEvent e); + public delegate void AgentCompleteEventHandler(object sender, Event.AgentCompleteEvent e); + public delegate void AgentConnectEventHandler(object sender, Event.AgentConnectEvent e); + public delegate void AgentDumpEventHandler(object sender, Event.AgentDumpEvent e); + public delegate void AgentLoginEventHandler(object sender, Event.AgentLoginEvent e); + public delegate void AgentLogoffEventHandler(object sender, Event.AgentLogoffEvent e); + public delegate void AgentsCompleteEventHandler(object sender, Event.AgentsCompleteEvent e); + public delegate void AgentsEventHandler(object sender, Event.AgentsEvent e); + public delegate void AlarmClearEventHandler(object sender, Event.AlarmClearEvent e); + public delegate void AlarmEventHandler(object sender, Event.AlarmEvent e); + public delegate void CdrEventHandler(object sender, Event.CdrEvent e); + public delegate void DBGetResponseEventHandler(object sender, Event.DBGetResponseEvent e); + public delegate void DialEventHandler(object sender, Event.DialEvent e); + public delegate void DNDStateEventHandler(object sender, Event.DNDStateEvent e); + public delegate void ExtensionStatusEventHandler(object sender, Event.ExtensionStatusEvent e); + public delegate void HangupEventHandler(object sender, Event.HangupEvent e); + public delegate void HoldedCallEventHandler(object sender, Event.HoldedCallEvent e); + public delegate void HoldEventHandler(object sender, Event.HoldEvent e); + public delegate void JoinEventHandler(object sender, Event.JoinEvent e); + public delegate void LeaveEventHandler(object sender, Event.LeaveEvent e); + public delegate void LinkEventHandler(object sender, Event.LinkEvent e); + public delegate void LogChannelEventHandler(object sender, Event.LogChannelEvent e); + public delegate void MeetMeJoinEventHandler(object sender, Event.MeetmeJoinEvent e); + public delegate void MeetMeLeaveEventHandler(object sender, Event.MeetmeLeaveEvent e); + public delegate void MeetMeTalkingEventHandler(object sender, Event.MeetmeTalkingEvent e); + public delegate void MessageWaitingEventHandler(object sender, Event.MessageWaitingEvent e); + public delegate void NewCallerIdEventHandler(object sender, Event.NewCallerIdEvent e); + public delegate void NewChannelEventHandler(object sender, Event.NewChannelEvent e); + public delegate void NewExtenEventHandler(object sender, Event.NewExtenEvent e); + public delegate void NewStateEventHandler(object sender, Event.NewStateEvent e); + public delegate void OriginateResponseEventHandler(object sender, Event.OriginateResponseEvent e); + public delegate void ParkedCallEventHandler(object sender, Event.ParkedCallEvent e); + public delegate void ParkedCallGiveUpEventHandler(object sender, Event.ParkedCallGiveUpEvent e); + public delegate void ParkedCallsCompleteEventHandler(object sender, Event.ParkedCallsCompleteEvent e); + public delegate void ParkedCallTimeOutEventHandler(object sender, Event.ParkedCallTimeOutEvent e); + public delegate void PeerEntryEventHandler(object sender, Event.PeerEntryEvent e); + public delegate void PeerlistCompleteEventHandler(object sender, Event.PeerlistCompleteEvent e); + public delegate void PeerStatusEventHandler(object sender, Event.PeerStatusEvent e); + public delegate void QueueEntryEventHandler(object sender, Event.QueueEntryEvent e); + public delegate void QueueMemberAddedEventHandler(object sender, Event.QueueMemberAddedEvent e); + public delegate void QueueMemberEventHandler(object sender, Event.QueueMemberEvent e); + public delegate void QueueMemberPausedEventHandler(object sender, Event.QueueMemberPausedEvent e); + public delegate void QueueMemberRemovedEventHandler(object sender, Event.QueueMemberRemovedEvent e); + public delegate void QueueMemberStatusEventHandler(object sender, Event.QueueMemberStatusEvent e); + public delegate void QueueParamsEventHandler(object sender, Event.QueueParamsEvent e); + public delegate void QueueStatusCompleteEventHandler(object sender, Event.QueueStatusCompleteEvent e); + public delegate void RegistryEventHandler(object sender, Event.RegistryEvent e); + public delegate void RenameEventHandler(object sender, Event.RenameEvent e); + public delegate void StatusCompleteEventHandler(object sender, Event.StatusCompleteEvent e); + public delegate void StatusEventHandler(object sender, Event.StatusEvent e); + public delegate void UnholdEventHandler(object sender, Event.UnholdEvent e); + public delegate void UnlinkEventHandler(object sender, Event.UnlinkEvent e); + public delegate void UnparkedCallEventHandler(object sender, Event.UnparkedCallEvent e); + public delegate void UserEventHandler(object sender, Event.UserEvent e); + public delegate void ZapShowChannelsCompleteEventHandler(object sender, Event.ZapShowChannelsCompleteEvent e); + public delegate void ZapShowChannelsEventHandler(object sender, Event.ZapShowChannelsEvent e); + public delegate void ConnectionStateEventHandler(object sender, Event.ConnectionStateEvent e); + + #endregion + + /// + /// Default implemention of the ManagerConnection interface. + /// + public class ManagerConnection + { + #region Variables + +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + private long actionIdCount = 0; + private string hostname; + private int port; + private string username; + private string password; + + private SocketConnection mrSocket; + private ThreadClass mrReaderThread; + private ManagerReader mrReader; + + private int defaultResponseTimeout = 2000; + private int defaultEventTimeout = 5000; + private int sleepTime = 50; + private bool keepAlive = true; + private bool keepAliveAfterAuthenticationFailure = false; + private string protocolIdentifier; + private AsteriskVersion asteriskVersion; + private Dictionary responseHandlers; + private Dictionary pingHandlers; + private Dictionary responseEventHandlers; + private int pingInterval = 10000; + + private object lockSocket = new object(); + private object lockHandlers = new object(); + + private bool enableEvents = true; + private string version = string.Empty; + private Encoding socketEncoding = Encoding.ASCII; + private bool reconnected = false; + private bool reconnectEnable = false; + private int reconnectCount; + + private Dictionary registeredEventClasses; + private Dictionary registeredEventHandlers; + private event ManagerEventHandler internalEvent; + private bool fireAllEvents = false; + private Thread callerThread; + private bool traceCallerThread = true; + + /// Default Fast Reconnect retry counter. + private int reconnectRetryFast = 5; + /// Default Maximum Reconnect retry counter. + private int reconnectRetryMax = 10; + /// Default Fast Reconnect interval in milliseconds. + private int reconnectIntervalFast = 5000; + /// Default Slow Reconnect interval in milliseconds. + private int reconnectIntervalMax = 10000; + + #endregion + + #region Events + + /// + /// An UnhandledEvent is triggered on unknown event. + /// + public event ManagerEventHandler UnhandledEvent; + /// + /// An AgentCallbackLogin is triggered when an agent is successfully logged in. + /// + public event AgentCallbackLoginEventHandler AgentCallbackLogin; + /// + /// An AgentCallbackLogoff is triggered when an agent that previously logged in is logged of.
+ ///
+ public event AgentCallbackLogoffEventHandler AgentCallbackLogoff; + /// + /// An AgentCalled is triggered when an agent is ring.
+ /// To enable AgentCalled you have to set eventwhencalled = yes in queues.conf.
+ ///
+ public event AgentCalledEventHandler AgentCalled; + /// + /// An AgentCompleteEvent is triggered when at the end of a call if the caller was connected to an agent. + /// + public event AgentCompleteEventHandler AgentComplete; + /// + /// An AgentConnectEvent is triggered when a caller is connected to an agent. + /// + public event AgentConnectEventHandler AgentConnect; + /// + /// An AgentDumpEvent is triggered when an agent dumps the caller while listening to the queue announcement. + /// + public event AgentDumpEventHandler AgentDump; + /// + /// An AgentLoginEvent is triggered when an agent is successfully logged in using AgentLogin. + /// + public event AgentLoginEventHandler AgentLogin; + /// + /// An AgentCallbackLogoffEvent is triggered when an agent that previously logged in using AgentLogin is logged of. + /// + public event AgentLogoffEventHandler AgentLogoff; + /// + /// An AgentsCompleteEvent is triggered after the state of all agents has been reported in response to an AgentsAction. + /// + public event AgentsCompleteEventHandler AgentsComplete; + /// + /// An AgentsEvent is triggered for each agent in response to an AgentsAction. + /// + public event AgentsEventHandler Agents; + /// + /// An AlarmEvent is triggered when a Zap channel leaves alarm state. + /// + public event AlarmClearEventHandler AlarmClear; + /// + /// An AlarmEvent is triggered when a Zap channel enters or changes alarm state. + /// + public event AlarmEventHandler Alarm; + /// + /// A CdrEvent is triggered when a call detail record is generated, usually at the end of a call. + /// + public event CdrEventHandler Cdr; + public event DBGetResponseEventHandler DBGetResponse; + /// + /// A Dial is triggered whenever a phone attempts to dial someone.
+ ///
+ public event DialEventHandler Dial; + /// + /// A DNDStateEvent is triggered by the Zap channel driver when a channel enters or leaves DND (do not disturb) state. + /// + public event DNDStateEventHandler DNDState; + /// + /// An ExtensionStatus is triggered when the state of an extension changes.
+ ///
+ public event ExtensionStatusEventHandler ExtensionStatus; + /// + /// A Hangup is triggered when a channel is hung up.
+ ///
+ public event HangupEventHandler Hangup; + /// + /// A HoldedCall is triggered when a channel is put on hold.
+ ///
+ public event HoldedCallEventHandler HoldedCall; + /// + /// A Hold is triggered by the SIP channel driver when a channel is put on hold.
+ ///
+ public event HoldEventHandler Hold; + /// + /// A Join is triggered when a channel joines a queue.
+ ///
+ public event JoinEventHandler Join; + /// + /// A Leave is triggered when a channel leaves a queue.
+ ///
+ public event LeaveEventHandler Leave; + /// + /// A Link is triggered when two voice channels are linked together and voice data exchange commences.
+ /// Several Link events may be seen for a single call. This can occur when Asterisk fails to setup a + /// native bridge for the call.This is when Asterisk must sit between two telephones and perform + /// CODEC conversion on their behalf. + ///
+ public event LinkEventHandler Link; + /// + /// A LogChannel is triggered when logging is turned on or off.
+ ///
+ public event LogChannelEventHandler LogChannel; + /// + /// A MeetMeJoin is triggered if a channel joins a meet me conference.
+ ///
+ public event MeetMeJoinEventHandler MeetMeJoin; + /// + /// A MeetMeLeave is triggered if a channel leaves a meet me conference.
+ ///
+ public event MeetMeLeaveEventHandler MeetMeLeave; + // public event MeetMeStopTalkingEventHandler MeetMeStopTalking; + /// + /// A MeetMeTalkingEvent is triggered when a user starts talking in a meet me conference.
+ /// To enable talker detection you must pass the option 'T' to the MeetMe application. + ///
+ public event MeetMeTalkingEventHandler MeetMeTalking; + /// + /// A MessageWaiting is triggered when someone leaves voicemail.
+ ///
+ public event MessageWaitingEventHandler MessageWaiting; + /// + /// A NewCallerId is triggered when the caller id of a channel changes.
+ ///
+ public event NewCallerIdEventHandler NewCallerId; + /// + /// A NewChannel is triggered when a new channel is created.
+ ///
+ public event NewChannelEventHandler NewChannel; + /// + /// A NewExten is triggered when a channel is connected to a new extension.
+ ///
+ public event NewExtenEventHandler NewExten; + /// + /// A NewState is triggered when the state of a channel has changed.
+ ///
+ public event NewStateEventHandler NewState; + // public event OriginateEventHandler Originate; + /// + /// An OriginateFailure is triggered when the execution of an OriginateAction failed. + /// + // public event OriginateFailureEventHandler OriginateFailure; + /// + /// An OriginateSuccess is triggered when the execution of an OriginateAction succeeded. + /// + // public event OriginateSuccessEventHandler OriginateSuccess; + /// + /// An OriginateResponse is triggered when the execution of an Originate. + /// + public event OriginateResponseEventHandler OriginateResponse; + /// + /// A ParkedCall is triggered when a channel is parked (in this case no + /// action id is set) and in response to a ParkedCallsAction.
+ ///
+ public event ParkedCallEventHandler ParkedCall; + /// + /// A ParkedCallGiveUp is triggered when a channel that has been parked is hung up.
+ ///
+ public event ParkedCallGiveUpEventHandler ParkedCallGiveUp; + /// + /// A ParkedCallsComplete is triggered after all parked calls have been reported in response to a ParkedCallsAction. + /// + public event ParkedCallsCompleteEventHandler ParkedCallsComplete; + /// + /// A ParkedCallTimeOut is triggered when call parking times out for a given channel.
+ ///
+ public event ParkedCallTimeOutEventHandler ParkedCallTimeOut; + /// + /// A PeerEntry is triggered in response to a SIPPeersAction or SIPShowPeerAction and contains information about a peer.
+ ///
+ public event PeerEntryEventHandler PeerEntry; + /// + /// A PeerlistComplete is triggered after the details of all peers has been reported in response to an SIPPeersAction or SIPShowPeerAction.
+ ///
+ public event PeerlistCompleteEventHandler PeerlistComplete; + /// + /// A PeerStatus is triggered when a SIP or IAX client attempts to registrer at this asterisk server.
+ ///
+ public event PeerStatusEventHandler PeerStatus; + /// + /// A QueueEntryEvent is triggered in response to a QueueStatusAction and contains information about an entry in a queue. + /// + public event QueueEntryEventHandler QueueEntry; + /// + /// A QueueMemberAddedEvent is triggered when a queue member is added to a queue. + /// + public event QueueMemberAddedEventHandler QueueMemberAdded; + /// + /// A QueueMemberEvent is triggered in response to a QueueStatusAction and contains information about a member of a queue. + /// + public event QueueMemberEventHandler QueueMember; + /// + /// A QueueMemberPausedEvent is triggered when a queue member is paused or unpaused. + /// + public event QueueMemberPausedEventHandler QueueMemberPaused; + /// + /// A QueueMemberRemovedEvent is triggered when a queue member is removed from a queue. + /// + public event QueueMemberRemovedEventHandler QueueMemberRemoved; + /// + /// A QueueMemberStatusEvent shows the status of a QueueMemberEvent. + /// + public event QueueMemberStatusEventHandler QueueMemberStatus; + /// + /// A QueueParamsEvent is triggered in response to a QueueStatusAction and contains the parameters of a queue. + /// + public event QueueParamsEventHandler QueueParams; + /// + /// A QueueStatusCompleteEvent is triggered after the state of all queues has been reported in response to a QueueStatusAction. + /// + public event QueueStatusCompleteEventHandler QueueStatusComplete; + /// + /// A Registry is triggered when this asterisk server attempts to register + /// as a client at another SIP or IAX server.
+ ///
+ public event RegistryEventHandler Registry; + /// + /// A RenameEvent is triggered when the name of a channel is changed. + /// + public event RenameEventHandler Rename; + /// + /// A StatusCompleteEvent is triggered after the state of all channels has been reported in response to a StatusAction. + /// + public event StatusCompleteEventHandler StatusComplete; + /// + /// A StatusEvent is triggered for each active channel in response to a StatusAction. + /// + public event StatusEventHandler Status; + /// + /// An UnholdEvent is triggered by the SIP channel driver when a channel is no longer put on hold. + /// + public event UnholdEventHandler Unhold; + /// + /// An UnlinkEvent is triggered when a link between two voice channels is discontinued, for example, just before call completion. + /// + public event UnlinkEventHandler Unlink; + /// + /// A UnparkedCallEvent is triggered when a channel that has been parked is resumed. + /// + public event UnparkedCallEventHandler UnparkedCall; + /// + /// A ZapShowChannelsEvent is triggered on UserEvent in dialplan. + /// + public event UserEventHandler UserEvents; + /// + /// A ZapShowChannelsCompleteEvent is triggered after the state of all zap channels has been reported in response to a ZapShowChannelsAction. + /// + public event ZapShowChannelsCompleteEventHandler ZapShowChannelsComplete; + /// + /// A ZapShowChannelsEvent is triggered in response to a ZapShowChannelsAction and shows the state of a zap channel. + /// + public event ZapShowChannelsEventHandler ZapShowChannels; + /// + /// A ConnectionState is triggered after Connect/Disconnect/Reload/Shutdown events. + /// + public event ConnectionStateEventHandler ConnectionState; + + #endregion + + #region Constructor - ManagerConnection() + /// Creates a new instance. + public ManagerConnection() + { + callerThread = Thread.CurrentThread; + + socketEncoding = Encoding.ASCII; + + responseHandlers = new Dictionary(); + pingHandlers = new Dictionary(); + responseEventHandlers = new Dictionary(); + registeredEventClasses = new Dictionary(); + + Helper.RegisterBuiltinEventClasses(registeredEventClasses); + + registeredEventHandlers = new Dictionary(); + + #region Event mapping table + Helper.RegisterEventHandler(registeredEventHandlers, 0, typeof(AgentCallbackLoginEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 1, typeof(AgentCallbackLogoffEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 2, typeof(AgentCalledEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 3, typeof(AgentCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 4, typeof(AgentConnectEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 5, typeof(AgentDumpEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 6, typeof(AgentLoginEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 7, typeof(AgentLogoffEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 8, typeof(AgentsCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 9, typeof(AgentsEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 10, typeof(AlarmClearEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 11, typeof(AlarmEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 12, typeof(CdrEvent)); + + Helper.RegisterEventHandler(registeredEventHandlers, 14, typeof(DBGetResponseEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 15, typeof(DialEvent)); + + Helper.RegisterEventHandler(registeredEventHandlers, 17, typeof(DNDStateEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 18, typeof(ExtensionStatusEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 19, typeof(HangupEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 20, typeof(HoldedCallEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 21, typeof(HoldEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 22, typeof(JoinEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 23, typeof(LeaveEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 24, typeof(LinkEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 25, typeof(LogChannelEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 26, typeof(MeetmeJoinEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 27, typeof(MeetmeLeaveEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 28, typeof(MeetmeTalkingEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 29, typeof(MessageWaitingEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 30, typeof(NewCallerIdEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 31, typeof(NewChannelEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 32, typeof(NewExtenEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 33, typeof(NewStateEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 34, typeof(OriginateResponseEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 35, typeof(ParkedCallEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 36, typeof(ParkedCallGiveUpEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 37, typeof(ParkedCallsCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 38, typeof(ParkedCallTimeOutEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 39, typeof(PeerEntryEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 40, typeof(PeerlistCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 41, typeof(PeerStatusEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 42, typeof(QueueEntryEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 43, typeof(QueueMemberAddedEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 44, typeof(QueueMemberEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 45, typeof(QueueMemberPausedEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 46, typeof(QueueMemberRemovedEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 47, typeof(QueueMemberStatusEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 48, typeof(QueueParamsEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 49, typeof(QueueStatusCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 50, typeof(RegistryEvent)); + + Helper.RegisterEventHandler(registeredEventHandlers, 52, typeof(RenameEvent)); + + Helper.RegisterEventHandler(registeredEventHandlers, 54, typeof(StatusCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 55, typeof(StatusEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 56, typeof(UnholdEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 57, typeof(UnlinkEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 58, typeof(UnparkedCallEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 59, typeof(UserEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 60, typeof(ZapShowChannelsCompleteEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 61, typeof(ZapShowChannelsEvent)); + + Helper.RegisterEventHandler(registeredEventHandlers, 62, typeof(ConnectEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 62, typeof(DisconnectEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 62, typeof(ReloadEvent)); + Helper.RegisterEventHandler(registeredEventHandlers, 62, typeof(ShutdownEvent)); + + + #endregion + + this.internalEvent += new ManagerEventHandler(internalEventHandler); + } + #endregion + + #region Constructor - ManagerConnection(hostname, port, username, password) + /// + /// Creates a new instance with the given connection parameters. + /// + /// the hosname of the Asterisk server to connect to. + /// the port where Asterisk listens for incoming Manager API connections, usually 5038. + /// the username to use for login + /// the password to use for login + public ManagerConnection(string hostname, int port, string username, string password) + : this() + { + this.hostname = hostname; + this.port = port; + this.username = username; + this.password = password; + } + #endregion + + #region Constructor - ManagerConnection(hostname, port, username, password, Encoding socketEncoding) + /// + /// Creates a new instance with the given connection parameters. + /// + /// the hosname of the Asterisk server to connect to. + /// the port where Asterisk listens for incoming Manager API connections, usually 5038. + /// the username to use for login + /// the password to use for login + /// text encoding to asterisk input/output stream + public ManagerConnection(string hostname, int port, string username, string password, Encoding encoding) + : this() + { + this.hostname = hostname; + this.port = port; + this.username = username; + this.password = password; + this.socketEncoding = encoding; + } + #endregion + + /// + /// Default Fast Reconnect retry counter. + /// + public int ReconnectRetryFast + { + get { return reconnectRetryFast; } + set { reconnectRetryFast = value; } + } + /// Default Maximum Reconnect retry counter. + public int ReconnectRetryMax + { + get { return reconnectRetryMax; } + set { reconnectRetryMax = value; } + } + /// Default Fast Reconnect interval in milliseconds. + public int ReconnectIntervalFast + { + get { return reconnectIntervalFast; } + set { reconnectIntervalFast = value; } + } + /// Default Slow Reconnect interval in milliseconds. + public int ReconnectIntervalMax + { + get { return reconnectIntervalMax; } + set { reconnectIntervalMax = value; } + } + + #region TraceCallerThread + /// + /// Enable + /// + internal bool TraceCallerThread + { + get { return traceCallerThread; } + set { traceCallerThread = value; } + } + #endregion + + #region CallerThread + internal Thread CallerThread + { + get { return callerThread; } + } + #endregion + + #region internalEventHandler(object sender, ManagerEvent e) + private void internalEventHandler(object sender, ManagerEvent e) + { + int eventHash = e.GetType().Name.GetHashCode(); + if (registeredEventHandlers.ContainsKey(eventHash)) + { + switch (registeredEventHandlers[eventHash]) + { + #region A-C + case 0: + if (AgentCallbackLogin != null) + { + AgentCallbackLogin(this, (AgentCallbackLoginEvent)e); + return; + } + break; + case 1: + if (AgentCallbackLogoff != null) + { + AgentCallbackLogoff(this, (AgentCallbackLogoffEvent)e); + return; + } + break; + case 2: + if (AgentCalled != null) + { + AgentCalled(this, (Event.AgentCalledEvent)e); + return; + } + break; + case 3: + if (AgentComplete != null) + { + AgentComplete(this, (Event.AgentCompleteEvent)e); + return; + } + break; + case 4: + if (AgentConnect != null) + { + AgentConnect(this, (Event.AgentConnectEvent)e); + return; + } + break; + case 5: + if (AgentDump != null) + { + AgentDump(this, (Event.AgentDumpEvent)e); + return; + } + break; + case 6: + if (AgentLogin != null) + { + AgentLogin(this, (Event.AgentLoginEvent)e); + return; + } + break; + case 7: + if (AgentLogoff != null) + { + AgentLogoff(this, (Event.AgentLogoffEvent)e); + return; + } + break; + case 8: + if (AgentsComplete != null) + { + AgentsComplete(this, (AgentsCompleteEvent)e); + return; + } + break; + case 9: + if (Agents != null) + { + Agents(this, (AgentsEvent)e); + return; + } + break; + case 10: + if (AlarmClear != null) + { + AlarmClear(this, (AlarmClearEvent)e); + return; + } + break; + case 11: + if (Alarm != null) + { + Alarm(this, (AlarmEvent)e); + return; + } + break; + case 12: + if (Cdr != null) + { + Cdr(this, (CdrEvent)e); + return; + } + break; + #endregion + #region D-L + case 14: + if (DBGetResponse != null) + { + DBGetResponse(this, (DBGetResponseEvent)e); + return; + } + break; + case 15: + if (Dial != null) + { + Dial(this, (DialEvent)e); + return; + } + break; + case 17: + if (DNDState != null) + { + DNDState(this, (DNDStateEvent)e); + return; + } + break; + case 18: + if (ExtensionStatus != null) + { + ExtensionStatus(this, (ExtensionStatusEvent)e); + return; + } + break; + case 19: + if (Hangup != null) + { + Hangup(this, (HangupEvent)e); + return; + } + break; + case 20: + if (HoldedCall != null) + { + HoldedCall(this, (HoldedCallEvent)e); + return; + } + break; + case 21: + if (Hold != null) + { + Hold(this, (HoldEvent)e); + return; + } + break; + case 22: + if (Join != null) + { + Join(this, (JoinEvent)e); + return; + } + break; + case 23: + if (Leave != null) + { + Leave(this, (LeaveEvent)e); + return; + } + break; + case 24: + if (Link != null) + { + Link(this, (LinkEvent)e); + return; + } + break; + case 25: + if (LogChannel != null) + { + LogChannel(this, (LogChannelEvent)e); + return; + } + break; + #endregion + #region M-P + case 26: + if (MeetMeJoin != null) + { + MeetMeJoin(this, (MeetmeJoinEvent)e); + return; + } + break; + case 27: + if (MeetMeLeave != null) + { + MeetMeLeave(this, (MeetmeLeaveEvent)e); + return; + } + break; + case 28: + if (MeetMeTalking != null) + { + MeetMeTalking(this, (MeetmeTalkingEvent)e); + return; + } + break; + case 29: + if (MessageWaiting != null) + { + MessageWaiting(this, (MessageWaitingEvent)e); + return; + } + break; + case 30: + if (NewCallerId != null) + { + NewCallerId(this, (NewCallerIdEvent)e); + return; + } + break; + case 31: + if (NewChannel != null) + { + NewChannel(this, (NewChannelEvent)e); + return; + } + break; + case 32: + if (NewExten != null) + { + NewExten(this, (NewExtenEvent)e); + return; + } + break; + case 33: + if (NewState != null) + { + NewState(this, (NewStateEvent)e); + return; + } + break; + case 34: + if (OriginateResponse != null) + { + OriginateResponse(this, (OriginateResponseEvent)e); + return; + } + break; + case 35: + if (ParkedCall != null) + { + ParkedCall(this, (ParkedCallEvent)e); + return; + } + break; + case 36: + if (ParkedCallGiveUp != null) + { + ParkedCallGiveUp(this, (ParkedCallGiveUpEvent)e); + return; + } + break; + case 37: + if (ParkedCallsComplete != null) + { + ParkedCallsComplete(this, (ParkedCallsCompleteEvent)e); + return; + } + break; + case 38: + if (ParkedCallTimeOut != null) + { + ParkedCallTimeOut(this, (ParkedCallTimeOutEvent)e); + return; + } + break; + case 39: + if (PeerEntry != null) + { + PeerEntry(this, (PeerEntryEvent)e); + return; + } + break; + case 40: + if (PeerlistComplete != null) + { + PeerlistComplete(this, (PeerlistCompleteEvent)e); + return; + } + break; + case 41: + if (PeerStatus != null) + { + PeerStatus(this, (PeerStatusEvent)e); + return; + } + break; + #endregion + #region Q-Z + case 42: + if (QueueEntry != null) + { + QueueEntry(this, (QueueEntryEvent)e); + return; + } + break; + case 43: + if (QueueMemberAdded != null) + { + QueueMemberAdded(this, (QueueMemberAddedEvent)e); + return; + } + break; + case 44: + if (QueueMember != null) + { + QueueMember(this, (QueueMemberEvent)e); + return; + } + break; + case 45: + if (QueueMemberPaused != null) + { + QueueMemberPaused(this, (QueueMemberPausedEvent)e); + return; + } + break; + case 46: + if (QueueMemberRemoved != null) + { + QueueMemberRemoved(this, (QueueMemberRemovedEvent)e); + return; + } + break; + case 47: + if (QueueMemberStatus != null) + { + QueueMemberStatus(this, (QueueMemberStatusEvent)e); + return; + } + break; + case 48: + if (QueueParams != null) + { + QueueParams(this, (QueueParamsEvent)e); + return; + } + break; + case 49: + if (QueueStatusComplete != null) + { + QueueStatusComplete(this, (QueueStatusCompleteEvent)e); + return; + } + break; + case 50: + if (Registry != null) + { + Registry(this, (RegistryEvent)e); + return; + } + break; + case 52: + if (Rename != null) + { + Rename(this, (RenameEvent)e); + return; + } + break; + case 54: + if (StatusComplete != null) + { + StatusComplete(this, (StatusCompleteEvent)e); + return; + } + break; + case 55: + if (Status != null) + { + Status(this, (StatusEvent)e); + return; + } + break; + case 56: + if (Unhold != null) + { + Unhold(this, (UnholdEvent)e); + return; + } + break; + case 57: + if (Unlink != null) + { + Unlink(this, (UnlinkEvent)e); + return; + } + break; + case 58: + if (UnparkedCall != null) + { + UnparkedCall(this, (UnparkedCallEvent)e); + return; + } + break; + case 59: + if (UserEvents != null) + { + UserEvents(this, (UserEvent)e); + return; + } + break; + case 60: + if (ZapShowChannelsComplete != null) + { + ZapShowChannelsComplete(this, (ZapShowChannelsCompleteEvent)e); + return; + } + break; + case 61: + if (ZapShowChannels != null) + { + ZapShowChannels(this, (ZapShowChannelsEvent)e); + return; + } + break; + #endregion + + case 62: + if (ConnectionState != null) + { + ConnectionState(this, (ConnectionStateEvent)e); + return; + } + break; + + default: + if (UnhandledEvent != null) + UnhandledEvent(this, e); + return; + } + } + if (fireAllEvents && UnhandledEvent != null) + UnhandledEvent(this, e); + } + #endregion + + #region FireAllEvents + /// + /// If this property set to true then ManagerConnection send all unassigned events to UnhandledEvent handler,
+ /// if set to false then all unassgned events lost and send only UnhandledEvent.
+ /// Default: false + ///
+ public bool FireAllEvents + { + get { return this.fireAllEvents; } + set { this.fireAllEvents = value; } + } + #endregion + + #region PingInterval + /// + /// Timeout from Ping to Pong. If no Pong received send Disconnect event. Set to zero to disable. + /// + public int PingInterval + { + get { return pingInterval; } + set { pingInterval = value; } + } + #endregion + + #region Hostname + /// Sets the hostname of the asterisk server to connect to.
+ /// Default is localhost. + ///
+ public string Hostname + { + get { return hostname; } + set { hostname = value; } + } + #endregion + + #region Port + /// + /// Sets the port to use to connect to the asterisk server. This is the port + /// specified in asterisk's manager.conf file.
+ /// Default is 5038. + ///
+ public int Port + { + get { return port; } + set { port = value; } + } + #endregion + + #region UserName + /// + /// Sets the username to use to connect to the asterisk server. This is the + /// username specified in asterisk's manager.conf file. + /// + public string Username + { + get { return username; } + set { username = value; } + } + #endregion + + #region Password + /// + /// Sets the password to use to connect to the asterisk server. This is the + /// password specified in asterisk's manager.conf file. + /// + public string Password + { + get { return password; } + set { password = value; } + } + #endregion + + #region DefaultResponseTimeout + /// Sets the time in milliseconds the synchronous method + /// will wait for a response before throwing a TimeoutException.
+ /// Default is 2000. + ///
+ public int DefaultResponseTimeout + { + get { return defaultResponseTimeout; } + set { defaultResponseTimeout = value; } + } + #endregion + + #region DefaultEventTimeout + /// Sets the time in milliseconds the synchronous method + /// will wait for a response and the last response event before throwing a TimeoutException.
+ /// Default is 5000. + ///
+ public int DefaultEventTimeout + { + get { return defaultEventTimeout; } + set { defaultEventTimeout = value; } + } + #endregion + + #region SleepTime + /// Sets the time in milliseconds the synchronous methods + /// SendAction(Action.ManagerAction) and + /// SendAction(Action.ManagerAction, long) will sleep between two checks + /// for the arrival of a response. This value should be rather small.
+ /// The sleepTime attribute is also used when checking for the protocol + /// identifer.
+ /// Default is 50. + ///
+ /// this has been replaced by an interrupt based response checking approach. + public int SleepTime + { + get { return sleepTime; } + set { sleepTime = value; } + } + #endregion + + #region KeepAliveAfterAuthenticationFailure + /// Set to true to try reconnecting to ther asterisk serve + /// even if the reconnection attempt threw an AuthenticationFailedException.
+ /// Default is false. + ///
+ public bool KeepAliveAfterAuthenticationFailure + { + set { keepAliveAfterAuthenticationFailure = value; } + get { return keepAliveAfterAuthenticationFailure; } + } + #endregion + + #region KeepAlive + /// + /// Should we attempt to reconnect when the connection is lost?
+ /// This is set to true after successful login and to false after logoff or after an authentication failure when keepAliveAfterAuthenticationFailure is false. + ///
+ public bool KeepAlive + { + get { return keepAlive; } + set { keepAlive = value; } + } + #endregion + + #region SocketEncoding + /// + /// Socket Encoding - default ASCII + /// + public Encoding SocketEncoding + { + get { return socketEncoding; } + set { socketEncoding = value; } + } + #endregion + + #region Version + public string Version + { + get { return version; } + } + #endregion + + #region AsteriskVersion + public AsteriskVersion AsteriskVersion + { + get { return asteriskVersion; } + } + #endregion + + #region login(timeout) + /// + /// Does the real login, following the steps outlined below.
+ /// Connects to the asterisk server by calling connect() if not already connected
+ /// Waits until the protocol identifier is received. This is checked every sleepTime ms but not longer than timeout ms in total.
+ /// Sends a ChallengeAction requesting a challenge for authType MD5.
+ /// When the ChallengeResponse is received a LoginAction is sent using the calculated key (MD5 hash of the password appended to the received challenge).
+ ///
+ /// the maximum time to wait for the protocol identifier (in ms) + /// + /// AuthenticationFailedException if username or password are incorrect and the login action returns an error or if the MD5 + /// hash cannot be computed. The connection is closed in this case. + /// + /// + /// TimeoutException if a timeout occurs either while waiting for the + /// protocol identifier or when sending the challenge or login + /// action. The connection is closed in this case. + /// + private void login(int timeout) + { + enableEvents = false; + if (reconnected) + { +#if LOGGER + logger.Error("Login during reconnect state."); +#endif + throw new AuthenticationFailedException("Unable login during reconnect state."); + } + + reconnectEnable = false; + DateTime start = DateTime.Now; + do + { + if (connect()) + { + // Increase delay after connection up to 500 ms + Thread.Sleep(10 * sleepTime); // 200 milliseconds delay + } + try + { + Thread.Sleep(4 * sleepTime); // 200 milliseconds delay + } + catch + { } + + if (string.IsNullOrEmpty(protocolIdentifier) && timeout > 0 && Helper.GetMillisecondsFrom(start) > timeout) + { + disconnect(true); + throw new TimeoutException("Timeout waiting for protocol identifier"); + } + } while (string.IsNullOrEmpty(protocolIdentifier)); + + ChallengeAction challengeAction = new ChallengeAction(); + Response.ManagerResponse response = SendAction(challengeAction, defaultResponseTimeout * 2); + if (response is ChallengeResponse) + { + ChallengeResponse challengeResponse = (ChallengeResponse)response; + string key, challenge = challengeResponse.Challenge; + try + { + Util.MD5Support md = Util.MD5Support.GetInstance(); + if (challenge != null) + md.Update(UTF8Encoding.UTF8.GetBytes(challenge)); + if (password != null) + md.Update(UTF8Encoding.UTF8.GetBytes(password)); + key = Helper.ToHexString(md.DigestData); + } + catch (Exception ex) + { + disconnect(true); +#if LOGGER + logger.Error("Unable to create login key using MD5 Message Digest.", ex); +#endif + throw new AuthenticationFailedException("Unable to create login key using MD5 Message Digest.", ex); + } + + Action.LoginAction loginAction = new Action.LoginAction(username, "MD5", key); + Response.ManagerResponse loginResponse = SendAction(loginAction); + if (loginResponse is Response.ManagerError) + { + disconnect(true); + throw new AuthenticationFailedException(loginResponse.Message); + } + + // successfully logged in so assure that we keep trying to reconnect when disconnected + reconnectEnable = keepAlive; + +#if LOGGER + logger.Info("Successfully logged in"); +#endif + asteriskVersion = determineVersion(); +#if LOGGER + logger.Info("Determined Asterisk version: " + asteriskVersion); +#endif + enableEvents = true; + ConnectEvent ce = new ConnectEvent(this); + ce.ProtocolIdentifier = this.protocolIdentifier; + DispatchEvent(ce); + } + else if (response is ManagerError) + throw new ManagerException("Unable login to Asterisk - " + response.Message); + else + throw new ManagerException("Unknown response during login to Asterisk - " + response.GetType().Name + " with message " + response.Message); + + } + #endregion + + #region determineVersion() + protected internal AsteriskVersion determineVersion() + { + // First try version check to 1.4 & 1.6 + Response.ManagerResponse response; + response = SendAction(new Action.CommandAction("core show version"), defaultResponseTimeout * 2); + if (response is Response.CommandResponse) + { + foreach (string line in ((Response.CommandResponse)response).Result) + { + foreach (Match m in Common.ASTERISK_VERSION.Matches(line)) + { + if (m.Groups.Count >= 2) + { + version = m.Groups[1].Value; + if (version.StartsWith("1.4.")) + return AsteriskVersion.ASTERISK_1_4; + else if (version.StartsWith("1.6.") || version.StartsWith("1.8.")) + return AsteriskVersion.ASTERISK_1_6; + else + throw new ManagerException("Unknown Asterisk version " + version); + } + } + } + } + + Response.ManagerResponse showVersionFilesResponse = SendAction(new Action.CommandAction("show version files"), defaultResponseTimeout * 2); + if (showVersionFilesResponse is Response.CommandResponse) + { + IList showVersionFilesResult = ((Response.CommandResponse)showVersionFilesResponse).Result; + if (showVersionFilesResult != null && showVersionFilesResult.Count > 0) + { + string line1; + line1 = (string)showVersionFilesResult[0]; + if (line1 != null && line1.StartsWith("File")) + return AsteriskVersion.ASTERISK_1_2; + } + } + return AsteriskVersion.ASTERISK_1_0; + } + + #endregion + + #region connect() + protected internal bool connect() + { + bool result = false; + bool startReader = false; + + lock (lockSocket) + { + if (mrSocket == null) + { +#if LOGGER + logger.Info("Connecting to {0}:{1}", hostname, port); +#endif + try + { + mrSocket = new SocketConnection(hostname, port, socketEncoding); + result = mrSocket.IsConnected; + } +#if LOGGER + catch (Exception ex) + { + logger.Info("Connect - Exception : {0}", ex.Message); +#else + catch + { +#endif + result = false; + } + if (result) + { + if (this.mrReader == null) + { + mrReader = new ManagerReader(this); + mrReaderThread = new Util.ThreadClass(new ThreadStart(this.mrReader.Run), "ManagerReader-" + DateTime.Now.Second); + mrReader.Socket = mrSocket; + startReader = true; + } + else + mrReader.Socket = mrSocket; + mrReader.Reinitialize(); + } + else + mrSocket = null; + } + } + + if (startReader) + mrReaderThread.Start(); + + return IsConnected(); + } + #endregion + + #region disconnect() + /// Closes the socket connection. + private void disconnect(bool withDie) + { + lock (lockSocket) + { + if (withDie) + { + reconnectEnable = false; + reconnected = false; + enableEvents = true; + } + + if (mrReader != null) + { + if (withDie) + { + mrReader.Die = true; + mrReader = null; + } + else + mrReader.Socket = null; + } + + if (this.mrSocket != null) + { + mrSocket.Close(); + mrSocket = null; + } + + responseEventHandlers.Clear(); + responseHandlers.Clear(); + pingHandlers.Clear(); + } + } + #endregion + + #region reconnect(bool init) + /// + /// Reconnects to the asterisk server when the connection is lost.
+ /// While keepAlive is true we will try to reconnect. + /// Reconnection attempts will be stopped when the logoff() method + /// is called or when the login after a successful reconnect results in an + /// AuthenticationFailedException suggesting that the manager + /// credentials have changed and keepAliveAfterAuthenticationFailure is not set.
+ /// This method is called when a DisconnectEvent is received from the reader. + ///
+ private void reconnect(bool init) + { +#if LOGGER + logger.Warning("reconnect (init: {0}), reconnectCount:{1}", init, reconnectCount); +#endif + if (init) + reconnectCount = 0; + else if (reconnectCount++ > reconnectRetryMax) + reconnectEnable = false; + + if (reconnectEnable) + { +#if LOGGER + logger.Warning("Try reconnect."); +#endif + enableEvents = false; + reconnected = true; + disconnect(false); + + int retryCount = 0; + while (reconnectEnable && !mrReader.Die) + { + if (retryCount >= reconnectRetryMax) + reconnectEnable = false; + else + { + try + { + if (retryCount < reconnectRetryFast) + { + // Try to reconnect quite fast for the first times + // this succeeds if the server has just been restarted +#if LOGGER + logger.Info("Reconnect delay : {0}, retry : {1}", reconnectIntervalFast, retryCount); +#endif + Thread.Sleep(reconnectIntervalFast); + } + else + { + // slow down after unsuccessful attempts assuming a shutdown of the server +#if LOGGER + logger.Info("Reconnect delay : {0}, retry : {1}", reconnectIntervalMax, retryCount); +#endif + Thread.Sleep(reconnectIntervalMax); + } + } + catch (ThreadInterruptedException) + { + continue; + } +#if LOGGER + catch (Exception ex) + { + logger.Info("Reconnect delay exception : ", ex.Message); +#else + catch + { +#endif + continue; + } + + try + { +#if LOGGER + logger.Info("Try connect."); +#endif + if (connect()) + break; + } +#if LOGGER + catch(Exception ex) + { + logger.Info("Connect exception : ", ex.Message); +#else + catch + { +#endif + } + retryCount++; + } + } + } + + if (!reconnectEnable) + { +#if LOGGER + logger.Info("Can't reconnect."); +#endif + enableEvents = true; + reconnected = false; + disconnect(true); + fireEvent(new DisconnectEvent(this)); + } + } + #endregion + + #region createInternalActionId() + /// + /// Creates a new unique internal action id based on the hash code of this connection and a sequence. + /// + private string createInternalActionId() + { + return this.GetHashCode() + "_" + (this.actionIdCount++); + } + #endregion + + #region Login() + /// + /// Logs in to the Asterisk manager using asterisk's MD5 based + /// challenge/response protocol. The login is delayed until the protocol + /// identifier has been received by the reader. + /// + /// AuthenticationFailedException if the username and/or password are incorrect + /// TimeoutException if no response is received within the specified timeout period + /// + /// + public void Login() + { + login(defaultResponseTimeout); + } + /// + /// Log in to the Asterisk manager using asterisk's MD5 based + /// challenge/response protocol. The login is delayed until the protocol + /// identifier has been received by the reader. + /// + /// Timeout in milliseconds to login. + public void Login(int timeout) + { + login(timeout); + } + #endregion + + #region IsConnected() + /// Returns true if there is a socket connection to the + /// asterisk server, false otherwise. + /// + /// + /// true if there is a socket connection to the + /// asterisk server, false otherwise. + /// + public bool IsConnected() + { + bool result = false; + lock (lockSocket) + result = mrSocket != null && mrSocket.IsConnected; + return result; + } + #endregion + + #region Logoff() + /// + /// Sends a LogoffAction and disconnects from the server. + /// + public void Logoff() + { + lock (lockSocket) + { + // stop reconnecting when we got disconnected + reconnectEnable = false; + if (mrReader != null && mrSocket != null) + try + { + mrReader.IsLogoff = true; + SendAction(new Action.LogoffAction()); + } + catch + { } + } + disconnect(true); + } + #endregion + + #region SendAction(action) + /// + /// Send Action with default timeout. + /// + /// + /// + public Response.ManagerResponse SendAction(Action.ManagerAction action) + { + return SendAction(action, defaultResponseTimeout); + } + #endregion + + #region SendAction(action, timeout) + /// + /// Send action ans with timeout (milliseconds) + /// + /// action to send + /// timeout in milliseconds + /// + public Response.ManagerResponse SendAction(ManagerAction action, int timeOut) + { + AutoResetEvent autoEvent = new AutoResetEvent(false); + ResponseHandler handler = new ResponseHandler(action, autoEvent); + + int hash = SendAction(action, handler); + bool result = autoEvent.WaitOne(timeOut <= 0 ? -1 : timeOut, true); + + RemoveResponseHandler(handler); + + if (result) + return handler.Response; + throw new TimeoutException("Timeout waiting for response to " + action.Action); + } + #endregion + + #region SendAction(action, responseHandler) + public int SendAction(ManagerAction action, ResponseHandler responseHandler) + { + if (action == null) + throw new ArgumentException("Unable to send action: action is null."); + + if (mrSocket == null) + throw new SystemException("Unable to send " + action.Action + " action: not connected."); + + // if the responseHandler is null the user is obviously not interested in the response, thats fine. + string internalActionId = string.Empty; + if (responseHandler != null) + { + internalActionId = createInternalActionId(); + responseHandler.Hash = internalActionId.GetHashCode(); + AddResponseHandler(responseHandler); + } + + SendToAsterisk(action, internalActionId); + + return responseHandler != null ? responseHandler.Hash : 0; + } + #endregion + + #region SendEventGeneratingAction(action) + public ResponseEvents SendEventGeneratingAction(ManagerActionEvent action) + { + return SendEventGeneratingAction(action, defaultEventTimeout); + } + #endregion + + #region SendEventGeneratingAction(action, timeout) + /// + /// + /// + /// + /// wait timeout in milliseconds + /// + public ResponseEvents SendEventGeneratingAction(ManagerActionEvent action, int timeout) + { + if (action == null) + throw new ArgumentException("Unable to send action: action is null."); + else if (action.ActionCompleteEventClass() == null) + throw new ArgumentException("Unable to send action: ActionCompleteEventClass is null."); + else if (!typeof(ResponseEvent).IsAssignableFrom(action.ActionCompleteEventClass())) + throw new ArgumentException("Unable to send action: ActionCompleteEventClass is not a ResponseEvent."); + + if (mrSocket == null) + throw new SystemException("Unable to send " + action.Action + " action: not connected."); + + AutoResetEvent autoEvent = new AutoResetEvent(false); + ResponseEventHandler handler = new ResponseEventHandler(this, action, autoEvent); + + string internalActionId = createInternalActionId(); + handler.Hash = internalActionId.GetHashCode(); + AddResponseHandler(handler); + AddResponseEventHandler(handler); + + SendToAsterisk(action, internalActionId); + + bool result = autoEvent.WaitOne(timeout <= 0 ? -1 : timeout, true); + + RemoveResponseHandler(handler); + RemoveResponseEventHandler(handler); + + if (result) + return handler.ResponseEvents; + + throw new EventTimeoutException("Timeout waiting for response or response events to " + action.Action, handler.ResponseEvents); + } + #endregion + + #region Response Handler helpers + private void AddResponseHandler(IResponseHandler handler) + { + lock (lockHandlers) + { + if (handler.Action is PingAction) + pingHandlers[handler.Hash] = handler; + else + responseHandlers[handler.Hash] = handler; + } + } + + private void AddResponseEventHandler(IResponseHandler handler) + { + lock (lockHandlers) + responseEventHandlers[handler.Hash] = handler; + } + + internal void RemoveResponseHandler(IResponseHandler handler) + { + int hash = handler.Hash; + if (hash != 0) + lock (lockHandlers) + if (responseHandlers.ContainsKey(hash)) + responseHandlers.Remove(hash); + } + + internal void RemoveResponseEventHandler(IResponseHandler handler) + { + int hash = handler.Hash; + if (hash != 0) + lock (lockHandlers) + if (responseEventHandlers.ContainsKey(hash)) + responseEventHandlers.Remove(hash); + } + + private IResponseHandler GetRemoveResponseHandler(int hash) + { + IResponseHandler handler = null; + if (hash != 0) + lock (lockHandlers) + if (responseHandlers.ContainsKey(hash)) + { + handler = responseHandlers[hash]; + responseHandlers.Remove(hash); + } + return handler; + } + + private IResponseHandler GetRemoveResponseEventHandler(int hash) + { + IResponseHandler handler = null; + if (hash != 0) + lock (lockHandlers) + if (responseEventHandlers.ContainsKey(hash)) + { + handler = responseEventHandlers[hash]; + responseEventHandlers.Remove(hash); + } + return handler; + } + + private IResponseHandler GetResponseHandler(int hash) + { + IResponseHandler handler = null; + if (hash != 0) + lock (lockHandlers) + if (responseHandlers.ContainsKey(hash)) + handler = responseHandlers[hash]; + return handler; + } + + private IResponseHandler GetResponseEventHandler(int hash) + { + IResponseHandler handler = null; + if (hash != 0) + lock (lockHandlers) + if (responseEventHandlers.ContainsKey(hash)) + handler = responseEventHandlers[hash]; + return handler; + } + #endregion + + #region SendToAsterisk(ManagerAction action, string internalActionId) + + internal void SendToAsterisk(ManagerAction action, string internalActionId) + { + if (mrSocket == null) + throw new SystemException("Unable to send action: socket is null"); + + string buffer = BuildAction(action, internalActionId); +#if LOGGER + logger.Debug("Sent action : '{0}' : {1}", internalActionId, action); +#endif + if (sa == null) + sa = new SendToAsteriskDelegate(sendToAsterisk); + sa.Invoke(buffer); + } + + private delegate void SendToAsteriskDelegate(string buffer); + private SendToAsteriskDelegate sa = null; + + private void sendToAsterisk(string buffer) + { + mrSocket.Write(buffer); + } + + #endregion + + #region BuildAction(action) + public string BuildAction(Action.ManagerAction action) + { + return BuildAction(action, null); + } + #endregion + + #region BuildAction(action, internalActionId) + public string BuildAction(ManagerAction action, string internalActionId) + { + MethodInfo getter; + object value; + StringBuilder sb = new StringBuilder(); + string valueAsString = string.Empty; + + if (typeof(Action.ProxyAction).IsAssignableFrom(action.GetType())) + sb.Append(string.Concat("ProxyAction: ", action.Action, Common.LINE_SEPARATOR)); + else + sb.Append(string.Concat("Action: ", action.Action, Common.LINE_SEPARATOR)); + + if (string.IsNullOrEmpty(internalActionId)) + valueAsString = action.ActionId; + else + valueAsString = string.Concat(internalActionId, Common.INTERNAL_ACTION_ID_DELIMITER, action.ActionId); + + if (!string.IsNullOrEmpty(valueAsString)) + sb.Append(string.Concat("ActionID: ", valueAsString, Common.LINE_SEPARATOR)); + + Dictionary getters = Helper.GetGetters(action.GetType()); + + foreach (string name in getters.Keys) + { + string nameLower = name.ToLower(Helper.CultureInfo); + if (nameLower == "class" || nameLower == "action" || nameLower == "actionid") + continue; + + getter = getters[name]; + Type propType = getter.ReturnType; + if (!(propType == typeof(string) + || propType == typeof(bool) + || propType == typeof(double) + || propType == typeof(DateTime) + || propType == typeof(int) + || propType == typeof(long) + || propType == typeof(Dictionary) + ) + ) + continue; + + try + { + value = getter.Invoke(action, new object[] { }); + } + catch (UnauthorizedAccessException ex) + { +#if LOGGER + logger.Error("Unable to retrieve property '" + name + "' of " + action.GetType(), ex); + continue; +#else + throw new ManagerException("Unable to retrieve property '" + name + "' of " + action.GetType(), ex); +#endif + } + catch (TargetInvocationException ex) + { +#if LOGGER + logger.Error("Unable to retrieve property '" + name + "' of " + action.GetType(), ex); + continue; +#else + throw new ManagerException("Unable to retrieve property '" + name + "' of " + action.GetType(), ex); +#endif + } + + if (value == null) + continue; + if (value is string) + { + valueAsString = (string)value; + if (valueAsString.Length == 0) + continue; + } + else if (value is bool) + valueAsString = ((bool)value ? "true" : "false"); + else if (value is DateTime) + valueAsString = value.ToString(); + else if (value is IDictionary) + { + valueAsString = Helper.JoinVariables((IDictionary)value, Common.LINE_SEPARATOR, ": "); + if (valueAsString.Length == 0) + continue; + sb.Append(valueAsString); + sb.Append(Common.LINE_SEPARATOR); + continue; + } + else + valueAsString = value.ToString(); + + sb.Append(string.Concat(name, ": ", valueAsString, Common.LINE_SEPARATOR)); + } + + sb.Append(Common.LINE_SEPARATOR); + return sb.ToString(); + } + #endregion + + #region GetProtocolIdentifier() + public string GetProtocolIdentifier() + { + return this.protocolIdentifier; + } + #endregion + + #region RegisterUserEventClass(class) + /// + /// Register User Event Class + /// + /// + public void RegisterUserEventClass(Type userEventClass) + { + Helper.RegisterEventClass(registeredEventClasses, userEventClass); + } + #endregion + + #region DispatchResponse(response) + /// + /// This method is called by the reader whenever a ManagerResponse is + /// received. The response is dispatched to the associated ManagerResponseHandler. + /// + /// the response received by the reader + /// + internal void DispatchResponse(Dictionary buffer) + { +#if LOGGER + logger.Debug("Dispatch response packet : {0}", Helper.JoinVariables(buffer, ", ", ": ")); +#endif + DispatchResponse(buffer, null); + } + + internal void DispatchResponse(ManagerResponse response) + { +#if LOGGER + logger.Debug("Dispatch response : {0}", response); +#endif + DispatchResponse(null, response); + } + + internal void DispatchResponse(Dictionary buffer, ManagerResponse response) + { + string responseActionId = string.Empty; + string actionId = string.Empty; + IResponseHandler responseHandler = null; + + if (buffer != null) + { + if (buffer["response"].ToLower(Helper.CultureInfo) == "error") + response = new ManagerError(buffer); + else if (buffer.ContainsKey("actionid")) + actionId = buffer["actionid"]; + } + + if (response != null) + actionId = response.ActionId; + + if (!string.IsNullOrEmpty(actionId)) + { + int hash = Helper.GetInternalActionId(actionId).GetHashCode(); + responseActionId = Helper.StripInternalActionId(actionId); + responseHandler = GetRemoveResponseHandler(hash); + + if (response != null) + response.ActionId = responseActionId; + if (responseHandler != null) + { + if (response == null) + { + ManagerActionResponse action = responseHandler.Action as ManagerActionResponse; + if (action == null || (response = action.ActionCompleteResponseClass() as ManagerResponse) == null) + response = Helper.BuildResponse(buffer); + else + Helper.SetAttributes(response, buffer); + response.ActionId = responseActionId; + } + + try + { + responseHandler.HandleResponse(response); + } + catch (Exception ex) + { +#if LOGGER + logger.Error("Unexpected exception in responseHandler {0}\n{1}", response, ex); +#else + throw new ManagerException("Unexpected exception in responseHandler " + responseHandler.GetType().FullName, ex); +#endif + } + } + } + else if (response == null && buffer.ContainsKey("ping") && buffer["ping"] == "Pong") + { + response = Helper.BuildResponse(buffer); + foreach (ResponseHandler pingHandler in pingHandlers.Values) + pingHandler.HandleResponse(response); + pingHandlers.Clear(); + } + + if (!reconnected) + return; + + if (response == null) + { + response = Helper.BuildResponse(buffer); + response.ActionId = responseActionId; + } +#if LOGGER + logger.Info("Reconnected - DispatchEvent : " + response); +#endif + #region Support background reconnect + if (response is ChallengeResponse) + { + string key = null; + if (response.IsSuccess()) + { + ChallengeResponse challengeResponse = (ChallengeResponse)response; + string challenge = challengeResponse.Challenge; + try + { + Util.MD5Support md = Util.MD5Support.GetInstance(); + if (challenge != null) + md.Update(UTF8Encoding.UTF8.GetBytes(challenge)); + if (password != null) + md.Update(UTF8Encoding.UTF8.GetBytes(password)); + key = Helper.ToHexString(md.DigestData); + } +#if LOGGER + catch (Exception ex) + { + logger.Error("Unable to create login key using MD5 Message Digest", ex); +#else + catch + { +#endif + key = null; + } + } + bool fail = true; + if (!string.IsNullOrEmpty(key)) + try + { + Action.LoginAction loginAction = new Action.LoginAction(username, "MD5", key); + SendAction(loginAction, null); + fail = false; + } + catch { } + if (fail) + if (keepAliveAfterAuthenticationFailure) + reconnect(true); + else + disconnect(true); + } + else if (response is ManagerError) + { + if (keepAliveAfterAuthenticationFailure) + reconnect(true); + else + disconnect(true); + } + else if (response is ManagerResponse) + { + if (response.IsSuccess()) + { + reconnected = false; + enableEvents = true; + reconnectEnable = keepAlive; + ConnectEvent ce = new ConnectEvent(this); + ce.Reconnect = true; + ce.ProtocolIdentifier = protocolIdentifier; + fireEvent(ce); + } + else if (keepAliveAfterAuthenticationFailure) + reconnect(true); + else + disconnect(true); + } + #endregion + } + #endregion + + #region DispatchEvent(...) + /// + /// This method is called by the reader whenever a ManagerEvent is received. + /// The event is dispatched to all registered ManagerEventHandlers. + /// + /// the event received by the reader + /// + internal void DispatchEvent(Dictionary buffer) + { + ManagerEvent e = Helper.BuildEvent(registeredEventClasses, this, buffer); + DispatchEvent(e); + } + + internal void DispatchEvent(ManagerEvent e) + { +#if LOGGER + logger.Debug("Dispatching event: {0}", e); +#endif + + if (e is ResponseEvent) + { + ResponseEvent responseEvent = (ResponseEvent)e; + if (!string.IsNullOrEmpty(responseEvent.ActionId) && !string.IsNullOrEmpty(responseEvent.InternalActionId)) + { + ResponseEventHandler eventHandler = (ResponseEventHandler)GetResponseEventHandler(responseEvent.InternalActionId.GetHashCode()); + if (eventHandler != null) + try + { + eventHandler.HandleEvent(e); + } + catch (SystemException ex) + { +#if LOGGER + logger.Error("Unexpected exception", ex); +#else + throw ex; +#endif + } + } + } + + #region ConnectEvent + if (e is ConnectEvent) + { + string protocol = ((ConnectEvent)e).ProtocolIdentifier; +#if LOGGER + logger.Info("Connected via {0}", protocol); +#endif + if (!string.IsNullOrEmpty(protocol) && protocol.StartsWith("Asterisk Call Manager")) + { + this.protocolIdentifier = protocol; + } + else + { + this.protocolIdentifier = (string.IsNullOrEmpty(protocol) ? "Empty" : protocol); +#if LOGGER + logger.Warning("Unsupported protocol version '{0}'. Use at your own risk.", protocol); +#endif + } + if (reconnected) + { +#if LOGGER + logger.Info("Send Challenge action."); +#endif + ChallengeAction challengeAction = new ChallengeAction(); + try + { + SendAction(challengeAction, null); + } +#if LOGGER + catch(Exception ex) + { + logger.Info("Send Challenge fail : ", ex.Message); +#else + catch + { +#endif + disconnect(true); + } + return; + } + } + #endregion + + if (reconnected && e is DisconnectEvent) + { + ((DisconnectEvent)e).Reconnect = true; + fireEvent(e); + reconnect(false); + } + else if (!reconnected && reconnectEnable && (e is DisconnectEvent || e is ReloadEvent || e is ShutdownEvent)) + { + ((ConnectionStateEvent)e).Reconnect = true; + fireEvent(e); + reconnect(true); + } + else + fireEvent(e); + } + + private void eventComplete(IAsyncResult result) + { + } + + private void fireEvent(ManagerEvent e) + { + if (enableEvents && internalEvent != null) + internalEvent.BeginInvoke(this, e, new AsyncCallback(eventComplete), null); + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/ManagerReader.cs b/Asterisk.2013/Asterisk.NET/Manager/ManagerReader.cs new file mode 100644 index 0000000..471a684 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/ManagerReader.cs @@ -0,0 +1,354 @@ +using System; +using System.IO; +using System.Collections; +using System.Threading; +using System.Collections.Generic; +using System.Net.Sockets; +using System.Text; +using Asterisk.NET.IO; +using Asterisk.NET.Manager.Event; +using Asterisk.NET.Manager.Response; + +namespace Asterisk.NET.Manager +{ + + /// + /// Default implementation of the ManagerReader interface. + /// + public class ManagerReader + { +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + + private ManagerConnection mrConnector; + private SocketConnection mrSocket; + + private bool die = false; + private bool is_logoff = false; + private bool disconnect = false; + private byte[] lineBytes; + private string lineBuffer; + private Queue lineQueue; + private ResponseHandler pingHandler; + private bool processingCommandResult; + private bool wait4identiier; + private DateTime lastPacketTime; + Dictionary packet; + List commandList; + + #region ManagerReader(dispatcher, asteriskServer) + /// + /// Creates a new ManagerReader. + /// + /// the dispatcher to use for dispatching events and responses. + public ManagerReader(ManagerConnection connection) + { + this.mrConnector = connection; + this.die = false; + lineQueue = new Queue(); + packet = new Dictionary(); + commandList = new List(); + } + #endregion + + #region Socket + /// + /// Sets the socket to use for reading from the asterisk server. + /// + internal IO.SocketConnection Socket + { + set + { + this.mrSocket = value; + } + } + #endregion + + #region Die + internal bool Die + { + get { return die; } + set + { + die = value; + if (die) + this.mrSocket = null; + } + } + #endregion + + #region IsLogoff + internal bool IsLogoff + { + set { is_logoff = value; } + } + #endregion + + #region mrReaderCallbback(IAsyncResult ar) + /// + /// Async Read callback + /// + /// IAsyncResult + private void mrReaderCallbback(IAsyncResult ar) + { + // mreader = Mr.Reader + ManagerReader mrReader = (ManagerReader)ar.AsyncState; + if (mrReader.die) + return; + + SocketConnection mrSocket = mrReader.mrSocket; + if (mrSocket == null || mrSocket.TcpClient == null) + { + // No socket - it's DISCONNECT !!! + disconnect = true; + return; + } + + NetworkStream nstream = mrSocket.NetworkStream; + if (nstream == null) + { + // No network stream - it's DISCONNECT !!! + disconnect = true; + return; + } + + try + { + int count = nstream.EndRead(ar); + if (count == 0) + { + // No received data - it's may be DISCONNECT !!! + if(!is_logoff) + disconnect = true; + return; + } + string line = mrSocket.Encoding.GetString(mrReader.lineBytes, 0, count); + mrReader.lineBuffer += line; + int idx; + // \n - because not all dev in Digium use \r\n + // .Trim() kill \r + lock (((ICollection)lineQueue).SyncRoot) + while (!string.IsNullOrEmpty(mrReader.lineBuffer) && (idx = mrReader.lineBuffer.IndexOf("\n")) >= 0) + { + line = idx > 0 ? mrReader.lineBuffer.Substring(0, idx).Trim() : string.Empty; + mrReader.lineBuffer = (idx + 1 < mrReader.lineBuffer.Length ? mrReader.lineBuffer.Substring(idx + 1) : string.Empty); + lineQueue.Enqueue(line); + } + // Give a next portion !!! + nstream.BeginRead(mrReader.lineBytes, 0, mrReader.lineBytes.Length, mrReaderCallbback, mrReader); + } +#if LOGGER + catch (Exception ex) + { + mrReader.logger.Error("Read data error", ex.Message); +#else + catch + { +#endif + // Any catch - disconncatch ! + disconnect = true; + if (mrReader.mrSocket != null) + mrReader.mrSocket.Close(); + mrReader.mrSocket = null; + } + } + #endregion + + #region Reinitialize + internal void Reinitialize() + { + mrSocket.Initial = false; + disconnect = false; + lineQueue.Clear(); + packet.Clear(); + commandList.Clear(); + lineBuffer = string.Empty; + lineBytes = new byte[mrSocket.TcpClient.ReceiveBufferSize]; + lastPacketTime = DateTime.Now; + wait4identiier = true; + processingCommandResult = false; + mrSocket.NetworkStream.BeginRead(lineBytes, 0, lineBytes.Length, mrReaderCallbback, this); + lastPacketTime = DateTime.Now; + } + #endregion + + #region Run() + /// + /// Reads line by line from the asterisk server, sets the protocol identifier as soon as it is + /// received and dispatches the received events and responses via the associated dispatcher. + /// + /// + /// + /// + internal void Run() + { + if (mrSocket == null) + throw new SystemException("Unable to run: socket is null."); + + string line; + + while (true) + { + try + { + while (!die) + { + #region check line from * + if (!is_logoff) + { + if (mrSocket != null && mrSocket.Initial) + { + Reinitialize(); + } + else if (disconnect) + { + disconnect = false; + mrConnector.DispatchEvent(new DisconnectEvent(mrConnector)); + } + } + if (lineQueue.Count == 0) + { + if (mrConnector.PingInterval > 0 + && mrSocket != null + && !wait4identiier + && !is_logoff + && lastPacketTime.AddMilliseconds(mrConnector.PingInterval) < DateTime.Now + ) + { + if (pingHandler != null) + { + // In 1.6.0 no Response from Ping + if (pingHandler.Response == null) + { + // If one PingInterval from Ping without Pong then send Disconnect event + mrConnector.DispatchEvent(new DisconnectEvent(mrConnector)); + } + pingHandler.Free(); + mrConnector.RemoveResponseHandler(pingHandler); + pingHandler = null; + } + else + { + // Send PING to * + try + { + pingHandler = new ResponseHandler(new Action.PingAction(), null); + mrConnector.SendAction(pingHandler.Action, pingHandler); + } + catch + { + disconnect = true; + mrSocket = null; + } + } + lastPacketTime = DateTime.Now; + } + Thread.Sleep(50); + if (mrConnector.TraceCallerThread && mrConnector.CallerThread != null && mrConnector.CallerThread.ThreadState == ThreadState.Stopped) + { + die = true; + break; + } + continue; + } + #endregion + + lastPacketTime = DateTime.Now; + lock (((ICollection)lineQueue).SyncRoot) + line = lineQueue.Dequeue().Trim(); +#if LOGGER + logger.Debug(line); +#endif + #region processing Response: Follows + if (processingCommandResult) + { + if (line == "--END COMMAND--") + { + Response.CommandResponse commandResponse = new Response.CommandResponse(); + Helper.SetAttributes(commandResponse, packet); + commandResponse.Result = commandList; + processingCommandResult = false; + packet.Clear(); + mrConnector.DispatchResponse(commandResponse); + continue; + } + else + { + string lineLower = line.ToLower(Helper.CultureInfo); + if (lineLower.StartsWith("privilege: ") + || lineLower.StartsWith("actionid: ") + || lineLower.StartsWith("timestamp: ") + || lineLower.StartsWith("server: ") + ) + Helper.AddKeyValue(packet, line); + else + commandList.Add(line); + } + continue; + } + #endregion + + #region collect key: value and ProtocolIdentifier + if (!string.IsNullOrEmpty(line)) + { + if (wait4identiier && line.StartsWith("Asterisk Call Manager")) + { + wait4identiier = false; + ConnectEvent connectEvent = new ConnectEvent(mrConnector); + connectEvent.ProtocolIdentifier = line; + mrConnector.DispatchEvent(connectEvent); + continue; + } + if (line.Trim().ToLower(Helper.CultureInfo) == "response: follows") + { + // Switch to wait "--END COMMAND--" mode + processingCommandResult = true; + packet.Clear(); + commandList.Clear(); + Helper.AddKeyValue(packet, line); + continue; + } + Helper.AddKeyValue(packet, line); + continue; + } + #endregion + + #region process events and responses + + if (packet.ContainsKey("event")) + mrConnector.DispatchEvent(packet); + + else if (packet.ContainsKey("response")) + mrConnector.DispatchResponse(packet); + + #endregion + + packet.Clear(); + } + if(mrSocket != null) + mrSocket.Close(); + break; + } +#if LOGGER + catch (Exception ex) + { + logger.Info("Exception : {0}", ex.Message); +#else + catch + { +#endif + } + + if (die) + break; + +#if LOGGER + logger.Info("No die, any error - send disconnect."); +#endif + mrConnector.DispatchEvent(new DisconnectEvent(mrConnector)); + } + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Originate.cs b/Asterisk.2013/Asterisk.NET/Manager/Originate.cs new file mode 100644 index 0000000..42cf644 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Originate.cs @@ -0,0 +1,179 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager +{ + public class Originate + { + private string channel; + private string exten; + private string context; + private int priority; + private long timeout; + private string callerId; + private Dictionary variables; + private string account; + private string application; + private string data; + + #region Account + /// + /// 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. + /// + public string Account + { + get { return account; } + set { this.account = value; } + } + #endregion + #region CallerId + /// + /// Get/Set the caller id to set on the outgoing channel. + /// + public string CallerId + { + get { return callerId; } + set { this.callerId = value; } + } + #endregion + #region Channel + /// + /// Get/Set the name of the channel to connect to the outgoing call. + /// This property is required. + /// + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + #endregion + #region Context + /// + /// 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. + /// + public string Context + { + get { return context; } + set { this.context = value; } + } + #endregion + #region Exten + /// + /// Get/Set the extension to connect to. + /// If you set the extension you also have to set the context and priority properties. + /// + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + #endregion + #region Priority + /// + /// 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. + /// + public int Priority + { + get { return priority; } + set { this.priority = value; } + } + #endregion + #region Application + /// + /// Get/Set the name of the application to connect to. + /// + public string Application + { + get { return application; } + set { this.application = value; } + } + #endregion + #region Data + /// + /// Get/Set the parameters to pass to the application. + /// + public string Data + { + get { return data; } + set { this.data = value; } + } + #endregion + #region Timeout + /// + /// Get/Set the timeout for the origination (in seconds) for the origination.
+ /// The channel must be answered within this time, otherwise the origination + /// is considered to have failed and an OriginateFailureEvent is generated.
+ /// If not set, a default value of 30 seconds. + ///
+ public long Timeout + { + get { return timeout / 1000; } + set { this.timeout = value * 1000; } + } + #endregion + + #region Variable + /// + /// Get/Set the variables to set on the originated call.
+ /// Variable assignments are of the form "VARNAME=VALUE". You can specify + /// multiple variable assignments separated by the '|' character.
+ /// Example: "VAR1=abc|VAR2=def" sets the channel variables VAR1 to "abc" and VAR2 to "def". + ///
+ public string Variable + { + get { return Helper.JoinVariables(variables, Common.VAR_DELIMITER, "="); } + set { variables = Helper.ParseVariables(variables, value, Common.VAR_DELIMITER); } + } + #endregion + + #region GetVariables() + /// + /// Get the variables dictionary to set on the originated call. + /// + public Dictionary GetVariables() + { + return variables; + } + #endregion + #region SetVariables(IDictionary vars) + /// + /// Set the variables dictionary to set on the originated call. + /// + public void SetVariables(Dictionary vars) + { + this.variables = vars; + } + #endregion + #region GetVariable(string name, string val) + /// + /// Gets a variable on the originated call. Replaces any existing variable with the same name. + /// + public string GetVariable(string key) + { + if (variables == null) + return string.Empty; + return variables[key]; + } + #endregion + #region SetVariable(string name, string val) + /// + /// Sets a variable dictionary on the originated call. Replaces any existing variable with the same name. + /// + public void SetVariable(string key, string value) + { + if (variables == null) + variables = new Dictionary(); + if (variables.ContainsKey(key)) + variables[key] = value; + else + variables.Add(key, value); + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/ChallengeResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/ChallengeResponse.cs new file mode 100644 index 0000000..060524f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/ChallengeResponse.cs @@ -0,0 +1,21 @@ +namespace Asterisk.NET.Manager.Response +{ + /// + /// Corresponds to a ChallengeAction and contains the challenge needed to log in using challenge/response. + /// + /// + /// + public class ChallengeResponse : ManagerResponse + { + private string challenge; + /// + /// Get/Set the challenge to use when creating the key for log in. + /// + /// + public string Challenge + { + get { return challenge; } + set { this.challenge = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/CommandResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/CommandResponse.cs new file mode 100644 index 0000000..e902543 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/CommandResponse.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Response +{ + /// + /// Corresponds to a CommandAction.
+ /// Asterisk's handling of the command action is generelly quite hairy. + /// It sends a "Response: Follows" line followed by the raw output of the command including empty lines. + /// At the end of the command output a line containing "--END COMMAND--" is sent. + /// The reader parses this response into a CommandResponse object to hide these details. + ///
+ /// + public class CommandResponse : ManagerResponse + { + protected internal List result; + + /// + /// Get/Set a List containing strings representing the lines returned by the CLI command. + /// + public List Result + { + get { return result; } + set { this.result = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/ExtensionStateResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/ExtensionStateResponse.cs new file mode 100644 index 0000000..e35dbe2 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/ExtensionStateResponse.cs @@ -0,0 +1,33 @@ +using System.Text; + +namespace Asterisk.NET.Manager.Response +{ + public class ExtensionStateResponse : ManagerResponse + { + private string exten; + private string context; + private string hint; + private int status; + + public string Exten + { + get { return exten; } + set { this.exten = value; } + } + public string Context + { + get { return context; } + set { this.context = value; } + } + public string Hint + { + get { return hint; } + set { this.hint = value; } + } + public int Status + { + get { return status; } + set { this.status = value; } + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/GetConfigResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/GetConfigResponse.cs new file mode 100644 index 0000000..d058550 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/GetConfigResponse.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Asterisk.NET.Manager.Response +{ + /// + /// Response that is received when sending a GetConfigAction.
+ /// Asterisk's response to the GetConfig command is ugly, and requires some + /// parsing of attributes. This class lazily parses its own attributes to hide + /// the ugly details. If the file requested exists but does not contain at least + /// a line with a category, the ResponseBuilder won't create an instance of + /// GetConfigResponse, as it won't know what the empty response is. + ///
+ public class GetConfigResponse : ManagerResponse + { + private Dictionary categories; + private Dictionary> lines; + + /// + /// Get the map of category numbers to category names. + /// + public Dictionary Categories + { + get + { + if (categories == null) + { + categories = new Dictionary(); + lines = new Dictionary>(); + if (attributes != null) + foreach (string key in attributes.Keys) + { + string keyLower = key.ToLower(Helper.CultureInfo); + if (keyLower.StartsWith("category-")) + { + string[] parts = key.Split(Common.MINUS_SEPARATOR); + if (parts.Length < 2) + continue; + + int categoryNumber; + if (!int.TryParse(parts[1], out categoryNumber)) + continue; + categories.Add(categoryNumber, attributes[key]); + continue; + } + if (keyLower.StartsWith("line-")) + { + string[] parts = key.Split(Common.MINUS_SEPARATOR); + if (parts.Length < 3) + continue; + + int categoryNumber; + if (!int.TryParse(parts[1], out categoryNumber)) + continue; + + int lineNumber; + if (!int.TryParse(parts[2], out lineNumber)) + continue; + if (!lines.ContainsKey(categoryNumber)) + lines.Add(categoryNumber, new Dictionary()); + + if(lines[categoryNumber].ContainsKey(lineNumber)) + lines[categoryNumber][lineNumber] = attributes[key]; + else + lines[categoryNumber].Add(lineNumber, attributes[key]); + } + } + } + return categories; + } + } + + /// + /// Returns the map of line number to line value for a given category. + /// + /// a valid category number from getCategories. + /// + public Dictionary Lines(int category) + { + if (lines == null) + { + lines = new Dictionary>(); + if (attributes != null) + foreach (string key in attributes.Keys) + if (key.ToLower(Helper.CultureInfo).StartsWith("line-")) + { + string[] parts = key.Split(Common.MINUS_SEPARATOR); + if (parts.Length < 3) + continue; + + int categoryNumber; + if (!int.TryParse(parts[1], out categoryNumber)) + continue; + + int lineNumber; + if (!int.TryParse(parts[2], out lineNumber)) + continue; + if (!lines.ContainsKey(categoryNumber)) + lines.Add(categoryNumber, new Dictionary()); + + if (lines[categoryNumber].ContainsKey(lineNumber)) + lines[categoryNumber][lineNumber] = attributes[key]; + else + lines[categoryNumber].Add(lineNumber, attributes[key]); + } + } + if (lines.ContainsKey(category)) + return lines[category]; + return new Dictionary(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxCountResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxCountResponse.cs new file mode 100644 index 0000000..c98a529 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxCountResponse.cs @@ -0,0 +1,39 @@ +namespace Asterisk.NET.Manager.Response +{ + /// + /// A MailboxCountResponse is sent in response to a MailboxCountAction and contains the number of old + /// and new messages in a mailbox. + /// + /// + public class MailboxCountResponse:ManagerResponse + { + private string mailbox; + private int newMessages; + private int oldMessages; + + /// + /// Get/Set the name of the mailbox. + /// + public string Mailbox + { + get { return mailbox; } + set { this.mailbox = value; } + } + /// + /// Get/Set the number of new messages in the mailbox. + /// + public int NewMessages + { + get { return newMessages; } + set { this.newMessages = value; } + } + /// + /// Returns the number of old messages in the mailbox. + /// + public int OldMessages + { + get { return oldMessages; } + set { this.oldMessages = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxStatusResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxStatusResponse.cs new file mode 100644 index 0000000..eb4297d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/MailboxStatusResponse.cs @@ -0,0 +1,32 @@ +namespace Asterisk.NET.Manager.Response +{ + /// + /// A MailboxStatusResponse is sent in response to a MailboxStatusAction and indicates if a set + /// of mailboxes contains waiting messages. + /// + /// + public class MailboxStatusResponse:ManagerResponse + { + /// The name of the mailbox. + private string mailbox; + /// Indicates if there are new messages waiting in the given set of mailboxes. + private bool waiting; + + /// + /// Get/Set the names of the mailboxes, separated by ",". + /// + public string Mailbox + { + get { return mailbox; } + set { this.mailbox = value; } + } + /// + /// Get/Set true if at least one of the given mailboxes contains new messages, false otherwise. + /// + public bool Waiting + { + get { return waiting; } + set { this.waiting = value; } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerError.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerError.cs new file mode 100644 index 0000000..030894c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerError.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Response +{ + /// + /// Represents an "Response: Error" response received from the asterisk server. + /// The cause for the error is given in the message attribute. + /// + public class ManagerError : ManagerResponse + { + /// Creates a new ManagerError. + public ManagerError() + : base() + { + } + public ManagerError(Dictionary attributes) + : base(attributes) + { + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerResponse.cs new file mode 100644 index 0000000..facc63b --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/ManagerResponse.cs @@ -0,0 +1,217 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager.Response +{ + /// + /// Represents a response received from the Asterisk server as the result of a + /// previously sent ManagerAction.
+ /// The response can be linked with the action that caused it by looking the + /// action id attribute that will match the action id of the corresponding + /// action. + ///
+ public class ManagerResponse : IParseSupport + { + private DateTime dateReceived; + private string privilege; + private string actionId; + private string response; + private string message; + private string uniqueId; + private string server; + protected Dictionary attributes; + + #region Constructor - ManagerEvent() + public ManagerResponse() + { + this.dateReceived = DateTime.Now; + } + public ManagerResponse(Dictionary attributes) + : this() + { + Helper.SetAttributes(this, attributes); + } + + #endregion + + + #region Attributes + /// + /// Store all unknown (without setter) keys from manager event.
+ /// Use in default Parse method . + ///
+ public Dictionary Attributes + { + get { return attributes; } + } + #endregion + + #region Server + /// + /// Specify a server to which to send your commands (x.x.x.x or hostname).
+ /// 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. + ///
+ public string Server + { + get { return this.server; } + set { this.server = value; } + } + #endregion + + #region DateReceived + /// + /// Get/Set the point in time this response was received from the asterisk server. + /// + public DateTime DateReceived + { + get { return dateReceived; } + set { this.dateReceived = value; } + } + #endregion + + #region Privilege + /// + /// Get/Set the AMI authorization class of this event.
+ /// This is one or more of system, call, log, verbose, command, agent or user. + /// Multiple privileges are separated by comma.
+ /// Note: This property is not available from Asterisk 1.0 servers. + ///
+ public string Privilege + { + get { return privilege; } + set { this.privilege = value; } + } + #endregion + + #region ActionId + /// + /// Get/Set the action id received with this response referencing the action that generated this response. + /// + public string ActionId + { + get { return actionId; } + set { this.actionId = value; } + } + #endregion + + #region Message + /// + /// Get/Set the message received with this response.
+ /// The content depends on the action that generated this response. + ///
+ public string Message + { + get { return message; } + set { this.message = value; } + } + #endregion + + #region Response + /// + /// Get/Set the value of the "Response:" line.
+ /// This typically a String like "Success" or "Error" but depends on the action that generated this response. + ///
+ public string Response + { + get { return response; } + set { this.response = value; } + } + #endregion + + #region UniqueId + /// + /// Get/Set the unique id received with this response.
+ /// The unique id is used to keep track of channels created by the action sent, for example an OriginateAction. + ///
+ public string UniqueId + { + get { return uniqueId; } + set { this.uniqueId = value; } + } + #endregion + + + #region IsSuccess() + /// + /// Return true if Response is success + /// + /// + public bool IsSuccess() + { + return response == "Success"; + } + #endregion + + #region GetAttribute(string key) + /// + /// Returns the value of the attribute with the given key.
+ /// This is particulary important when a response contains special + /// attributes that are dependent on the action that has been sent.
+ /// An example of this is the response to the GetVarAction. + /// It contains the value of the channel variable as an attribute + /// stored under the key of the variable name.
+ /// Example: + ///
+		/// GetVarAction action = new GetVarAction();
+		/// action.setChannel("SIP/1310-22c3");
+		/// action.setVariable("ALERT_INFO");
+		/// ManagerResponse response = connection.SendAction(action);
+		/// String alertInfo = response.getAttribute("ALERT_INFO");
+		/// 
+ /// As all attributes are internally stored in lower case the key is + /// automatically converted to lower case before lookup. + ///
+ /// the key to lookup. + /// the value of the attribute stored under this key or + /// null if there is no such attribute. + /// + public string GetAttribute(string key) + { + return (string) attributes[key.ToLower(Helper.CultureInfo)]; + } + #endregion + + #region Parse(string key, string value) + /// + /// Unknown properties parser + /// + /// key name + /// key value + /// true - value parsed, false - can't parse value + public virtual bool Parse(string key, string value) + { + if (attributes == null) + attributes = new Dictionary(); + + if (attributes.ContainsKey(key)) + // Key already presents, add with delimiter + attributes[key] += string.Concat(Common.LINE_SEPARATOR, value); + else + attributes.Add(key, value); + return true; + } + #endregion + + #region ParseSpecial(Dictionary attributes) + /// + /// Unknown properties parser + /// + /// dictionary + /// updated dictionary + public virtual Dictionary ParseSpecial(Dictionary attributes) + { + return attributes; + } + #endregion + + #region ToString() + public override string ToString() + { + return Helper.ToString(this); + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/Response/OriginateResponse.cs b/Asterisk.2013/Asterisk.NET/Manager/Response/OriginateResponse.cs new file mode 100644 index 0000000..d0fa50f --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/Response/OriginateResponse.cs @@ -0,0 +1,110 @@ +using System; +using System.Text; + +namespace Asterisk.NET.Manager.Response +{ + public class OriginateResponse + { + private string uniqueId; + private int reason; + private string channel; + private string channelName; + private string response; + + private DateTime startTime; + private DateTime endTime = DateTime.MinValue; + + #region IsSuccess + public bool IsSuccess + { + get { return response == "Success"; } + } + #endregion + + #region UniqueId + public string UniqueId + { + get { return uniqueId; } + set { this.uniqueId = value; } + } + #endregion + + #region Channel + public string Channel + { + get { return channel; } + set { this.channel = value; } + } + #endregion + + #region ChannelName + public string ChannelName + { + get { return channelName; } + set { this.channelName = value; } + } + #endregion + + #region Reason + public int Reason + { + get { return reason; } + set { this.reason = value; } + } + #endregion + + #region Response + public string Response + { + get { return response; } + set { response = value; } + } + #endregion + + #region StartTime + public DateTime StartTime + { + get { return startTime; } + set { this.startTime = value; } + } + #endregion + + #region EndTime + public DateTime EndTime + { + get { return endTime; } + set { this.endTime = value; } + } + #endregion + + #region Constructor - Call() + public OriginateResponse() + { + startTime = DateTime.Now; + } + #endregion + + #region CalcDuration() + /// + /// Return the duration of the call in milliseconds. If the call is has not + /// ended, the duration so far is calculated. + /// + public long CalcDuration() + { + DateTime compTime; + if (endTime != DateTime.MinValue) + compTime = endTime; + else + compTime = DateTime.Now; + return (compTime.Ticks - startTime.Ticks) / 10000; + } + #endregion + + #region ToString() + public override string ToString() + { + return Helper.ToString(this); + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/ResponseEventHandler.cs b/Asterisk.2013/Asterisk.NET/Manager/ResponseEventHandler.cs new file mode 100644 index 0000000..1d4bc6c --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/ResponseEventHandler.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using Asterisk.NET.Manager.Action; +using Asterisk.NET.Manager.Event; +using Asterisk.NET.Manager.Response; + +namespace Asterisk.NET.Manager +{ + /// + /// A combinded event and response handler that adds received events and the response to a ResponseEvents object. + /// + public class ResponseEventHandler : IResponseHandler + { + private ManagerConnection connection; + private AutoResetEvent autoEvent; + private ResponseEvents events; + private ManagerActionEvent action; + private int hash; + + /// + /// Creates a new instance. + /// + /// the ResponseEvents to store the events in + /// the type of event that indicates that all events have been received + /// the thread to interrupt when the actionCompleteEventClass has been received + public ResponseEventHandler(ManagerConnection connection, ManagerActionEvent action, AutoResetEvent autoEvent) + { + this.connection = connection; + this.events = new ResponseEvents(); + this.action = action; + this.autoEvent = autoEvent; + } + + public ManagerAction Action + { + get { return action; } + } + + public ResponseEvents ResponseEvents + { + get { return events; } + } + + public int Hash + { + get { return hash; } + set { hash = value; } + } + + public void Free() + { + connection = null; + autoEvent = null; + action = null; + events.Events.Clear(); + events.Response = null; + events = null; + } + + public void HandleEvent(ManagerEvent e) + { + // should always be a ResponseEvent, anyway... + if (e is Event.ResponseEvent) + { + ResponseEvent responseEvent = (ResponseEvent)e; + events.AddEvent(responseEvent); + } + + // finished? + if (action.ActionCompleteEventClass().IsAssignableFrom(e.GetType())) + { + lock (events) + events.Complete = true; + if (events.Response != null) + autoEvent.Set(); + } + } + + public void HandleResponse(ManagerResponse response) + { + events.Response = response; + if (response is ManagerError) + events.Complete = true; + + if (events.Complete && autoEvent != null) + autoEvent.Set(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Manager/ResponseEvents.cs b/Asterisk.2013/Asterisk.NET/Manager/ResponseEvents.cs new file mode 100644 index 0000000..6c976ee --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/ResponseEvents.cs @@ -0,0 +1,55 @@ +using System.Collections; +using Asterisk.NET.Manager.Response; +using Asterisk.NET.Manager.Event; +using System.Collections.Generic; + +namespace Asterisk.NET.Manager +{ + /// + /// Collection of ResponseEvent. Use in events generation actions. + /// + public class ResponseEvents + { + private ManagerResponse response; + private List events; + private bool complete; + + public ManagerResponse Response + { + get { return this.response; } + set { this.response = value; } + } + + public List Events + { + get { return events; } + } + /// + /// Indicats if all events have been received. + /// + public bool Complete + { + get { return this.complete; } + set { this.complete = value; } + } + + /// Creates a new instance. + public ResponseEvents() + { + this.events = new List(); + this.complete = false; + } + + /// + /// Adds a ResponseEvent that has been received. + /// + /// the ResponseEvent that has been received. + public void AddEvent(ResponseEvent e) + { + lock (((IList)events).SyncRoot) + { + events.Add(e); + } + } + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Manager/ResponseHandler.cs b/Asterisk.2013/Asterisk.NET/Manager/ResponseHandler.cs new file mode 100644 index 0000000..1b1906e --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Manager/ResponseHandler.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using Asterisk.NET.Manager.Action; +using Asterisk.NET.Manager.Response; + +namespace Asterisk.NET.Manager +{ + /// + /// A simple response handler that stores the received response in a ResponseHandlerResult for further processing. + /// + public class ResponseHandler : IResponseHandler + { + private ManagerResponse response; + private AutoResetEvent autoEvent; + private ManagerAction action; + private int hash; + + /// + /// Creates a new instance. + /// + /// the result to store the response in + /// the thread to interrupt when the response has been received + public ResponseHandler(ManagerAction action, AutoResetEvent autoEvent) + { + this.response = null; + this.action = action; + this.autoEvent = autoEvent; + } + + public ManagerAction Action + { + get { return this.action; } + } + + public ManagerResponse Response + { + get { return this.response; } + } + + public int Hash + { + get { return hash; } + set { hash = value; } + } + + public void Free() + { + autoEvent = null; + action = null; + response = null; + } + + public virtual void HandleResponse(ManagerResponse response) + { + this.response = response; + if(autoEvent != null) + this.autoEvent.Set(); + } + } +} diff --git a/Asterisk.2013/Asterisk.NET/Properties/AssemblyInfo.cs b/Asterisk.2013/Asterisk.NET/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1b3a1d1 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Asterisk.NET")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("X893, http://akb77.com/g")] +[assembly: AssemblyProduct("Asterisk.NET")] +[assembly: AssemblyCopyright("Copyright © 2005-2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: Guid("abe98502-ea83-4b04-98c3-ffe3eabe06b0")] +[assembly: AssemblyVersion("1.6.3.1")] +[assembly: AssemblyFileVersion("1.6.3.1")] + +// 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)] diff --git a/Asterisk.2013/Asterisk.NET/Util/MD5Support.cs b/Asterisk.2013/Asterisk.NET/Util/MD5Support.cs new file mode 100644 index 0000000..70922cb --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Util/MD5Support.cs @@ -0,0 +1,98 @@ +using System; +using System.Security.Cryptography; + +namespace Asterisk.NET.Util +{ + /// + /// Encapsulates the functionality of message digest algorithms such as SHA-1 or MD5. + /// + public class MD5Support + { + private HashAlgorithm algorithm; + private byte[] data = new byte[0]; + private int position; + private string algorithmName; + + /// + /// Creates a message digest using the specified name to set Algorithm property. + /// + /// The name of the algorithm to use + public MD5Support(string algorithm) + { + if (algorithm.Equals("SHA-1")) + this.algorithmName = "SHA"; + else + this.algorithmName = algorithm; + this.algorithm = (HashAlgorithm)CryptoConfig.CreateFromName(this.algorithmName); + this.data = new byte[0]; + this.position = 0; + } + + #region DigestData + /// + /// Computes the hash value for the internal data digest. + /// + /// The array of signed bytes with the resulting hash value + public sbyte[] DigestData + { + get + { + byte[] bytes = this.algorithm.ComputeHash(this.data); + sbyte[] sbytes = new sbyte[bytes.Length]; + for (int i = 0; i < bytes.Length; i++) + sbytes[i] = (sbyte)bytes[i]; + this.data = null; + this.position = 0; + return sbytes; + } + } + #endregion + + #region Update + /// + /// Updates the digest data with the specified array of bytes by making an append + /// operation in the internal array of data. + /// + /// The array of bytes for the update operation + public void Update(byte[] newData) + { + if (position == 0) + { + this.data = newData; + this.position = this.data.Length - 1; + } + else + { + byte[] oldData = this.data; + this.data = new byte[newData.Length + position + 1]; + oldData.CopyTo(this.data, 0); + newData.CopyTo(this.data, oldData.Length); + this.position = this.data.Length - 1; + } + } + #endregion + + #region GetInstance + /// + /// Generates a new instance of the MessageDigestSupport class using the specified algorithm + /// + /// The name of the algorithm to use + /// A new instance of the MessageDigestSupport class + public static MD5Support GetInstance(string algorithm) + { + return new MD5Support(algorithm); + } + #endregion + + #region GetInstance + /// + /// Generates a new instance of the MessageDigestSupport class using the specified algorithm + /// + /// A new instance of the MD5 algorithm class + public static MD5Support GetInstance() + { + return new MD5Support("MD5"); + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Util/ThreadClass.cs b/Asterisk.2013/Asterisk.NET/Util/ThreadClass.cs new file mode 100644 index 0000000..0798d48 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Util/ThreadClass.cs @@ -0,0 +1,125 @@ +using System; +using System.Threading; + +namespace Asterisk.NET.Util +{ + /// + /// Support class used to handle threads + /// + public class ThreadClass + { + /// The instance of Threading.Thread + private Thread thread; + + #region ThreadClass() + /// + /// Initializes a new instance of the ThreadClass class + /// + public ThreadClass() + { + thread = new Thread(new ThreadStart(Run)); + } + #endregion + + #region ThreadClass(name) + /// + /// Initializes a new instance of the Thread class. + /// + /// The name of the thread + public ThreadClass(string Name) + { + thread = new Thread(new ThreadStart(Run)); + this.Name = Name; + } + #endregion + + #region ThreadClass(start) + /// + /// Initializes a new instance of the Thread class. + /// + /// A ThreadStart delegate that references the methods to be invoked when this thread begins executing + public ThreadClass(ThreadStart start) + { + thread = new Thread(start); + } + #endregion + + #region ThreadClass(start, name) + /// + /// Initializes a new instance of the Thread class. + /// + /// A ThreadStart delegate that references the methods to be invoked when this thread begins executing + /// The name of the thread + public ThreadClass(ThreadStart start, string name) + { + thread = new Thread(start); + this.Name = name; + } + #endregion + + #region Run() + /// + /// This method has no functionality unless the method is overridden + /// + public virtual void Run() + { + } + #endregion + + #region Start() + /// + /// Causes the operating system to change the state of the current thread instance to ThreadState.Running + /// + public void Start() + { + thread.Start(); + } + #endregion + + #region Interrupt() + /// + /// Interrupts a thread that is in the WaitSleepJoin thread state + /// + public void Interrupt() + { + thread.Interrupt(); + } + #endregion + + #region Name + /// + /// Gets or sets the name of the thread + /// + public string Name + { + get { return thread.Name; } + set + { + if (thread.Name == null) + thread.Name = value; + } + } + #endregion + + #region IsAlive + /// + /// Gets a value indicating the execution status of the current thread + /// + public bool IsAlive + { + get { return thread.IsAlive; } + } + #endregion + + #region IsBackground + /// + /// Gets or sets a value indicating whether or not a thread is a background thread. + /// + public bool IsBackground + { + get { return thread.IsBackground; } + set { thread.IsBackground = value; } + } + #endregion + } +} diff --git a/Asterisk.2013/Asterisk.NET/Util/ThreadPool.cs b/Asterisk.2013/Asterisk.NET/Util/ThreadPool.cs new file mode 100644 index 0000000..1f826c5 --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Util/ThreadPool.cs @@ -0,0 +1,118 @@ +using System.Threading; +using System.Collections; +using Asterisk.NET.FastAGI; +using System.Collections.Generic; + +namespace Asterisk.NET.Util +{ + /// + /// A fixed sized thread pool. + /// + public class ThreadPool + { +#if LOGGER + private Logger logger = Logger.Instance(); +#endif + private bool running; + private int numThreads; + private string name; + private List jobs; + + #region Constructor - ThreadPool(string name, int numThreads) + /// + /// Creates a new ThreadPool of numThreads size. These Threads are waiting + /// for jobs to be added via the addJob method. + /// + /// the name to use for the thread group and worker threads. + /// the number of threads to create. + public ThreadPool(string name, int numThreads) + { + this.name = name; + this.numThreads = numThreads; + jobs = new List(); + running = true; + + // create and start the threads + for (int i = 0; i < this.numThreads; i++) + { + ThreadTask thread; + thread = new ThreadTask(this, this.name + "-TaskThread-" + i); + thread.Start(); + } +#if LOGGER + logger.Debug("ThreadPool created with " + this.numThreads + " threads."); +#endif + } + #endregion + + #region obtainJob() + /// + /// Gets a job from the queue. If none is availble the calling thread is + /// blocked until one is added. + /// + /// the next job to service, null if the worker thread should be shut down. + internal AGIConnectionHandler obtainJob() + { + AGIConnectionHandler job = null; + lock (jobs) + { + while (job == null && running) + { + try + { + if (jobs.Count == 0) + Monitor.Wait(jobs); + } + catch (ThreadInterruptedException ex) + { +#if LOGGER + logger.Error("System.Threading.ThreadInterruptedException.", ex); +#else + throw ex; +#endif + } + if (jobs.Count > 0) + { + job = jobs[0]; + jobs.RemoveAt(0); + } + } + } + + if (running) + return job; + else + return null; + } + #endregion + + #region AddJob(AGIConnectionHandler runnable) + /// Adds a new job to the queue. This will be picked up by the next available + /// active thread. + /// + public void AddJob(AGIConnectionHandler runnable) + { + lock (jobs) + { + jobs.Add(runnable); + Monitor.PulseAll(jobs); + } + } + #endregion + + #region Shutdown() + /// Turn off the pool. Every thread, when finished with its current work, + /// will realize that the pool is no longer running, and will exit. + /// + public void Shutdown() + { + running = false; + lock (jobs) + Monitor.PulseAll(jobs); +#if LOGGER + logger.Debug("ThreadPool shutting down."); +#endif + } + #endregion + } +} \ No newline at end of file diff --git a/Asterisk.2013/Asterisk.NET/Util/ThreadTask.cs b/Asterisk.2013/Asterisk.NET/Util/ThreadTask.cs new file mode 100644 index 0000000..7727e7d --- /dev/null +++ b/Asterisk.2013/Asterisk.NET/Util/ThreadTask.cs @@ -0,0 +1,44 @@ +using System; +using Asterisk.NET.FastAGI; + +namespace Asterisk.NET.Util +{ + /// + /// A TaskThread sits in a loop, asking the pool for a job, and servicing it. + /// + internal class ThreadTask : ThreadClass + { + private ThreadPool threadPool; + + #region ThreadPool + public ThreadPool ThreadPool + { + get { return threadPool; } + } + #endregion + + #region ThreadTask(ThreadPool enclosingInstance, string name) + public ThreadTask(ThreadPool enclosingInstance, string name) + : base(name) + { + this.threadPool = enclosingInstance; + } + #endregion + + #region Run() + /// + /// Get a job from the pool, run it, repeat. If the obtained job is null, we exit the loop and the thread. + /// + public override void Run() + { + while (true) + { + AGIConnectionHandler job = ThreadPool.obtainJob(); + if (job == null) + break; + job.Run(); + } + } + #endregion + } +} diff --git a/Asterisk.2013/ChangeLog.txt b/Asterisk.2013/ChangeLog.txt new file mode 100644 index 0000000..39bd59e --- /dev/null +++ b/Asterisk.2013/ChangeLog.txt @@ -0,0 +1,69 @@ +18.01.2013 (ppumkin) + Re-homed project to CodePlex with TFS Support + Upgraded Project to .NET 4.5 + Allow connection to Asterisk 1.8.x - Still uses the 1.6.x logic, so any 1.8 changes are not yet implemented. <- TODO + + +09.06.2009 + Fix QueueAdd action + Fix Ping actionid with 1.6.0.10 version + Fix Bridge event attributes +02.18.2009 + Fix event with unregistered or empry action id + Add reconnect properties +10.15.2008 + Change MappingStrategy.cs line 80 + from ResourceReader rr = new ResourceReader(resourceName); + to ResourceReader rr = new ResourceReader(AppDomain.CurrentDomain.BaseDirectory + resourceName); + Change AsteriskFastAGI default socket encoding from Encoding.UTF8 to Encoding.ASCII +09.18.2008 + Change read socket to async model (BeginRead/EndRead) + Add Ping/Pong heartbeat to check connection (ManagerConnection.PingInterval in milliseconds). + Remove AsteriskManager class (use ManagerConnection). + Change Events event handler to UnhandleEvent and change event logic: + ManagerConnection.FireAllEvents property = false + ManagerConnection event only individual events by event handlers and event all other (unknown) events to UnhandleEvent + ManagerConnection.FireAllEvents property = true + ManagerConnection event individual events by event handlers and if individual handler not set then fire event to UnhandleEvent + Combine Connect/Disconnect/Reload/Shutdown events to one ConnectionState event handler. + Change reconnection login: + ManagerConnection.KeepAlive property to control auto reconnection on Disconnect/Reload/Shutdown (default true) + add Reconnect property to Connect/Disconnect/Reload/Shutdown events and if this property = true - it's means that reconnection process started (Disconnect/Reload/Shutdown) or done (Connect) + by default thi means that two events fire on reconnect. For example: + 1. Reload (.Reconnect = true) + 2. if success Connect (.Reconnect = true) + 3. or fail Disconnect (.Reconnect = false) + Fix error with Close Windows.Forms without Logoff() - to disable this feauture set TraceCallerThread property to false + Add GetConfig/UpdateConfig actions. + Change Logger logic. Add Visible() methods to define logger rules by level and class:method. + Add custom response to Action (only event in previous) (see GetConfig for example) + +08.26.2008 + Remove exception if internalActionId is not define in event + Add Attributes property to ManagerEvent to accept all non defined keys (resolve no setter exception). + Add Attributes property to ManagerResponse to accept all non defined keys (resolve no setter exception). +08.18.2008 + Fix Originate error (check for 1.4.21 and 1.6-b9) +08.16.2008 + Add AstManProxy support + Add NO_TIMEOUT symbol to disable timeout + Fix numeric conversion error (asterisk 1.6-beta-9 chan_iax2.c line 7501) +08.09.2008 + Add VideoSupport, TextSupport, RealtimeDevice to PeerEntryEvent + Change all .ToLower() to .ToLower(...culture("en")...) + Separate MeetmeJoinEvent and MeetmeLeaveEvent (both from MeetmeEvent). + Rename all MeetMe... to Meetme... + Remove Event tail from event name (ex. JabberEvent) +06.20.2008 + Change reconnect thread model. +06.19.2008 + Fix JoinEvent UniqueId parameter. + Set default socket encoding to ASCII (thanks to Phillip N.) - see SocketEncoding property of AsteriskManager class. + Add support Manager events with Asteirsk 1.6 beta 9. + Fix Asterisk version determination. + Fix Reload and Disconnect event handlers. + Add WinForm example (thanks to shurik_ev). +05.19.2008 + Add Name property to QueueMemberEvent class +04.22.2008 + Support Asterisk 1.4 and new custom IVR with FastAGI protocol.