//******************************************************************************************************// ServiceHelper.cs - Gbtc//// Copyright © 2015, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may// not use this file except in compliance with the License. You may obtain a copy of the License at://// http://www.opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 08/29/2006 - Pinal C. Patel// Original version of source code generated.// 11/30/2007 - Pinal C. Patel// Modified the "design time" check in EndInit() method to use LicenseManager. UsageMode property// instead of DesignMode property as the former is more accurate than the latter.// 12/14/2007 - Pinal C. Patel// Made monitoring of service health optional via the MonitorServiceHealth property.// 09/30/2008 - J. Ritchie Carroll// Converted to C#.// 03/06/2009 - Pinal C. Patel// Edited code comments.// 06/19/2009 - Pinal C. Patel// Modified Initialize() method to just load settings from the config file.// 06/30/2009 - Pinal C. Patel// Changed ServiceComponents to be a collection of object instead of ISupportLifecycle so it can// host a wide range of object implementing various interfaces used by the ServiceHelper commands.// 07/15/2009 - Pinal C. Patel// Added AllowedRemoteUsers and ImpersonateRemoteUser properties as part of security.// 07/16/2009 - Pinal C. Patel// Added SupportTelnetSessions property so that telnet support can be optionally turned-on.// 07/17/2009 - Pinal C. Patel// Modified MonitorServiceHealth to default to false as it is not always needed.// 07/30/2009 - Pinal C. Patel// Modified to set the principal of the thread where client request are handled to that of the the // process or remote client (if transmitted by the remote client upon connection).// 08/07/2009 - Pinal C. Patel// Subscribed to ErrorLogger.LoggingException event.// 08/10/2009 - Josh L. Patterson// Edited Comments// 09/08/2009 - Pinal C. Patel// Modified GetProcess(), GetConnectedClient() and GetClientRequestHandler() to use lambda// functions instead of manual iteration of collection items.// Renamed GetProcess(), GetConnectedClient() and GetClientRequestHandler() to FindProcess(), // FindConnectedClient() and FindClientRequestHandler() respectively.// 09/14/2009 - Stephen C. Wills// Added new header and license agreement.// 10/23/2009 - Pinal C. Patel// Modified UpdateStatus() method to allow the type of update to be specified.// 12/18/2009 - Pinal C. Patel// Added message flooding control to UpdateStatus().// 06/16/2010 - Pinal C. Patel// Made changes necessary to implement role-based security.// Added thread synchronization to list-based member variable.// 10/14/2010 - Pinal C. Patel// Updated security implementation to include the entire client request text including command // and arguments instead of just the command.// 12/16/2010 - Pinal C. Patel// Added SupportSystemCommands property that can be used to enable or disable system-level access // via the build-in commands.// 01/04/2011 - J. Ritchie Carroll// Changed default performance sampling interval on services to every 5 seconds instead of every// second to reduce overhead and increase performance. Added a "-lifetime" option to the Health// command to show lifetime performance statistics. Made sampling interval a user config option.// 01/17/2011 - J. Ritchie Carroll// Added default "version" and "time" service commands.// 02/11/2011 - Pinal C. Patel// Updated VerifySecurity() to work correctly with TCP integrated windows authentication. // 03/09/2011 - Pinal C. Patel// Moved UpdateType enumeration to GSF namespace in GSF.Core.dll for broader usage.// 04/14/2011 - Pinal C. Patel// Updated to use new serialization methods in GSF.Serialization class.// 09/22/2011 - J. Ritchie Carroll// Added Mono implementation exception regions.// 02/17/2012 - Stephen C. Wills// Added ReloadCryptoCache service command.// 04/12/2012 - Pinal C. Patel// Added new UnscheduleProcess() and RemoveScheduledProcess methods.// Changed AddProcess(), AddScheduledProcess() and ScheduleProcess() method to return a boolean.// 12/20/2012 - Starlynn Danyelle Gilliam// Modified Header.// 02/09/2016 - Pinal C. Patel// Added new Files and Transfer commands.////****************************************************************************************************** using GSF.Annotations;using GSF.Collections;using GSF.Communication;using GSF.Configuration;using GSF.Console;using GSF.Diagnostics;using GSF.ErrorManagement;using GSF.IO;using GSF.Reflection;using GSF.Scheduling;using GSF.Security;using GSF.Security.Cryptography;using GSF.Threading;using GSF.Units;using System;using System.Collections.Generic;using System.ComponentModel;using System.Configuration;using System.Diagnostics;using System.Drawing;using System.Globalization;using System.IO;using System.Linq;using System.Reflection;using System.Security;using System.Security.Principal;using System.ServiceProcess;using System.Text;using System.Text.RegularExpressions;using System.Threading;using System.Web; // ReSharper disable InconsistentlySynchronizedFieldnamespace GSF.ServiceProcess{ #region [ Enumerations ] /// <summary> /// Indicates the state of a Windows Service. /// </summary> public enum ServiceState { /// <summary> /// Service has started. /// </summary> Started, /// <summary> /// Service has stopped. /// </summary> Stopped, /// <summary> /// Service has paused. /// </summary> Paused, /// <summary> /// Service has resumed. /// </summary> Resumed, /// <summary> /// Service has shutdown. /// </summary> Shutdown } #endregion /// <summary> /// Component that provides added functionality to a Windows Service. /// </summary> /// <seealso cref="ServiceProcess"/> /// <seealso cref="ServiceResponse"/> /// <seealso cref="ClientInfo"/> /// <seealso cref="ClientRequest"/> /// <seealso cref="ClientRequestHandler"/> [ToolboxBitmap(typeof(ServiceHelper))] public class ServiceHelper : Component, ISupportLifecycle, ISupportInitialize, IProvideStatus, IPersistSettings { #region [ Members ] // Nested Types private class StatusUpdate { public StatusUpdate(Guid client, UpdateType type, string message) { Client = client; Type = type; Message = message; } public readonly Guid Client; public readonly UpdateType Type; public readonly string Message; } private class ClientFilter { public ClientFilter() { TypeInclusionFilters = new List<UpdateType>(); TypeExclusionFilters = new List<UpdateType>(); PatternInclusionFilters = new List<string>(); PatternExclusionFilters = new List<string>(); } public bool PassesFilter(StatusUpdate update) { UpdateType updateType = update.Type; string message = update.Message; return TypeInclusionFilters.DefaultIfEmpty(update.Type).Any(type => type == updateType) && TypeExclusionFilters.All(type => type != updateType) && PatternInclusionFilters.DefaultIfEmpty("").Any(pattern => Regex.IsMatch(message, pattern, RegexOptions.IgnoreCase)) && PatternExclusionFilters.All(pattern => !Regex.IsMatch(message, pattern, RegexOptions.IgnoreCase)); } public readonly List<UpdateType> TypeInclusionFilters; public readonly List<UpdateType> TypeExclusionFilters; public readonly List<string> PatternInclusionFilters; public readonly List<string> PatternExclusionFilters; } private class ClientStatusUpdateConfiguration { #region [ Members ] // Constants private const int HighPriority = 2; private const int LowPriority = 1; // Fields private readonly Guid m_clientID; private readonly ClientFilter m_filter; private readonly LogicalThread m_thread; private readonly List<Tuple<DateTime, int>> m_messageCounts; private readonly ServiceHelper m_serviceHelper; #endregion #region [ Constructors ] public ClientStatusUpdateConfiguration(Guid clientID, ServiceHelper serviceHelper) { m_clientID = clientID; m_filter = new ClientFilter(); m_thread = serviceHelper.m_threadScheduler.CreateThread(); m_messageCounts = new List<Tuple<DateTime, int>>(); m_serviceHelper = serviceHelper; } #endregion #region [ Methods ] public void PrioritizeUpdate(StatusUpdate update) { m_thread.Push(HighPriority, () => m_serviceHelper.SendUpdateClientStatusResponse(update.Client, update.Type, update.Message)); } public void SendBroadcastUpdates(List<StatusUpdate> updates) { m_thread.Push(LowPriority, () => { UpdateType type = UpdateType.Information; StringBuilder messageBuilder = new StringBuilder(); List<StatusUpdate> messages = new List<StatusUpdate>(); int messageCount = GetRecentMessageCount(); int suppressedMessageCount = 0; foreach (StatusUpdate update in updates.Where(m_filter.PassesFilter)) { // If the message count exceeds the maximum status update frequency, suppress the message if (messageCount + messages.Count >= m_serviceHelper.m_maxStatusUpdatesFrequency) { suppressedMessageCount++; continue; } // If the string builder is empty, // set the initial state of type if (messageBuilder.Length == 0) type = update.Type; // If the current update's type is not the same as the // current message, start a new message with the new type if (update.Type != type) { // Add the current message to the list of messages messages.Add(new StatusUpdate(m_clientID, type, messageBuilder.ToString())); // If the message count exceeds the maximum status update frequency, begin message suppression if (messageCount + messages.Count >= m_serviceHelper.m_maxStatusUpdatesFrequency) { suppressedMessageCount++; continue; } // Update the message type // and reset the string builder type = update.Type; messageBuilder.Clear(); } // Update the message and increment the counter variable messageBuilder.Append(update.Message); } // If the maximum frequency has not been reached, add the final message to the list of messages; // otherwise, add a message warning the user that messages were suppressed due to high frequency if (messageCount + messages.Count < m_serviceHelper.m_maxStatusUpdatesFrequency) messages.Add(new StatusUpdate(m_clientID, type, messageBuilder.ToString())); else messages.Add(new StatusUpdate(m_clientID, UpdateType.Warning, $"Suppressed {suppressedMessageCount:N0} status updates due to high frequency to avoid flooding.\r\n\r\n")); // Send messages to the client foreach (StatusUpdate message in messages) m_serviceHelper.SendUpdateClientStatusResponse(message.Client, message.Type, message.Message); // Add the number of messages sent to the list of messages for the current time m_messageCounts.Add(Tuple.Create(DateTime.UtcNow, messages.Count)); }); } public void UpdateFilters(ClientFilter mergeFilter, List<int> removalIDs) { m_thread.Push(HighPriority, () => { foreach (int filterID in removalIDs.OrderByDescending(id => id)) RemoveFilter(filterID); m_filter.TypeInclusionFilters.AddRange(mergeFilter.TypeInclusionFilters); m_filter.TypeExclusionFilters.AddRange(mergeFilter.TypeExclusionFilters); m_filter.PatternInclusionFilters.AddRange(mergeFilter.PatternInclusionFilters); m_filter.PatternExclusionFilters.AddRange(mergeFilter.PatternExclusionFilters); }); } public void UpdateFilters(Action<ClientFilter> updateAction) { m_thread.Push(HighPriority, () => updateAction(m_filter)); } public void ListFilters(ClientRequestInfo requestInfo) { m_thread.Push(HighPriority, () => { StringBuilder messageBuilder = new StringBuilder(); StringBuilder commandBuilder = new StringBuilder(); int i = 0; commandBuilder.Append("Filter"); // List type inclusion filters if (m_filter.TypeInclusionFilters.Any()) { messageBuilder.AppendLine("Type Inclusion Filters:"); foreach (UpdateType filter in m_filter.TypeInclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Include Type {Arguments.Escape(filter.ToString())}"); } } // List type exclusion filters if (m_filter.TypeExclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Type Exclusion Filters:"); foreach (UpdateType filter in m_filter.TypeExclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Exclude Type {Arguments.Escape(filter.ToString())}"); } } // List pattern exclusion filters if (m_filter.PatternInclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Pattern Inclusion Filters:"); foreach (string filter in m_filter.PatternInclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Include Regex {Arguments.Escape(filter)}"); } } // List pattern exclusion filters if (m_filter.PatternExclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Pattern Exclusion Filters:"); foreach (string filter in m_filter.PatternExclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Exclude Regex {Arguments.Escape(filter)}"); } } // Display a message if no filters are defined if (messageBuilder.Length == 0) { messageBuilder.AppendLine("No filters defined."); } else { string command = commandBuilder.ToString(); string argument = Arguments.Escape(command); string url = HttpUtility.UrlEncode(command); messageBuilder.AppendLine(); messageBuilder.AppendLine("Command:"); messageBuilder.AppendLine(command); messageBuilder.AppendLine(); messageBuilder.AppendLine("Argument:"); messageBuilder.AppendLine(argument); messageBuilder.AppendLine(); messageBuilder.AppendLine("URL:"); messageBuilder.AppendLine(url); } // Send the message to the client m_serviceHelper.SendActionableResponse(requestInfo, true, null, messageBuilder.ToString().TrimEnd()); }); } private int GetRecentMessageCount() { // Get the total number of messages sent to the client in the last second DateTime lastSecond = DateTime.UtcNow.AddSeconds(-1.0D); int oldestMessageIndex = m_messageCounts.TakeWhile(tuple => tuple.Item1 < lastSecond).Count(); m_messageCounts.RemoveRange(0, oldestMessageIndex); return m_messageCounts.Sum(tuple => tuple.Item2); } private void RemoveFilter(int filterID) { int index = filterID; // Determine whether to remove all filters if (index < 0) { m_filter.TypeInclusionFilters.Clear(); m_filter.TypeExclusionFilters.Clear(); m_filter.PatternInclusionFilters.Clear(); m_filter.PatternExclusionFilters.Clear(); return; } // Attempt to remove a filter from the list of type inclusion filters if (index < m_filter.TypeInclusionFilters.Count) { m_filter.TypeInclusionFilters.RemoveAt(index); return; } index -= m_filter.TypeInclusionFilters.Count; // Attempt to remove a filter from the list of type exclusion filters if (index < m_filter.TypeExclusionFilters.Count) { m_filter.TypeExclusionFilters.RemoveAt(index); return; } index -= m_filter.TypeExclusionFilters.Count; // Attempt to remove a filter from the list of pattern inclusion filters if (index < m_filter.PatternInclusionFilters.Count) { m_filter.PatternInclusionFilters.RemoveAt(index); return; } index -= m_filter.PatternInclusionFilters.Count; // Attempt to remove a filter from the list of pattern exclusion filters if (index < m_filter.PatternExclusionFilters.Count) m_filter.PatternExclusionFilters.RemoveAt(index); } #endregion } // Delegates private delegate bool TryGetClientPrincipalFunctionSignature(Guid clientID, out WindowsPrincipal clientPrincipal); // Constants /// <summary> /// Specifies the default value for the <see cref="LogStatusUpdates"/> property. /// </summary> public const bool DefaultLogStatusUpdates = true; /// <summary> /// Specifies the default value for the <see cref="MaxStatusUpdatesLength"/> property. /// </summary>Lines 1–500 of 4734. Load more
//****************************************************************************************************** // ServiceHelper.cs - Gbtc // // Copyright © 2015, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 08/29/2006 - Pinal C. Patel // Original version of source code generated. // 11/30/2007 - Pinal C. Patel // Modified the "design time" check in EndInit() method to use LicenseManager. UsageMode property // instead of DesignMode property as the former is more accurate than the latter. // 12/14/2007 - Pinal C. Patel // Made monitoring of service health optional via the MonitorServiceHealth property. // 09/30/2008 - J. Ritchie Carroll // Converted to C#. // 03/06/2009 - Pinal C. Patel // Edited code comments. // 06/19/2009 - Pinal C. Patel // Modified Initialize() method to just load settings from the config file. // 06/30/2009 - Pinal C. Patel // Changed ServiceComponents to be a collection of object instead of ISupportLifecycle so it can // host a wide range of object implementing various interfaces used by the ServiceHelper commands. // 07/15/2009 - Pinal C. Patel // Added AllowedRemoteUsers and ImpersonateRemoteUser properties as part of security. // 07/16/2009 - Pinal C. Patel // Added SupportTelnetSessions property so that telnet support can be optionally turned-on. // 07/17/2009 - Pinal C. Patel // Modified MonitorServiceHealth to default to false as it is not always needed. // 07/30/2009 - Pinal C. Patel // Modified to set the principal of the thread where client request are handled to that of the the // process or remote client (if transmitted by the remote client upon connection). // 08/07/2009 - Pinal C. Patel // Subscribed to ErrorLogger.LoggingException event. // 08/10/2009 - Josh L. Patterson // Edited Comments // 09/08/2009 - Pinal C. Patel // Modified GetProcess(), GetConnectedClient() and GetClientRequestHandler() to use lambda // functions instead of manual iteration of collection items. // Renamed GetProcess(), GetConnectedClient() and GetClientRequestHandler() to FindProcess(), // FindConnectedClient() and FindClientRequestHandler() respectively. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 10/23/2009 - Pinal C. Patel // Modified UpdateStatus() method to allow the type of update to be specified. // 12/18/2009 - Pinal C. Patel // Added message flooding control to UpdateStatus(). // 06/16/2010 - Pinal C. Patel // Made changes necessary to implement role-based security. // Added thread synchronization to list-based member variable. // 10/14/2010 - Pinal C. Patel // Updated security implementation to include the entire client request text including command // and arguments instead of just the command. // 12/16/2010 - Pinal C. Patel // Added SupportSystemCommands property that can be used to enable or disable system-level access // via the build-in commands. // 01/04/2011 - J. Ritchie Carroll // Changed default performance sampling interval on services to every 5 seconds instead of every // second to reduce overhead and increase performance. Added a "-lifetime" option to the Health // command to show lifetime performance statistics. Made sampling interval a user config option. // 01/17/2011 - J. Ritchie Carroll // Added default "version" and "time" service commands. // 02/11/2011 - Pinal C. Patel // Updated VerifySecurity() to work correctly with TCP integrated windows authentication. // 03/09/2011 - Pinal C. Patel // Moved UpdateType enumeration to GSF namespace in GSF.Core.dll for broader usage. // 04/14/2011 - Pinal C. Patel // Updated to use new serialization methods in GSF.Serialization class. // 09/22/2011 - J. Ritchie Carroll // Added Mono implementation exception regions. // 02/17/2012 - Stephen C. Wills // Added ReloadCryptoCache service command. // 04/12/2012 - Pinal C. Patel // Added new UnscheduleProcess() and RemoveScheduledProcess methods. // Changed AddProcess(), AddScheduledProcess() and ScheduleProcess() method to return a boolean. // 12/20/2012 - Starlynn Danyelle Gilliam // Modified Header. // 02/09/2016 - Pinal C. Patel // Added new Files and Transfer commands. // //****************************************************************************************************** using GSF.Annotations; using GSF.Collections; using GSF.Communication; using GSF.Configuration; using GSF.Console; using GSF.Diagnostics; using GSF.ErrorManagement; using GSF.IO; using GSF.Reflection; using GSF.Scheduling; using GSF.Security; using GSF.Security.Cryptography; using GSF.Threading; using GSF.Units; using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Security; using System.Security.Principal; using System.ServiceProcess; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Web; // ReSharper disable InconsistentlySynchronizedField namespace GSF.ServiceProcess { #region [ Enumerations ] /// <summary> /// Indicates the state of a Windows Service. /// </summary> public enum ServiceState { /// <summary> /// Service has started. /// </summary> Started, /// <summary> /// Service has stopped. /// </summary> Stopped, /// <summary> /// Service has paused. /// </summary> Paused, /// <summary> /// Service has resumed. /// </summary> Resumed, /// <summary> /// Service has shutdown. /// </summary> Shutdown } #endregion /// <summary> /// Component that provides added functionality to a Windows Service. /// </summary> /// <seealso cref="ServiceProcess"/> /// <seealso cref="ServiceResponse"/> /// <seealso cref="ClientInfo"/> /// <seealso cref="ClientRequest"/> /// <seealso cref="ClientRequestHandler"/> [ToolboxBitmap(typeof(ServiceHelper))] public class ServiceHelper : Component, ISupportLifecycle, ISupportInitialize, IProvideStatus, IPersistSettings { #region [ Members ] // Nested Types private class StatusUpdate { public StatusUpdate(Guid client, UpdateType type, string message) { Client = client; Type = type; Message = message; } public readonly Guid Client; public readonly UpdateType Type; public readonly string Message; } private class ClientFilter { public ClientFilter() { TypeInclusionFilters = new List<UpdateType>(); TypeExclusionFilters = new List<UpdateType>(); PatternInclusionFilters = new List<string>(); PatternExclusionFilters = new List<string>(); } public bool PassesFilter(StatusUpdate update) { UpdateType updateType = update.Type; string message = update.Message; return TypeInclusionFilters.DefaultIfEmpty(update.Type).Any(type => type == updateType) && TypeExclusionFilters.All(type => type != updateType) && PatternInclusionFilters.DefaultIfEmpty("").Any(pattern => Regex.IsMatch(message, pattern, RegexOptions.IgnoreCase)) && PatternExclusionFilters.All(pattern => !Regex.IsMatch(message, pattern, RegexOptions.IgnoreCase)); } public readonly List<UpdateType> TypeInclusionFilters; public readonly List<UpdateType> TypeExclusionFilters; public readonly List<string> PatternInclusionFilters; public readonly List<string> PatternExclusionFilters; } private class ClientStatusUpdateConfiguration { #region [ Members ] // Constants private const int HighPriority = 2; private const int LowPriority = 1; // Fields private readonly Guid m_clientID; private readonly ClientFilter m_filter; private readonly LogicalThread m_thread; private readonly List<Tuple<DateTime, int>> m_messageCounts; private readonly ServiceHelper m_serviceHelper; #endregion #region [ Constructors ] public ClientStatusUpdateConfiguration(Guid clientID, ServiceHelper serviceHelper) { m_clientID = clientID; m_filter = new ClientFilter(); m_thread = serviceHelper.m_threadScheduler.CreateThread(); m_messageCounts = new List<Tuple<DateTime, int>>(); m_serviceHelper = serviceHelper; } #endregion #region [ Methods ] public void PrioritizeUpdate(StatusUpdate update) { m_thread.Push(HighPriority, () => m_serviceHelper.SendUpdateClientStatusResponse(update.Client, update.Type, update.Message)); } public void SendBroadcastUpdates(List<StatusUpdate> updates) { m_thread.Push(LowPriority, () => { UpdateType type = UpdateType.Information; StringBuilder messageBuilder = new StringBuilder(); List<StatusUpdate> messages = new List<StatusUpdate>(); int messageCount = GetRecentMessageCount(); int suppressedMessageCount = 0; foreach (StatusUpdate update in updates.Where(m_filter.PassesFilter)) { // If the message count exceeds the maximum status update frequency, suppress the message if (messageCount + messages.Count >= m_serviceHelper.m_maxStatusUpdatesFrequency) { suppressedMessageCount++; continue; } // If the string builder is empty, // set the initial state of type if (messageBuilder.Length == 0) type = update.Type; // If the current update's type is not the same as the // current message, start a new message with the new type if (update.Type != type) { // Add the current message to the list of messages messages.Add(new StatusUpdate(m_clientID, type, messageBuilder.ToString())); // If the message count exceeds the maximum status update frequency, begin message suppression if (messageCount + messages.Count >= m_serviceHelper.m_maxStatusUpdatesFrequency) { suppressedMessageCount++; continue; } // Update the message type // and reset the string builder type = update.Type; messageBuilder.Clear(); } // Update the message and increment the counter variable messageBuilder.Append(update.Message); } // If the maximum frequency has not been reached, add the final message to the list of messages; // otherwise, add a message warning the user that messages were suppressed due to high frequency if (messageCount + messages.Count < m_serviceHelper.m_maxStatusUpdatesFrequency) messages.Add(new StatusUpdate(m_clientID, type, messageBuilder.ToString())); else messages.Add(new StatusUpdate(m_clientID, UpdateType.Warning, $"Suppressed {suppressedMessageCount:N0} status updates due to high frequency to avoid flooding.\r\n\r\n")); // Send messages to the client foreach (StatusUpdate message in messages) m_serviceHelper.SendUpdateClientStatusResponse(message.Client, message.Type, message.Message); // Add the number of messages sent to the list of messages for the current time m_messageCounts.Add(Tuple.Create(DateTime.UtcNow, messages.Count)); }); } public void UpdateFilters(ClientFilter mergeFilter, List<int> removalIDs) { m_thread.Push(HighPriority, () => { foreach (int filterID in removalIDs.OrderByDescending(id => id)) RemoveFilter(filterID); m_filter.TypeInclusionFilters.AddRange(mergeFilter.TypeInclusionFilters); m_filter.TypeExclusionFilters.AddRange(mergeFilter.TypeExclusionFilters); m_filter.PatternInclusionFilters.AddRange(mergeFilter.PatternInclusionFilters); m_filter.PatternExclusionFilters.AddRange(mergeFilter.PatternExclusionFilters); }); } public void UpdateFilters(Action<ClientFilter> updateAction) { m_thread.Push(HighPriority, () => updateAction(m_filter)); } public void ListFilters(ClientRequestInfo requestInfo) { m_thread.Push(HighPriority, () => { StringBuilder messageBuilder = new StringBuilder(); StringBuilder commandBuilder = new StringBuilder(); int i = 0; commandBuilder.Append("Filter"); // List type inclusion filters if (m_filter.TypeInclusionFilters.Any()) { messageBuilder.AppendLine("Type Inclusion Filters:"); foreach (UpdateType filter in m_filter.TypeInclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Include Type {Arguments.Escape(filter.ToString())}"); } } // List type exclusion filters if (m_filter.TypeExclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Type Exclusion Filters:"); foreach (UpdateType filter in m_filter.TypeExclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Exclude Type {Arguments.Escape(filter.ToString())}"); } } // List pattern exclusion filters if (m_filter.PatternInclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Pattern Inclusion Filters:"); foreach (string filter in m_filter.PatternInclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Include Regex {Arguments.Escape(filter)}"); } } // List pattern exclusion filters if (m_filter.PatternExclusionFilters.Any()) { if (messageBuilder.Length > 0) messageBuilder.AppendLine(); messageBuilder.AppendLine("Pattern Exclusion Filters:"); foreach (string filter in m_filter.PatternExclusionFilters) { messageBuilder.AppendLine($"[{i++}] {filter}"); commandBuilder.Append($" -Exclude Regex {Arguments.Escape(filter)}"); } } // Display a message if no filters are defined if (messageBuilder.Length == 0) { messageBuilder.AppendLine("No filters defined."); } else { string command = commandBuilder.ToString(); string argument = Arguments.Escape(command); string url = HttpUtility.UrlEncode(command); messageBuilder.AppendLine(); messageBuilder.AppendLine("Command:"); messageBuilder.AppendLine(command); messageBuilder.AppendLine(); messageBuilder.AppendLine("Argument:"); messageBuilder.AppendLine(argument); messageBuilder.AppendLine(); messageBuilder.AppendLine("URL:"); messageBuilder.AppendLine(url); } // Send the message to the client m_serviceHelper.SendActionableResponse(requestInfo, true, null, messageBuilder.ToString().TrimEnd()); }); } private int GetRecentMessageCount() { // Get the total number of messages sent to the client in the last second DateTime lastSecond = DateTime.UtcNow.AddSeconds(-1.0D); int oldestMessageIndex = m_messageCounts.TakeWhile(tuple => tuple.Item1 < lastSecond).Count(); m_messageCounts.RemoveRange(0, oldestMessageIndex); return m_messageCounts.Sum(tuple => tuple.Item2); } private void RemoveFilter(int filterID) { int index = filterID; // Determine whether to remove all filters if (index < 0) { m_filter.TypeInclusionFilters.Clear(); m_filter.TypeExclusionFilters.Clear(); m_filter.PatternInclusionFilters.Clear(); m_filter.PatternExclusionFilters.Clear(); return; } // Attempt to remove a filter from the list of type inclusion filters if (index < m_filter.TypeInclusionFilters.Count) { m_filter.TypeInclusionFilters.RemoveAt(index); return; } index -= m_filter.TypeInclusionFilters.Count; // Attempt to remove a filter from the list of type exclusion filters if (index < m_filter.TypeExclusionFilters.Count) { m_filter.TypeExclusionFilters.RemoveAt(index); return; } index -= m_filter.TypeExclusionFilters.Count; // Attempt to remove a filter from the list of pattern inclusion filters if (index < m_filter.PatternInclusionFilters.Count) { m_filter.PatternInclusionFilters.RemoveAt(index); return; } index -= m_filter.PatternInclusionFilters.Count; // Attempt to remove a filter from the list of pattern exclusion filters if (index < m_filter.PatternExclusionFilters.Count) m_filter.PatternExclusionFilters.RemoveAt(index); } #endregion } // Delegates private delegate bool TryGetClientPrincipalFunctionSignature(Guid clientID, out WindowsPrincipal clientPrincipal); // Constants /// <summary> /// Specifies the default value for the <see cref="LogStatusUpdates"/> property. /// </summary> public const bool DefaultLogStatusUpdates = true; /// <summary> /// Specifies the default value for the <see cref="MaxStatusUpdatesLength"/> property. /// </summary>