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); }