//// <summary> /// 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. /// </summary> /// <seealso cref="ManagerConnection.DispatchEvent(ManagerEvent)" /> /// <seealso cref="ManagerConnection.DispatchResponse(Response.ManagerResponse)" /> /// <seealso cref="ManagerConnection.setProtocolIdentifier(String)" /> 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 (lastPacketTime.AddMilliseconds(mrConnector.PingInterval) < DateTime.Now && mrConnector.PingInterval > 0 && mrSocket != null && !wait4identiier && !is_logoff ) { if (pingHandler != null) { if (pingHandler.Response == null) { // If one PingInterval from Ping without Pong then send Disconnect event mrConnector.RemoveResponseHandler(pingHandler); mrConnector.DispatchEvent(new DisconnectEvent(mrConnector)); } pingHandler.Free(); pingHandler = null; } else { // Send PING to * try { pingHandler = new ResponseHandler(new PingAction(), null); mrConnector.SendAction(pingHandler.Action, pingHandler); } catch { disconnect = true; mrSocket = null; } } lastPacketTime = DateTime.Now; } Thread.Sleep(50); 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--") { var commandResponse = new CommandResponse(); Helper.SetAttributes(commandResponse, packet); commandResponse.Result = commandList; processingCommandResult = false; packet.Clear(); mrConnector.DispatchResponse(commandResponse); } 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; var 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)); } }
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) { #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 } } } 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 }
/// <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) { #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); }