2013-01-18 15:55:50 +00:00
using System ;
using System.Threading ;
using System.Collections ;
2014-01-08 14:16:39 +00:00
using AsterNET.Manager.Action ;
using AsterNET.Manager.Event ;
using AsterNET.Manager.Response ;
2013-01-18 15:55:50 +00:00
using System.Text.RegularExpressions ;
using System.Text ;
using System.Collections.Generic ;
using System.Reflection ;
2014-01-08 14:16:39 +00:00
using AsterNET.IO ;
2013-01-18 15:55:50 +00:00
2014-01-08 14:16:39 +00:00
namespace AsterNET.Manager
2013-01-18 15:55:50 +00:00
{
2016-09-20 13:29:46 +00:00
/// <summary>
2018-09-21 22:50:34 +00:00
/// Default implementation of the ManagerConnection interface.
2016-09-20 13:29:46 +00:00
/// </summary>
public class ManagerConnection
2017-04-05 05:32:55 +00:00
{
#region Variables
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
private Logger logger = Logger . Instance ( ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
private long actionIdCount = 0 ;
private string hostname ;
private int port ;
private string username ;
private string password ;
private SocketConnection mrSocket ;
private Thread 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 < int , IResponseHandler > responseHandlers ;
private Dictionary < int , IResponseHandler > pingHandlers ;
private Dictionary < int , IResponseHandler > 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 < int , ConstructorInfo > registeredEventClasses ;
2018-01-16 06:39:11 +00:00
private Dictionary < int , Func < ManagerEvent , bool > > registeredEventHandlers ;
2017-04-05 05:32:55 +00:00
private event EventHandler < ManagerEvent > internalEvent ;
private bool fireAllEvents = false ;
private Thread callerThread ;
/// <summary> Default Fast Reconnect retry counter.</summary>
private int reconnectRetryFast = 5 ;
/// <summary> Default Maximum Reconnect retry counter.</summary>
private int reconnectRetryMax = 10 ;
/// <summary> Default Fast Reconnect interval in milliseconds.</summary>
private int reconnectIntervalFast = 5000 ;
/// <summary> Default Slow Reconnect interval in milliseconds.</summary>
private int reconnectIntervalMax = 10000 ;
2013-01-18 15:55:50 +00:00
2015-04-02 17:23:08 +00:00
public char [ ] VAR_DELIMITER = { '|' } ;
2017-04-05 05:32:55 +00:00
#endregion
2013-01-18 15:55:50 +00:00
2014-01-08 14:16:39 +00:00
/// <summary>
/// Allows you to specifiy how events are fired. If false (default) then
/// events will be fired in order. Otherwise events will be fired as they arrive and
/// control logic in your application will need to handle synchronization.
/// </summary>
2017-04-05 05:32:55 +00:00
public bool UseASyncEvents = false ;
#region Events
/// <summary>
/// An UnhandledEvent is triggered on unknown event.
/// </summary>
public event EventHandler < ManagerEvent > UnhandledEvent ;
/// <summary>
/// An AgentCallbackLogin is triggered when an agent is successfully logged in.
/// </summary>
public event EventHandler < AgentCallbackLoginEvent > AgentCallbackLogin ;
/// <summary>
/// An AgentCallbackLogoff is triggered when an agent that previously logged in is logged of.<br/>
/// </summary>
public event EventHandler < AgentCallbackLogoffEvent > AgentCallbackLogoff ;
/// <summary>
/// An AgentCalled is triggered when an agent is ring.<br/>
/// To enable AgentCalled you have to set eventwhencalled = yes in queues.conf.<br/>
/// </summary>
public event EventHandler < AgentCalledEvent > AgentCalled ;
/// <summary>
/// An AgentCompleteEvent is triggered when at the end of a call if the caller was connected to an agent.
/// </summary>
public event EventHandler < AgentCompleteEvent > AgentComplete ;
/// <summary>
/// An AgentConnectEvent is triggered when a caller is connected to an agent.
/// </summary>
public event EventHandler < AgentConnectEvent > AgentConnect ;
/// <summary>
/// An AgentDumpEvent is triggered when an agent dumps the caller while listening to the queue announcement.
/// </summary>
public event EventHandler < AgentDumpEvent > AgentDump ;
/// <summary>
/// An AgentLoginEvent is triggered when an agent is successfully logged in using AgentLogin.
/// </summary>
public event EventHandler < AgentLoginEvent > AgentLogin ;
/// <summary>
/// An AgentCallbackLogoffEvent is triggered when an agent that previously logged in using AgentLogin is logged of.
/// </summary>
public event EventHandler < AgentLogoffEvent > AgentLogoff ;
/// <summary>
/// An AgentsCompleteEvent is triggered after the state of all agents has been reported in response to an AgentsAction.
/// </summary>
public event EventHandler < AgentsCompleteEvent > AgentsComplete ;
/// <summary>
/// An AgentsEvent is triggered for each agent in response to an AgentsAction.
/// </summary>
public event EventHandler < AgentsEvent > Agents ;
/// <summary>
/// An AlarmEvent is triggered when a Zap channel leaves alarm state.
/// </summary>
public event EventHandler < AlarmClearEvent > AlarmClear ;
/// <summary>
///
/// </summary>
public event EventHandler < BridgeEvent > Bridge ;
/// <summary>
/// An AlarmEvent is triggered when a Zap channel enters or changes alarm state.
/// </summary>
public event EventHandler < AlarmEvent > Alarm ;
/// <summary>
/// A CdrEvent is triggered when a call detail record is generated, usually at the end of a call.
/// </summary>
public event EventHandler < CdrEvent > Cdr ;
public event EventHandler < DBGetResponseEvent > DBGetResponse ;
/// <summary>
/// A Dial is triggered whenever a phone attempts to dial someone.<br/>
/// </summary>
public event EventHandler < DialEvent > Dial ;
public event EventHandler < DTMFEvent > DTMF ;
/// <summary>
/// A DNDStateEvent is triggered by the Zap channel driver when a channel enters or leaves DND (do not disturb) state.
/// </summary>
public event EventHandler < DNDStateEvent > DNDState ;
/// <summary>
/// An ExtensionStatus is triggered when the state of an extension changes.<br/>
/// </summary>
public event EventHandler < ExtensionStatusEvent > ExtensionStatus ;
/// <summary>
/// A Hangup is triggered when a channel is hung up.<br/>
/// </summary>
public event EventHandler < HangupEvent > Hangup ;
/// <summary>
/// A HoldedCall is triggered when a channel is put on hold.<br/>
/// </summary>
public event EventHandler < HoldedCallEvent > HoldedCall ;
/// <summary>
/// A Hold is triggered by the SIP channel driver when a channel is put on hold.<br/>
/// </summary>
public event EventHandler < HoldEvent > Hold ;
/// <summary>
/// A Join is triggered when a channel joines a queue.<br/>
/// </summary>
public event EventHandler < JoinEvent > Join ;
/// <summary>
/// A Leave is triggered when a channel leaves a queue.<br/>
/// </summary>
public event EventHandler < LeaveEvent > Leave ;
/// <summary>
/// A Link is triggered when two voice channels are linked together and voice data exchange commences.<br/>
/// 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.
/// </summary>
public event EventHandler < LinkEvent > Link ;
/// <summary>
/// A LogChannel is triggered when logging is turned on or off.<br/>
/// </summary>
public event EventHandler < LogChannelEvent > LogChannel ;
/// <summary>
/// A MeetMeJoin is triggered if a channel joins a meet me conference.<br/>
/// </summary>
public event EventHandler < MeetmeJoinEvent > MeetMeJoin ;
/// <summary>
/// A MeetMeLeave is triggered if a channel leaves a meet me conference.<br/>
/// </summary>
public event EventHandler < MeetmeLeaveEvent > MeetMeLeave ;
2018-07-05 21:59:19 +00:00
// public event EventHandler<MeetMeStopTalkingEvent> MeetMeStopTalking;
2017-04-05 05:32:55 +00:00
/// <summary>
/// A MeetMeTalkingEvent is triggered when a user starts talking in a meet me conference.<br/>
/// To enable talker detection you must pass the option 'T' to the MeetMe application.
/// </summary>
public event EventHandler < MeetmeTalkingEvent > MeetMeTalking ;
/// <summary>
/// A MessageWaiting is triggered when someone leaves voicemail.<br/>
/// </summary>
public event EventHandler < MessageWaitingEvent > MessageWaiting ;
/// <summary>
/// A NewCallerId is triggered when the caller id of a channel changes.<br/>
/// </summary>
public event EventHandler < NewCallerIdEvent > NewCallerId ;
/// <summary>
/// A NewChannel is triggered when a new channel is created.<br/>
/// </summary>
public event EventHandler < NewChannelEvent > NewChannel ;
/// <summary>
/// A NewExten is triggered when a channel is connected to a new extension.<br/>
/// </summary>
public event EventHandler < NewExtenEvent > NewExten ;
/// <summary>
/// A NewState is triggered when the state of a channel has changed.<br/>
/// </summary>
public event EventHandler < NewStateEvent > NewState ;
2018-07-05 21:59:19 +00:00
// public event EventHandler<OriginateEvent> Originate;
2017-04-05 05:32:55 +00:00
/// <summary>
/// An OriginateFailure is triggered when the execution of an OriginateAction failed.
/// </summary>
2018-07-05 21:59:19 +00:00
// public event EventHandler<OriginateFailureEvent> OriginateFailure;
2017-04-05 05:32:55 +00:00
/// <summary>
/// An OriginateSuccess is triggered when the execution of an OriginateAction succeeded.
/// </summary>
2018-07-05 21:59:19 +00:00
// public event EventHandler<OriginateSuccessEvent> OriginateSuccess;
2017-04-05 05:32:55 +00:00
/// <summary>
/// An OriginateResponse is triggered when the execution of an Originate.
/// </summary>
public event EventHandler < OriginateResponseEvent > OriginateResponse ;
/// <summary>
/// A ParkedCall is triggered when a channel is parked (in this case no
/// action id is set) and in response to a ParkedCallsAction.<br/>
/// </summary>
public event EventHandler < ParkedCallEvent > ParkedCall ;
/// <summary>
/// A ParkedCallGiveUp is triggered when a channel that has been parked is hung up.<br/>
/// </summary>
public event EventHandler < ParkedCallGiveUpEvent > ParkedCallGiveUp ;
/// <summary>
/// A ParkedCallsComplete is triggered after all parked calls have been reported in response to a ParkedCallsAction.
/// </summary>
public event EventHandler < ParkedCallsCompleteEvent > ParkedCallsComplete ;
/// <summary>
/// A ParkedCallTimeOut is triggered when call parking times out for a given channel.<br/>
/// </summary>
public event EventHandler < ParkedCallTimeOutEvent > ParkedCallTimeOut ;
/// <summary>
/// A PeerEntry is triggered in response to a SIPPeersAction or SIPShowPeerAction and contains information about a peer.<br/>
/// </summary>
public event EventHandler < PeerEntryEvent > PeerEntry ;
/// <summary>
/// A PeerlistComplete is triggered after the details of all peers has been reported in response to an SIPPeersAction or SIPShowPeerAction.<br/>
/// </summary>
public event EventHandler < PeerlistCompleteEvent > PeerlistComplete ;
/// <summary>
/// A PeerStatus is triggered when a SIP or IAX client attempts to registrer at this asterisk server.<br/>
/// </summary>
public event EventHandler < PeerStatusEvent > PeerStatus ;
/// <summary>
/// A QueueEntryEvent is triggered in response to a QueueStatusAction and contains information about an entry in a queue.
/// </summary>
public event EventHandler < QueueCallerAbandonEvent > QueueCallerAbandon ;
/// <summary>
/// A QueueEntryEvent is triggered in response to a QueueStatusAction and contains information about an entry in a queue.
/// </summary>
public event EventHandler < QueueEntryEvent > QueueEntry ;
/// <summary>
/// A QueueMemberAddedEvent is triggered when a queue member is added to a queue.
/// </summary>
public event EventHandler < QueueMemberAddedEvent > QueueMemberAdded ;
/// <summary>
/// A QueueMemberEvent is triggered in response to a QueueStatusAction and contains information about a member of a queue.
/// </summary>
public event EventHandler < QueueMemberEvent > QueueMember ;
2016-10-19 07:20:23 +00:00
/// <summary>
/// A QueueMemberPausedEvent is triggered when a queue member is paused or unpaused.
/// <b>Replaced by : </b> <see cref="QueueMemberPauseEvent"/> since <see href="https://wiki.asterisk.org/wiki/display/AST/Asterisk+12+Documentation" target="_blank" alt="Asterisk 12 wiki docs">Asterisk 12</see>.<br/>
/// <b>Removed since : </b> <see href="https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Documentation" target="_blank" alt="Asterisk 13 wiki docs">Asterisk 13</see>.<br/>
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < QueueMemberPausedEvent > QueueMemberPaused ;
/// <summary>
/// A QueueMemberRemovedEvent is triggered when a queue member is removed from a queue.
/// </summary>
public event EventHandler < QueueMemberRemovedEvent > QueueMemberRemoved ;
/// <summary>
/// A QueueMemberStatusEvent shows the status of a QueueMemberEvent.
/// </summary>
public event EventHandler < QueueMemberStatusEvent > QueueMemberStatus ;
/// <summary>
/// A QueueParamsEvent is triggered in response to a QueueStatusAction and contains the parameters of a queue.
/// </summary>
public event EventHandler < QueueParamsEvent > QueueParams ;
/// <summary>
/// A QueueStatusCompleteEvent is triggered after the state of all queues has been reported in response to a QueueStatusAction.
/// </summary>
public event EventHandler < QueueStatusCompleteEvent > QueueStatusComplete ;
/// <summary>
/// A Registry is triggered when this asterisk server attempts to register
/// as a client at another SIP or IAX server.<br/>
/// </summary>
public event EventHandler < RegistryEvent > Registry ;
/// <summary>
/// A RenameEvent is triggered when the name of a channel is changed.
/// </summary>
public event EventHandler < RenameEvent > Rename ;
/// <summary>
/// A StatusCompleteEvent is triggered after the state of all channels has been reported in response to a StatusAction.
/// </summary>
public event EventHandler < StatusCompleteEvent > StatusComplete ;
/// <summary>
/// A StatusEvent is triggered for each active channel in response to a StatusAction.
/// </summary>
public event EventHandler < StatusEvent > Status ;
/// <summary>
///
/// </summary>
public event EventHandler < TransferEvent > Transfer ;
/// <summary>
/// An UnholdEvent is triggered by the SIP channel driver when a channel is no longer put on hold.
/// </summary>
public event EventHandler < UnholdEvent > Unhold ;
/// <summary>
/// An UnlinkEvent is triggered when a link between two voice channels is discontinued, for example, just before call completion.
/// </summary>
public event EventHandler < UnlinkEvent > Unlink ;
/// <summary>
/// A UnparkedCallEvent is triggered when a channel that has been parked is resumed.
/// </summary>
public event EventHandler < UnparkedCallEvent > UnparkedCall ;
/// <summary>
/// A ZapShowChannelsEvent is triggered on UserEvent in dialplan.
/// </summary>
public event EventHandler < UserEvent > UserEvents ;
/// <summary>
/// A ZapShowChannelsCompleteEvent is triggered after the state of all zap channels has been reported in response to a ZapShowChannelsAction.
/// </summary>
public event EventHandler < ZapShowChannelsCompleteEvent > ZapShowChannelsComplete ;
/// <summary>
/// A ZapShowChannelsEvent is triggered in response to a ZapShowChannelsAction and shows the state of a zap channel.
/// </summary>
public event EventHandler < ZapShowChannelsEvent > ZapShowChannels ;
/// <summary>
/// A ConnectionState is triggered after Connect/Disconnect/Reload/Shutdown events.
/// </summary>
public event EventHandler < ConnectionStateEvent > ConnectionState ;
/// <summary>
/// When a variable is set
/// </summary>
public event EventHandler < VarSetEvent > VarSet ;
/// <summary>
/// AgiExec is execute
/// </summary>
public event EventHandler < AGIExecEvent > AGIExec ;
/// <summary>
/// This event is sent when the first user requests a conference and it is instantiated
/// </summary>
public event EventHandler < ConfbridgeStartEvent > ConfbridgeStart ;
/// <summary>
/// This event is sent when a user joins a conference - either one already in progress or as the first user to join a newly instantiated bridge.
/// </summary>
public event EventHandler < ConfbridgeJoinEvent > ConfbridgeJoin ;
/// <summary>
/// This event is sent when a user leaves a conference.
/// </summary>
public event EventHandler < ConfbridgeLeaveEvent > ConfbridgeLeave ;
/// <summary>
/// This event is sent when the last user leaves a conference and it is torn down.
/// </summary>
public event EventHandler < ConfbridgeEndEvent > ConfbridgeEnd ;
/// <summary>
/// This event is sent when the conference detects that a user has either begin or stopped talking.
/// </summary>
public event EventHandler < ConfbridgeTalkingEvent > ConfbridgeTalking ;
2014-01-03 16:39:46 +00:00
2014-04-07 10:35:39 +00:00
/// <summary>
///
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < FailedACLEvent > FailedACL ;
2014-04-07 10:35:39 +00:00
2017-04-05 05:32:55 +00:00
public event EventHandler < AttendedTransferEvent > AttendedTransfer ;
public event EventHandler < BlindTransferEvent > BlindTransfer ;
2015-03-16 20:48:35 +00:00
2017-04-05 05:32:55 +00:00
public event EventHandler < BridgeCreateEvent > BridgeCreate ;
public event EventHandler < BridgeDestroyEvent > BridgeDestroy ;
public event EventHandler < BridgeEnterEvent > BridgeEnter ;
public event EventHandler < BridgeLeaveEvent > BridgeLeave ;
2015-03-16 20:48:35 +00:00
2016-09-20 13:29:46 +00:00
/// <summary>
/// Raised when a dial action has started.<br/>
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < DialBeginEvent > DialBegin ;
2016-09-20 13:29:46 +00:00
2016-09-21 07:04:36 +00:00
/// <summary>
/// Raised when a dial action has completed.<br/>
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < DialEndEvent > DialEnd ;
2016-09-21 07:04:36 +00:00
/// <summary>
/// Raised when a caller joins a Queue.<br/>
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < QueueCallerJoinEvent > QueueCallerJoin ;
2016-09-21 07:04:36 +00:00
/// <summary>
/// Raised when a caller leaves a Queue.<br/>
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < QueueCallerLeaveEvent > QueueCallerLeave ;
2016-09-21 07:04:36 +00:00
2016-10-18 14:07:25 +00:00
/// <summary>
/// A QueueMemberPauseEvent is triggered when a queue member is paused or unpaused.<br />
2016-10-19 07:20:23 +00:00
/// <b>Available since : </b> <see href="https://wiki.asterisk.org/wiki/display/AST/Asterisk+12+Documentation" target="_blank" alt="Asterisk 12 wiki docs">Asterisk 12</see>.
2016-10-18 14:07:25 +00:00
/// </summary>
2017-04-05 05:32:55 +00:00
public event EventHandler < QueueMemberPauseEvent > QueueMemberPause ;
2016-10-18 14:07:25 +00:00
2018-07-05 21:59:19 +00:00
/// <summary>
/// A ChallengeResponseFailed is triggered when a request's attempt to authenticate has been challenged, and the request failed the authentication challenge.
/// </summary>
public event EventHandler < ChallengeResponseFailedEvent > ChallengeResponseFailed ;
/// <summary>
/// A InvalidAccountID is triggered when a request fails an authentication check due to an invalid account ID.
/// </summary>
public event EventHandler < InvalidAccountIDEvent > InvalidAccountID ;
/// <summary>
/// A DeviceStateChanged is triggered when a device state changes.
/// </summary>
public event EventHandler < DeviceStateChangeEvent > DeviceStateChanged ;
/// <summary>
/// A ChallengeSent is triggered when an Asterisk service sends an authentication challenge to a request..
/// </summary>
public event EventHandler < ChallengeSentEvent > ChallengeSent ;
/// <summary>
/// A SuccessfulAuth is triggered when a request successfully authenticates with a service.
/// </summary>
public event EventHandler < SuccessfulAuthEvent > SuccessfulAuth ;
/// <summary>
/// Raised when call queue summary
/// </summary>
public event EventHandler < QueueSummaryEvent > QueueSummary ;
2016-09-20 13:29:46 +00:00
#endregion
2013-01-18 15:55:50 +00:00
2016-09-20 13:29:46 +00:00
#region Constructor - ManagerConnection ( )
/// <summary> Creates a new instance.</summary>
public ManagerConnection ( )
2017-04-05 05:32:55 +00:00
{
callerThread = Thread . CurrentThread ;
socketEncoding = Encoding . ASCII ;
responseHandlers = new Dictionary < int , IResponseHandler > ( ) ;
pingHandlers = new Dictionary < int , IResponseHandler > ( ) ;
responseEventHandlers = new Dictionary < int , IResponseHandler > ( ) ;
registeredEventClasses = new Dictionary < int , ConstructorInfo > ( ) ;
Helper . RegisterBuiltinEventClasses ( registeredEventClasses ) ;
2018-01-16 06:39:11 +00:00
registeredEventHandlers = new Dictionary < int , Func < ManagerEvent , bool > > ( ) ;
2017-04-05 05:32:55 +00:00
#region Event mapping table
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentCallbackLoginEvent ) , arg = > fireEvent ( AgentCallbackLogin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentCallbackLogoffEvent ) , arg = > fireEvent ( AgentCallbackLogoff , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentCalledEvent ) , arg = > fireEvent ( AgentCalled , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentCompleteEvent ) , arg = > fireEvent ( AgentComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentConnectEvent ) , arg = > fireEvent ( AgentConnect , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentDumpEvent ) , arg = > fireEvent ( AgentDump , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentLoginEvent ) , arg = > fireEvent ( AgentLogin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentLogoffEvent ) , arg = > fireEvent ( AgentLogoff , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentsCompleteEvent ) , arg = > fireEvent ( AgentsComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AgentsEvent ) , arg = > fireEvent ( Agents , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AlarmClearEvent ) , arg = > fireEvent ( AlarmClear , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AlarmEvent ) , arg = > fireEvent ( Alarm , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( CdrEvent ) , arg = > fireEvent ( Cdr , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DBGetResponseEvent ) , arg = > fireEvent ( DBGetResponse , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DialEvent ) , arg = > fireEvent ( Dial , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DNDStateEvent ) , arg = > fireEvent ( DNDState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ExtensionStatusEvent ) , arg = > fireEvent ( ExtensionStatus , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( HangupEvent ) , arg = > fireEvent ( Hangup , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( HoldedCallEvent ) , arg = > fireEvent ( HoldedCall , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( HoldEvent ) , arg = > fireEvent ( Hold , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( JoinEvent ) , arg = > fireEvent ( Join , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( LeaveEvent ) , arg = > fireEvent ( Leave , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( LinkEvent ) , arg = > fireEvent ( Link , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( LogChannelEvent ) , arg = > fireEvent ( LogChannel , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( MeetmeJoinEvent ) , arg = > fireEvent ( MeetMeJoin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( MeetmeLeaveEvent ) , arg = > fireEvent ( MeetMeLeave , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( MeetmeTalkingEvent ) , arg = > fireEvent ( MeetMeTalking , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( MessageWaitingEvent ) , arg = > fireEvent ( MessageWaiting , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( NewCallerIdEvent ) , arg = > fireEvent ( NewCallerId , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( NewChannelEvent ) , arg = > fireEvent ( NewChannel , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( NewExtenEvent ) , arg = > fireEvent ( NewExten , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( NewStateEvent ) , arg = > fireEvent ( NewState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( OriginateResponseEvent ) , arg = > fireEvent ( OriginateResponse , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ParkedCallEvent ) , arg = > fireEvent ( ParkedCall , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ParkedCallGiveUpEvent ) , arg = > fireEvent ( ParkedCallGiveUp , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ParkedCallsCompleteEvent ) , arg = > fireEvent ( ParkedCallsComplete , arg ) ) ;
2018-07-05 21:59:19 +00:00
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ParkedCallTimeOutEvent ) , arg = > fireEvent ( ParkedCallTimeOut , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( PeerEntryEvent ) , arg = > fireEvent ( PeerEntry , arg ) ) ;
2017-04-05 05:32:55 +00:00
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( PeerlistCompleteEvent ) , arg = > fireEvent ( PeerlistComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( PeerStatusEvent ) , arg = > fireEvent ( PeerStatus , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueEntryEvent ) , arg = > fireEvent ( QueueEntry , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberAddedEvent ) , arg = > fireEvent ( QueueMemberAdded , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberEvent ) , arg = > fireEvent ( QueueMember , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberPausedEvent ) , arg = > fireEvent ( QueueMemberPaused , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberRemovedEvent ) , arg = > fireEvent ( QueueMemberRemoved , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberStatusEvent ) , arg = > fireEvent ( QueueMemberStatus , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueParamsEvent ) , arg = > fireEvent ( QueueParams , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueStatusCompleteEvent ) , arg = > fireEvent ( QueueStatusComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( RegistryEvent ) , arg = > fireEvent ( Registry , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueCallerAbandonEvent ) , arg = > fireEvent ( QueueCallerAbandon , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( RenameEvent ) , arg = > fireEvent ( Rename , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( StatusCompleteEvent ) , arg = > fireEvent ( StatusComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( StatusEvent ) , arg = > fireEvent ( Status , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( UnholdEvent ) , arg = > fireEvent ( Unhold , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( UnlinkEvent ) , arg = > fireEvent ( Unlink , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( UnparkedCallEvent ) , arg = > fireEvent ( UnparkedCall , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( UserEvent ) , arg = > fireEvent ( UserEvents , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ZapShowChannelsCompleteEvent ) , arg = > fireEvent ( ZapShowChannelsComplete , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ZapShowChannelsEvent ) , arg = > fireEvent ( ZapShowChannels , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConnectEvent ) , arg = > fireEvent ( ConnectionState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DisconnectEvent ) , arg = > fireEvent ( ConnectionState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ReloadEvent ) , arg = > fireEvent ( ConnectionState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ShutdownEvent ) , arg = > fireEvent ( ConnectionState , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BridgeEvent ) , arg = > fireEvent ( Bridge , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( TransferEvent ) , arg = > fireEvent ( Transfer , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DTMFEvent ) , arg = > fireEvent ( DTMF , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( VarSetEvent ) , arg = > fireEvent ( VarSet , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AGIExecEvent ) , arg = > fireEvent ( AGIExec , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConfbridgeStartEvent ) , arg = > fireEvent ( ConfbridgeStart , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConfbridgeJoinEvent ) , arg = > fireEvent ( ConfbridgeJoin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConfbridgeLeaveEvent ) , arg = > fireEvent ( ConfbridgeLeave , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConfbridgeEndEvent ) , arg = > fireEvent ( ConfbridgeEnd , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ConfbridgeTalkingEvent ) , arg = > fireEvent ( ConfbridgeTalking , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( FailedACLEvent ) , arg = > fireEvent ( FailedACL , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( AttendedTransferEvent ) , arg = > fireEvent ( AttendedTransfer , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BridgeCreateEvent ) , arg = > fireEvent ( BridgeCreate , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BridgeDestroyEvent ) , arg = > fireEvent ( BridgeDestroy , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BridgeEnterEvent ) , arg = > fireEvent ( BridgeEnter , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BridgeLeaveEvent ) , arg = > fireEvent ( BridgeLeave , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( BlindTransferEvent ) , arg = > fireEvent ( BlindTransfer , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DialBeginEvent ) , arg = > fireEvent ( DialBegin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DialEndEvent ) , arg = > fireEvent ( DialEnd , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueCallerJoinEvent ) , arg = > fireEvent ( QueueCallerJoin , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueCallerLeaveEvent ) , arg = > fireEvent ( QueueCallerLeave , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueMemberPauseEvent ) , arg = > fireEvent ( QueueMemberPause , arg ) ) ;
2018-07-05 21:59:19 +00:00
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ChallengeResponseFailedEvent ) , arg = > fireEvent ( ChallengeResponseFailed , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( InvalidAccountIDEvent ) , arg = > fireEvent ( InvalidAccountID , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( DeviceStateChangeEvent ) , arg = > fireEvent ( DeviceStateChanged , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( ChallengeSentEvent ) , arg = > fireEvent ( ChallengeSent , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( SuccessfulAuthEvent ) , arg = > fireEvent ( SuccessfulAuth , arg ) ) ;
Helper . RegisterEventHandler ( registeredEventHandlers , typeof ( QueueSummaryEvent ) , arg = > fireEvent ( QueueSummary , arg ) ) ;
2016-09-20 13:29:46 +00:00
#endregion
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
this . internalEvent + = new EventHandler < ManagerEvent > ( internalEventHandler ) ;
}
#endregion
#region Constructor - ManagerConnection ( hostname , port , username , password )
/// <summary>
/// Creates a new instance with the given connection parameters.
/// </summary>
/// <param name="hostname">the hosname of the Asterisk server to connect to.</param>
/// <param name="port">the port where Asterisk listens for incoming Manager API connections, usually 5038.</param>
/// <param name="username">the username to use for login</param>
/// <param name="password">the password to use for login</param>
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 )
/// <summary>
/// Creates a new instance with the given connection parameters.
/// </summary>
/// <param name="hostname">the hosname of the Asterisk server to connect to.</param>
/// <param name="port">the port where Asterisk listens for incoming Manager API connections, usually 5038.</param>
/// <param name="username">the username to use for login</param>
/// <param name="password">the password to use for login</param>
2018-07-05 21:59:19 +00:00
/// <param name="socketEncoding">text encoding to asterisk input/output stream</param>
2017-04-05 05:32:55 +00:00
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
/// <summary>
/// Default Fast Reconnect retry counter.
/// </summary>
public int ReconnectRetryFast
{
get { return reconnectRetryFast ; }
set { reconnectRetryFast = value ; }
}
/// <summary> Default Maximum Reconnect retry counter.</summary>
public int ReconnectRetryMax
{
get { return reconnectRetryMax ; }
set { reconnectRetryMax = value ; }
}
/// <summary> Default Fast Reconnect interval in milliseconds.</summary>
public int ReconnectIntervalFast
{
get { return reconnectIntervalFast ; }
set { reconnectIntervalFast = value ; }
}
/// <summary> Default Slow Reconnect interval in milliseconds.</summary>
public int ReconnectIntervalMax
{
get { return reconnectIntervalMax ; }
set { reconnectIntervalMax = value ; }
}
#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 ) )
{
var currentEvent = registeredEventHandlers [ eventHash ] ;
2018-01-16 06:39:11 +00:00
if ( currentEvent ( e ) )
{
return ;
}
2017-04-05 05:32:55 +00:00
}
if ( fireAllEvents )
2017-04-06 06:25:48 +00:00
{
2017-04-05 05:32:55 +00:00
fireEvent ( UnhandledEvent , e ) ;
2018-01-15 19:03:40 +00:00
}
2017-04-05 05:32:55 +00:00
}
#endregion
#region FireAllEvents
/// <summary>
/// If this property set to <b>true</b> then ManagerConnection send all unassigned events to UnhandledEvent handler,<br/>
/// if set to <b>false</b> then all unassgned events lost and send only UnhandledEvent.<br/>
/// Default: <b>false</b>
/// </summary>
public bool FireAllEvents
{
get { return this . fireAllEvents ; }
set { this . fireAllEvents = value ; }
}
#endregion
#region PingInterval
/// <summary>
/// Timeout from Ping to Pong. If no Pong received send Disconnect event. Set to zero to disable.
/// </summary>
public int PingInterval
{
get { return pingInterval ; }
set { pingInterval = value ; }
}
#endregion
#region Hostname
/// <summary> Sets the hostname of the asterisk server to connect to.<br/>
/// Default is localhost.
/// </summary>
public string Hostname
{
get { return hostname ; }
set { hostname = value ; }
}
#endregion
#region Port
/// <summary>
/// Sets the port to use to connect to the asterisk server. This is the port
/// specified in asterisk's manager.conf file.<br/>
/// Default is 5038.
/// </summary>
public int Port
{
get { return port ; }
set { port = value ; }
}
#endregion
#region UserName
/// <summary>
/// Sets the username to use to connect to the asterisk server. This is the
/// username specified in asterisk's manager.conf file.
/// </summary>
public string Username
{
get { return username ; }
set { username = value ; }
}
#endregion
#region Password
/// <summary>
/// Sets the password to use to connect to the asterisk server. This is the
/// password specified in asterisk's manager.conf file.
/// </summary>
public string Password
{
get { return password ; }
set { password = value ; }
}
#endregion
#region DefaultResponseTimeout
/// <summary> Sets the time in milliseconds the synchronous method
/// will wait for a response before throwing a TimeoutException.<br/>
/// Default is 2000.
/// </summary>
public int DefaultResponseTimeout
{
get { return defaultResponseTimeout ; }
set { defaultResponseTimeout = value ; }
}
#endregion
#region DefaultEventTimeout
/// <summary> Sets the time in milliseconds the synchronous method
/// will wait for a response and the last response event before throwing a TimeoutException.<br/>
/// Default is 5000.
/// </summary>
public int DefaultEventTimeout
{
get { return defaultEventTimeout ; }
set { defaultEventTimeout = value ; }
}
#endregion
#region SleepTime
/// <summary> 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.<br/>
/// The sleepTime attribute is also used when checking for the protocol
/// identifer.<br/>
/// Default is 50.
/// </summary>
/// <deprecated> this has been replaced by an interrupt based response checking approach.</deprecated>
public int SleepTime
{
get { return sleepTime ; }
set { sleepTime = value ; }
}
#endregion
#region KeepAliveAfterAuthenticationFailure
/// <summary> Set to true to try reconnecting to ther asterisk serve
/// even if the reconnection attempt threw an AuthenticationFailedException.<br/>
/// Default is false.
/// </summary>
public bool KeepAliveAfterAuthenticationFailure
{
set { keepAliveAfterAuthenticationFailure = value ; }
get { return keepAliveAfterAuthenticationFailure ; }
}
#endregion
#region KeepAlive
/// <summary>
/// Should we attempt to reconnect when the connection is lost?<br/>
/// This is set to true after successful login and to false after logoff or after an authentication failure when keepAliveAfterAuthenticationFailure is false.
/// </summary>
public bool KeepAlive
{
get { return keepAlive ; }
set { keepAlive = value ; }
}
#endregion
#region SocketEncoding
/// <summary>
/// Socket Encoding - default ASCII
/// </summary>
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 )
/// <summary>
/// Does the real login, following the steps outlined below.<br/>
/// Connects to the asterisk server by calling connect() if not already connected<br/>
/// Waits until the protocol identifier is received. This is checked every sleepTime ms but not longer than timeout ms in total.<br/>
/// Sends a ChallengeAction requesting a challenge for authType MD5.<br/>
/// When the ChallengeResponse is received a LoginAction is sent using the calculated key (MD5 hash of the password appended to the received challenge).<br/>
/// </summary>
/// <param name="timeout">the maximum time to wait for the protocol identifier (in ms)</param>
/// <throws>
/// 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.
/// </throws>
/// <throws>
/// 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.
/// </throws>
private void login ( int timeout )
{
enableEvents = false ;
if ( reconnected )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Login during reconnect state." ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
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 ) ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Unable to create login key using MD5 Message Digest." , ex ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
throw new AuthenticationFailedException ( "Unable to create login key using MD5 Message Digest." , ex ) ;
}
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
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 ) ;
}
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
// successfully logged in so assure that we keep trying to reconnect when disconnected
reconnectEnable = keepAlive ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Successfully logged in" ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
asteriskVersion = determineVersion ( ) ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Determined Asterisk version: " + asteriskVersion ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
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 ( )
{
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 ;
2015-03-31 17:40:55 +00:00
if ( version . StartsWith ( "1.4." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { '|' } ;
2015-03-31 17:40:55 +00:00
return AsteriskVersion . ASTERISK_1_4 ;
}
else if ( version . StartsWith ( "1.6." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { '|' } ;
2015-03-31 17:40:55 +00:00
return Manager . AsteriskVersion . ASTERISK_1_6 ;
}
else if ( version . StartsWith ( "1.8." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { '|' } ;
2015-03-31 17:40:55 +00:00
return Manager . AsteriskVersion . ASTERISK_1_8 ;
}
else if ( version . StartsWith ( "10." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { '|' } ;
2015-03-31 17:40:55 +00:00
return Manager . AsteriskVersion . ASTERISK_10 ;
}
else if ( version . StartsWith ( "11." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { ',' } ;
2015-03-31 17:40:55 +00:00
return Manager . AsteriskVersion . ASTERISK_11 ;
}
else if ( version . StartsWith ( "12." ) )
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { ',' } ;
2015-03-31 17:40:55 +00:00
return Manager . AsteriskVersion . ASTERISK_12 ;
}
2014-10-28 21:22:14 +00:00
else if ( version . StartsWith ( "13." ) )
2015-03-31 17:40:55 +00:00
{
2015-04-02 17:23:08 +00:00
VAR_DELIMITER = new char [ ] { ',' } ;
2014-10-28 21:22:14 +00:00
return Manager . AsteriskVersion . ASTERISK_13 ;
2015-03-31 17:40:55 +00:00
}
else
throw new ManagerException ( "Unknown Asterisk version " + version ) ;
2017-04-05 05:32:55 +00:00
}
}
}
}
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 )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Connecting to {0}:{1}" , hostname , port ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
try
{
mrSocket = new SocketConnection ( hostname , port , socketEncoding ) ;
result = mrSocket . IsConnected ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
catch ( Exception ex )
{
logger . Info ( "Connect - Exception : {0}" , ex . Message ) ;
2013-01-18 15:55:50 +00:00
#else
catch
{
#endif
2017-04-05 05:32:55 +00:00
result = false ;
}
2014-06-12 08:08:52 +00:00
if ( result )
{
if ( mrReader = = null )
{
mrReader = new ManagerReader ( this ) ;
mrReaderThread = new Thread ( mrReader . Run ) { IsBackground = true , Name = "ManagerReader-" + DateTime . Now . Second } ;
mrReader . Socket = mrSocket ;
startReader = true ;
}
else
{
mrReader . Socket = mrSocket ;
}
mrReader . Reinitialize ( ) ;
}
else
{
mrSocket = null ;
}
2017-04-05 05:32:55 +00:00
}
}
2013-01-18 15:55:50 +00:00
2014-06-12 08:08:52 +00:00
if ( startReader )
{
mrReaderThread . Start ( ) ;
}
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
return IsConnected ( ) ;
}
#endregion
#region disconnect ( )
/// <summary> Closes the socket connection.</summary>
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 )
/// <summary>
/// Reconnects to the asterisk server when the connection is lost.<br/>
/// 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.<br/>
/// This method is called when a DisconnectEvent is received from the reader.
/// </summary>
private void reconnect ( bool init )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Warning ( "reconnect (init: {0}), reconnectCount:{1}" , init , reconnectCount ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
if ( init )
reconnectCount = 0 ;
else if ( reconnectCount + + > reconnectRetryMax )
reconnectEnable = false ;
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
if ( reconnectEnable )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Warning ( "Try reconnect." ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
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
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Reconnect delay : {0}, retry : {1}" , reconnectIntervalFast , retryCount ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
Thread . Sleep ( reconnectIntervalFast ) ;
}
else
{
// slow down after unsuccessful attempts assuming a shutdown of the server
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Reconnect delay : {0}, retry : {1}" , reconnectIntervalMax , retryCount ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
Thread . Sleep ( reconnectIntervalMax ) ;
}
}
catch ( ThreadInterruptedException )
{
continue ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
catch ( Exception ex )
{
logger . Info ( "Reconnect delay exception : " , ex . Message ) ;
2013-01-18 15:55:50 +00:00
#else
catch
{
#endif
2017-04-05 05:32:55 +00:00
continue ;
}
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
try
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Try connect." ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
if ( connect ( ) )
break ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
catch ( Exception ex )
{
logger . Info ( "Connect exception : " , ex . Message ) ;
2013-01-18 15:55:50 +00:00
#else
catch
{
#endif
2017-04-05 05:32:55 +00:00
}
retryCount + + ;
}
}
}
if ( ! reconnectEnable )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Can't reconnect." ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
enableEvents = true ;
reconnected = false ;
disconnect ( true ) ;
2018-07-05 21:59:19 +00:00
fireEvent ( new DisconnectEvent ( this ) ) ;
2017-04-05 05:32:55 +00:00
}
}
#endregion
#region createInternalActionId ( )
/// <summary>
/// Creates a new unique internal action id based on the hash code of this connection and a sequence.
/// </summary>
private string createInternalActionId ( )
{
return this . GetHashCode ( ) + "_" + ( this . actionIdCount + + ) ;
}
#endregion
#region Login ( )
/// <summary>
/// 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.
/// </summary>
/// <throws> AuthenticationFailedException if the username and/or password are incorrect</throws>
/// <throws> TimeoutException if no response is received within the specified timeout period</throws>
/// <seealso cref="Action.ChallengeAction"/>
/// <seealso cref="Action.LoginAction"/>
public void Login ( )
{
login ( defaultResponseTimeout ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="timeout">Timeout in milliseconds to login.</param>
public void Login ( int timeout )
{
login ( timeout ) ;
}
#endregion
#region IsConnected ( )
/// <summary> Returns true if there is a socket connection to the
/// asterisk server, false otherwise.
///
/// </summary>
/// <returns> true if there is a socket connection to the
/// asterisk server, false otherwise.
/// </returns>
public bool IsConnected ( )
{
bool result = false ;
lock ( lockSocket )
result = mrSocket ! = null & & mrSocket . IsConnected ;
return result ;
}
#endregion
#region Logoff ( )
/// <summary>
/// Sends a LogoffAction and disconnects from the server.
/// </summary>
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 )
/// <summary>
/// Send Action with default timeout.
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public Response . ManagerResponse SendAction ( Action . ManagerAction action )
{
return SendAction ( action , defaultResponseTimeout ) ;
}
#endregion
#region SendAction ( action , timeout )
/// <summary>
/// Send action ans with timeout (milliseconds)
/// </summary>
/// <param name="action">action to send</param>
/// <param name="timeout">timeout in milliseconds</param>
/// <returns></returns>
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 )
/// <summary>
///
/// </summary>
/// <param name="action"></param>
/// <param name="timeout">wait timeout in milliseconds</param>
/// <returns></returns>
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 ) ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Debug ( "Sent action : '{0}' : {1}" , internalActionId , action ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
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 < string , MethodInfo > 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 < string , string > )
)
)
continue ;
try
{
value = getter . Invoke ( action , new object [ ] { } ) ;
}
catch ( UnauthorizedAccessException ex )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Unable to retrieve property '" + name + "' of " + action . GetType ( ) , ex ) ;
continue ;
2013-01-18 15:55:50 +00:00
#else
throw new ManagerException ( "Unable to retrieve property '" + name + "' of " + action . GetType ( ) , ex ) ;
#endif
2017-04-05 05:32:55 +00:00
}
catch ( TargetInvocationException ex )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Unable to retrieve property '" + name + "' of " + action . GetType ( ) , ex ) ;
continue ;
2013-01-18 15:55:50 +00:00
#else
throw new ManagerException ( "Unable to retrieve property '" + name + "' of " + action . GetType ( ) , ex ) ;
#endif
2017-04-05 05:32:55 +00:00
}
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 ) ) ;
}
2013-01-18 15:55:50 +00:00
2015-04-02 17:23:08 +00:00
IActionVariable actionVar = action as IActionVariable ;
2017-04-05 05:32:55 +00:00
if ( actionVar ! = null )
2015-04-02 17:23:08 +00:00
{
2016-03-18 17:43:20 +00:00
var variables = actionVar . GetVariables ( ) ;
2017-04-05 05:32:55 +00:00
if ( variables ! = null & & variables . Count > 0 )
2016-03-18 17:43:20 +00:00
{
2017-04-05 05:32:55 +00:00
sb . Append ( string . Concat ( "Variable: " , Helper . JoinVariables ( actionVar . GetVariables ( ) , VAR_DELIMITER , "=" ) , Common . LINE_SEPARATOR ) ) ;
2016-03-18 17:43:20 +00:00
}
2015-04-02 17:23:08 +00:00
}
2016-03-18 17:43:20 +00:00
sb . Append ( Common . LINE_SEPARATOR ) ;
2017-04-05 05:32:55 +00:00
return sb . ToString ( ) ;
}
#endregion
#region GetProtocolIdentifier ( )
public string GetProtocolIdentifier ( )
{
return this . protocolIdentifier ;
}
#endregion
#region RegisterUserEventClass ( class )
/// <summary>
/// Register User Event Class
/// </summary>
/// <param name="userEventClass"></param>
public void RegisterUserEventClass ( Type userEventClass )
{
Helper . RegisterEventClass ( registeredEventClasses , userEventClass ) ;
}
#endregion
#region DispatchResponse ( response )
/// <summary>
/// This method is called by the reader whenever a ManagerResponse is
/// received. The response is dispatched to the associated <see cref="IManagerResponseHandler"/>ManagerResponseHandler.
/// </summary>
/// <param name="response">the response received by the reader</param>
/// <seealso cref="ManagerReader" />
internal void DispatchResponse ( Dictionary < string , string > buffer )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Debug ( "Dispatch response packet : {0}" , Helper . JoinVariables ( buffer , ", " , ": " ) ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
DispatchResponse ( buffer , null ) ;
}
2013-01-18 15:55:50 +00:00
2017-04-05 05:32:55 +00:00
internal void DispatchResponse ( ManagerResponse response )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Debug ( "Dispatch response : {0}" , response ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
DispatchResponse ( null , response ) ;
}
internal void DispatchResponse ( Dictionary < string , string > 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 )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Unexpected exception in responseHandler {0}\n{1}" , response , ex ) ;
2013-01-18 15:55:50 +00:00
#else
throw new ManagerException ( "Unexpected exception in responseHandler " + responseHandler . GetType ( ) . FullName , ex ) ;
#endif
2017-04-05 05:32:55 +00:00
}
}
}
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 ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Reconnected - DispatchEvent : " + response ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
#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 ) ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
catch ( Exception ex )
{
logger . Error ( "Unable to create login key using MD5 Message Digest" , ex ) ;
2013-01-18 15:55:50 +00:00
#else
catch
{
#endif
2017-04-05 05:32:55 +00:00
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 ;
2018-07-05 21:59:19 +00:00
fireEvent ( ce ) ;
2017-04-05 05:32:55 +00:00
}
else if ( keepAliveAfterAuthenticationFailure )
reconnect ( true ) ;
else
disconnect ( true ) ;
}
#endregion
}
#endregion
#region DispatchEvent ( . . . )
/// <summary>
/// This method is called by the reader whenever a ManagerEvent is received.
/// The event is dispatched to all registered ManagerEventHandlers.
/// </summary>
/// <param name="e">the event received by the reader</param>
/// <seealso cref="ManagerReader"/>
internal void DispatchEvent ( Dictionary < string , string > buffer )
{
ManagerEvent e = Helper . BuildEvent ( registeredEventClasses , this , buffer ) ;
DispatchEvent ( e ) ;
}
internal void DispatchEvent ( ManagerEvent e )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Debug ( "Dispatching event: {0}" , e ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
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 )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Error ( "Unexpected exception" , ex ) ;
2013-01-18 15:55:50 +00:00
#else
throw ex ;
#endif
2017-04-05 05:32:55 +00:00
}
}
}
#region ConnectEvent
if ( e is ConnectEvent )
{
string protocol = ( ( ConnectEvent ) e ) . ProtocolIdentifier ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Connected via {0}" , protocol ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
if ( ! string . IsNullOrEmpty ( protocol ) & & protocol . StartsWith ( "Asterisk Call Manager" ) )
{
this . protocolIdentifier = protocol ;
}
else
{
this . protocolIdentifier = ( string . IsNullOrEmpty ( protocol ) ? "Empty" : protocol ) ;
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Warning ( "Unsupported protocol version '{0}'. Use at your own risk." , protocol ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
}
if ( reconnected )
{
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
logger . Info ( "Send Challenge action." ) ;
2013-01-18 15:55:50 +00:00
#endif
2017-04-05 05:32:55 +00:00
ChallengeAction challengeAction = new ChallengeAction ( ) ;
try
{
SendAction ( challengeAction , null ) ;
}
2013-01-18 15:55:50 +00:00
#if LOGGER
2017-04-05 05:32:55 +00:00
catch ( Exception ex )
{
logger . Info ( "Send Challenge fail : " , ex . Message ) ;
2013-01-18 15:55:50 +00:00
#else
catch
{
#endif
2017-04-05 05:32:55 +00:00
disconnect ( true ) ;
}
return ;
}
}
#endregion
if ( reconnected & & e is DisconnectEvent )
{
( ( DisconnectEvent ) e ) . Reconnect = true ;
2018-07-05 21:59:19 +00:00
fireEvent ( e ) ;
2017-04-05 05:32:55 +00:00
reconnect ( false ) ;
}
else if ( ! reconnected & & reconnectEnable & & ( e is DisconnectEvent | | e is ReloadEvent | | e is ShutdownEvent ) )
{
( ( ConnectionStateEvent ) e ) . Reconnect = true ;
2018-07-05 21:59:19 +00:00
fireEvent ( e ) ;
2017-04-05 05:32:55 +00:00
reconnect ( true ) ;
}
else
2018-07-05 21:59:19 +00:00
fireEvent ( e ) ;
2017-04-05 05:32:55 +00:00
}
private void eventComplete ( IAsyncResult result )
{
}
2018-07-05 21:59:19 +00:00
private void fireEvent ( ManagerEvent e )
2017-04-05 05:32:55 +00:00
{
if ( enableEvents & & internalEvent ! = null )
if ( UseASyncEvents )
internalEvent . BeginInvoke ( this , e , new AsyncCallback ( eventComplete ) , null ) ;
2014-01-08 14:16:39 +00:00
else
internalEvent . Invoke ( this , e ) ;
2017-04-05 05:32:55 +00:00
}
2017-04-06 06:25:48 +00:00
/// <summary>
/// This method is called when send event to client if subscribed
/// </summary>
/// <typeparam name="T">EventHandler argument</typeparam>
/// <param name="asterEvent">Event delegate</param>
/// <param name="arg">ManagerEvent or inherited class. Argument of eventHandler.</param>
2018-01-16 06:39:11 +00:00
private bool fireEvent < T > ( EventHandler < T > asterEvent , ManagerEvent arg ) where T : ManagerEvent
2017-04-05 05:32:55 +00:00
{
2017-04-06 06:25:48 +00:00
if ( asterEvent ! = null )
2018-01-16 06:39:11 +00:00
{
2017-04-06 06:25:48 +00:00
asterEvent ( this , ( T ) arg ) ;
2018-01-16 06:39:11 +00:00
return true ;
}
return false ;
2017-04-05 05:32:55 +00:00
}
#endregion
}
2018-07-05 21:59:19 +00:00
}