//// <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));
			}
		}
Example #2
0
		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
		}
Example #3
0
		/// <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);

		}