using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
namespace Microsoft.Win32.TaskScheduler.Events
{
///
/// Object which encapsulates the filter query used by objects.
///
[XmlRoot("QueryList", IsNullable = false)]
public class EventQuery
{
///
/// Initializes a new instance of the class.
///
public EventQuery() { }
///
/// Initializes a new instance of the class.
///
/// The path.
/// The event ids.
/// The start time.
/// The end time.
public EventQuery(string path, int[] eventIDs = null, DateTime? startTime = null, DateTime? endTime = null)
{
Query.AddPath(path);
if (eventIDs != null)
Query.IDs.AddRange(Array.ConvertAll(eventIDs, i => new CQuery.CID(i)));
if (startTime.HasValue || endTime.HasValue)
Query.Times = new CQuery.CTimeCreated(startTime, endTime);
}
///
/// Initializes a new instance of the class.
///
/// The path.
/// The last span.
/// The event ids.
public EventQuery(string path, TimeSpan lastSpan, int[] eventIDs = null)
{
Query.AddPath(path);
if (eventIDs != null)
Query.IDs.AddRange(Array.ConvertAll(eventIDs, i => new CQuery.CID(i)));
Query.Times = new CQuery.CTimeCreated(lastSpan);
}
///
/// The query sub element
///
[XmlElement]
public CQuery Query = new CQuery();
private static Serializer QLSerializer = new Serializer();
///
/// Deserializes the specified XML.
///
/// The XML.
///
public static EventQuery Deserialize(string xml)
{
return QLSerializer.Deserialize(xml);
}
///
/// Serializes the specified q.
///
/// The q.
///
public static string Serialize(EventQuery q)
{
q.FinalizeObject();
return QLSerializer.Serialize(q);
}
internal void FinalizeObject()
{
this.Query.Suppress.Clear();
if (this.Query.SuppressedIDs.Count > 0)
{
foreach (var i in this.Query.Select)
this.Query.Suppress.Add(new CQuery.CSuppress(this.Query, i.Path));
}
}
///
/// Represents the Query sub object.
///
public class CQuery : IXmlSerializable
{
///
/// The identifier
///
[XmlAttribute]
public int Id = 0;
///
/// The path
///
[XmlAttribute]
public string Path;
///
/// The computers
///
[XmlIgnore]
public List Computers = new List();
///
/// The data items (e.g. TaskName) that are being requested
///
[XmlIgnore]
public Dictionary Data = new Dictionary();
///
/// The ids
///
[XmlIgnore]
public List IDs = new List();
///
/// The is select vs suppress
///
[XmlIgnore]
public bool IsSelect = true;
///
/// The keywords
///
[XmlIgnore]
public long Keywords = 0;
///
/// The levels
///
[XmlIgnore]
public List Levels = new List();
///
/// The providers
///
[XmlIgnore]
public List Providers = new List();
///
/// The select
///
[XmlElement("Select")]
public List Select = new List();
///
/// The suppress
///
[XmlElement("Suppress")]
public List Suppress = new List();
///
/// The suppressed i ds
///
[XmlIgnore]
public List SuppressedIDs = new List();
///
/// The tasks
///
[XmlIgnore]
public List Tasks = new List();
///
/// The times
///
[XmlIgnore]
public CTimeCreated Times;
///
/// The user
///
[XmlIgnore]
public string User = null;
///
/// Gets or sets the identifier string.
///
///
/// The identifier string.
///
[XmlIgnore]
public string IDString
{
get
{
int tc = SuppressedIDs.Count + IDs.Count;
if (tc == 0) return string.Empty;
var output = new string[tc];
for (int i = 0; i < IDs.Count; i++)
output[i] = IDs[i].Text;
for (int i = 0; i < SuppressedIDs.Count; i++)
output[i + IDs.Count] = string.Format("{0}", SuppressedIDs[i].Text);
return string.Join(",", output);
}
set
{
SuppressedIDs.Clear();
IDs.Clear();
value = value.Replace(" ", "");
var IDRegex = new Regex(@"(?(?\d+)-(?\d+))|(?-?\d+)", RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
foreach (Match m in IDRegex.Matches(value))
{
if (m.Groups["n"].Success)
{
var i = int.Parse(m.Groups["n"].Value);
if (i >= 0)
IDs.Add(new CID(i));
else
SuppressedIDs.Add(new CID(-i));
}
else if (m.Groups["r"].Success)
IDs.Add(new CID(int.Parse(m.Groups["r1"].Value), int.Parse(m.Groups["r2"].Value)));
}
}
}
///
/// Adds the path.
///
/// The path.
public void AddPath(string path)
{
this.Path = path;
this.Select.Add(new CSelect(this, path));
}
///
/// Adds the validated computers.
///
/// The computers.
public void AddValidatedComputers(string computers)
{
this.Computers.Clear();
// TODO: Validate items
this.Computers.AddRange(computers.Replace(" ", "").Split(',', ';'));
}
///
/// Adds the validated user.
///
/// The user.
///
public void AddValidatedUser(string user)
{
string sid = NativeMethods.AccountUtils.SidStringFromUserName(user);
if (sid == null)
throw new System.Security.Principal.IdentityNotMappedException();
this.User = sid;
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Query")
{
Id = int.Parse(reader["Id"]);
Path = reader["Path"];
if (reader.ReadToDescendant("Select"))
{
while (reader.MoveToContent() == XmlNodeType.Element && (reader.LocalName == "Select" || reader.LocalName == "Suppress"))
{
if (reader.LocalName == "Select")
{
CSelect sel = new CSelect(this, null);
sel.ReadXml(reader);
this.Select.Add(sel);
}
else
{
CSuppress sup = new CSuppress(this, null);
sup.ReadXml(reader);
this.Suppress.Add(sup);
}
}
}
reader.ReadEndElement();
}
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
// Serialize attributes
writer.WriteAttributeString("Id", Id.ToString());
if (!string.IsNullOrEmpty(Path))
writer.WriteAttributeString("Path", Path);
// Serialize Selects
var selSerializer = new Serializer();
foreach (var s in Select)
selSerializer.Serialize(writer, s);
// Serialize Suppress
var supSerializer = new Serializer();
foreach (var s in Suppress)
supSerializer.Serialize(writer, s);
}
///
/// Represents event IDs
///
public class CID
{
private int low, high;
///
/// Initializes a new instance of the class.
///
/// The identifier.
public CID(int id)
{
low = id; high = -1;
}
///
/// Initializes a new instance of the class.
///
/// The identifier low.
/// The identifier high.
public CID(int idLow, int idHigh)
{
low = idLow; high = idHigh;
}
///
/// Gets the text.
///
///
/// The text.
///
[XmlIgnore]
public string Text
{
get
{
if (high == -1)
return string.Format("{0}", low);
return string.Format("{0}-{1}", low, high);
}
}
///
/// Returns a that represents this instance.
///
///
/// A that represents this instance.
///
public override string ToString()
{
if (high == -1)
return string.Format("EventID={0}", low);
return string.Format("(EventID >= {0} and EventID <= {1})", low, high);
}
}
///
/// Represents the Select element
///
[XmlRoot("Select")]
public class CSelect : IXmlSerializable
{
///
/// The path
///
[XmlAttribute]
public string Path;
///
/// The parent
///
[XmlIgnore]
public CQuery Parent;
///
/// The is select
///
[XmlIgnore]
protected bool IsSelect = true;
// Regex patterns to find different values
private const string computer = @"(?\(\s*(?:Computer\s*=\s*'([^']+)')(?:\s*or\s*(?:Computer\s*=\s*'([^']+)'))*\s*\))";
private const string evid = @"(?:EventID\s*=\s*(?-?\d+))";
private const string evidrange = @"(?EventID\s*>=\s*(\d+)\s*and\s*EventID\s*<=\s*(\d+))";
private const string keyword = @"band\s*\(\s*Keywords\s*,\s*(?\d+)\s*\)";
private const string level = @"(?\(\s*(?:Level\s*=\s*(\d+))(?:\s*or\s*(?:Level\s*=\s*(\d+)))*\s*\))";
private const string prov = @"(?Provider\s*\[\s*(?:@Name\s*=\s*'([^']+)')(?:\s*or\s*(?:@Name\s*=\s*'([^']+)'))*\s*\])";
private const string tasks = @"(?\(\s*(?:Task\s*=\s*(\d+))(?:\s*or\s*(?:Task\s*=\s*(\d+)))*\s*\))";
private const string time2dates = @"(?TimeCreated\s*\[\s*(?:\s*@SystemTime\s*([<>]=)\s*'([^']+)')\s*and\s*@SystemTime\s*([<>]=)\s*'([^']+)'\s*\])";
private const string timedate2end = @"TimeCreated\s*\[\s*(?:\s*@SystemTime\s*>=\s*'(?[^']+)')\s*\]";
private const string timediff = @"TimeCreated\s*\[\s*(?:timediff\s*\(\s*@SystemTime\s*\)\s*<=\s*(?\d+))\s*\]";
private const string timestart2date = @"TimeCreated\s*\[\s*(?:\s*@SystemTime\s*<=\s*'(?[^']+)')\s*\]";
private const string user = @"Security\s*\[\s*(?:@UserID\s*=\s*'(?[^']+)')\s*\]";
private const string OR = " or ";
private const string AND = " and ";
private const string eventData = @"\s*Data\s*\[\s*@Name\s*=\s*'(?[^']*)'\s*\]\s*=\s*'(?[^']*)'\s*(?:and\s*)?";
///
/// Initializes a new instance of the class.
///
/// The parent.
/// The path.
public CSelect(CQuery parent, string path)
{
Parent = parent;
Path = path;
}
///
/// Initializes a new instance of the class.
///
protected CSelect() { }
private void CheckSelectValue(string value)
{
string[] inner = InnerValue(value);
if (inner[0] == null && inner[1] == null) return;
string newVal = Regex.Replace(inner[0], string.Join("|", new string[] { computer, evidrange, evid, keyword, level, prov, tasks, user, time2dates, timedate2end, timestart2date, timediff, AND, @"\s*\(\s*\)\s*", @"\s+" }), string.Empty, RegexOptions.IgnoreCase);
newVal = Regex.Replace(newVal, @"\s*\(\s*(?:or\s*)*\)\s*", string.Empty, RegexOptions.IgnoreCase);
ThrowIfBadXml(value, newVal, "Select");
newVal = Regex.Replace(inner[1], eventData, string.Empty, RegexOptions.IgnoreCase);
ThrowIfBadXml(value, newVal, "Select");
}
private void CheckSuppressValue(string value)
{
string[] inner = InnerValue(value);
if (inner[0] == null) return;
string newVal = Regex.Replace(inner[0], evid, string.Empty, RegexOptions.IgnoreCase);
ThrowIfBadXml(value, newVal, "Suppress");
}
private string[] InnerValue(string value)
{
string[] ret = new string[] { null, null };
var r = new Regex(@"^\s*\*\s*(?:\[\s*System\s*\[(?.*)\]\s*\]\s*)?(?:and\s*)?(?:\*\s*\[\s*EventData\s*\[(?.*)\]\s*\]\s*)?$", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.RightToLeft);
Match m = r.Match(value);
if (m.Success)
{
if (m.Groups["sys"].Success)
ret[0] = m.Groups["sys"].Value;
if (m.Groups["data"].Success)
ret[1] = m.Groups["data"].Value;
}
return ret;
}
private void ThrowIfBadXml(string value, string xtraXml, string parentNode)
{
if (Regex.Replace(xtraXml, @"\s+", string.Empty).Length > 0)
{
var exc = new InvalidOperationException(string.Format("Invalid value for {0} node.", parentNode));
exc.Data.Add("Remaining text", xtraXml);
var idx = value.IndexOf(xtraXml);
if (idx > -1)
exc.Data.Add("Value index", idx);
throw exc;
}
}
///
/// Gets or sets the value.
///
///
/// The value.
///
[XmlText]
public virtual string Value
{
get
{
var sb = new System.Text.StringBuilder();
sb.Append("*");
if (IsSelect)
{
if (this.Parent.Providers.Count > 0)
sb.AppendFormat("Provider[{0}]", string.Join(OR, this.Parent.Providers.ConvertAll(s => string.Format("@Name='{0}'", s)).ToArray()));
if (this.Parent.Computers.Count > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, this.Parent.Computers.ConvertAll(i => string.Format("Computer='{0}'", i)).ToArray()));
}
if (this.Parent.Levels.Count > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, this.Parent.Levels.ConvertAll(i => string.Format("Level={0}", i)).ToArray()));
}
if (this.Parent.Tasks.Count > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, this.Parent.Tasks.ConvertAll(i => string.Format("Task={0}", i)).ToArray()));
}
if (this.Parent.Keywords != 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("(band(Keywords,{0}))", this.Parent.Keywords);
}
if (this.Parent.IDs.Count > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, this.Parent.IDs.ConvertAll(i => i.ToString()).ToArray()));
}
if (!string.IsNullOrEmpty(this.Parent.User))
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("Security[@UserID='{0}']", this.Parent.User);
}
if (this.Parent.Times != null && ((this.Parent.Times.span.HasValue && this.Parent.Times.span.Value != TimeSpan.Zero) || this.Parent.Times.HasDates))
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("TimeCreated[{0}]", this.Parent.Times);
}
}
else
{
if (this.Parent.SuppressedIDs.Count > 0)
{
if (sb.Length > 1) sb.Append(AND);
sb.AppendFormat("({0})", string.Join(OR, this.Parent.SuppressedIDs.ConvertAll(i => i.ToString()).ToArray()));
}
}
if (sb.Length > 1)
{
sb.Insert(1, "[System[");
sb.Append("]]");
}
if (IsSelect && this.Parent.Data.Count > 0)
{
if (sb.Length > 1)
sb.Append(AND + "*");
sb.Append("[EventData[");
var dataItems = new List();
foreach (var kv in this.Parent.Data)
dataItems.Add(string.Format("Data[@Name='{0}']='{1}'", kv.Key, kv.Value));
sb.Append(string.Join(AND, dataItems.ToArray()));
sb.Append("]]");
}
return sb.ToString();
}
set
{
if (IsSelect)
{
CheckSelectValue(value);
// Providers
this.Parent.Providers.AddRange(GetParsedValue(prov, value).ConvertAll(o => (string)o));
// Levels
this.Parent.Levels.AddRange(GetParsedValue(level, value).ConvertAll(o => (int)o));
// IDs
this.Parent.IDs.AddRange(GetParsedValue(evid, value).ConvertAll(o => new CID((int)o)));
var idr = GetParsedValue(evidrange, value);
for (int i = 0; i < idr.Count; i += 2)
this.Parent.IDs.Add(new CID((int)idr[i], (int)idr[i + 1]));
// Tasks
this.Parent.Tasks.AddRange(GetParsedValue(tasks, value).ConvertAll(o => (int)o));
// Keywords
var k = GetParsedValue(keyword, value);
if (k.Count > 0) this.Parent.Keywords = (long)k[0];
// Users
var u = GetParsedValue(user, value);
if (u.Count > 0) this.Parent.User = NativeMethods.AccountUtils.UserNameFromSidString((string)u[0]);
// Computers
this.Parent.Computers.AddRange(GetParsedValue(computer, value).ConvertAll(o => (string)o));
// Times
var tc = GetParsedValue(timediff, value);
if (tc.Count > 0)
this.Parent.Times = new CTimeCreated(TimeSpan.FromMilliseconds((long)tc[0]));
else
{
DateTime? s = null, e = null;
tc = GetParsedValue(time2dates, value);
if (tc.Count > 1)
{
s = (DateTime)tc[0];
e = (DateTime)tc[1];
}
else
{
tc = GetParsedValue(timestart2date, value);
if (tc.Count > 0)
e = (DateTime)tc[0];
else
{
tc = GetParsedValue(timedate2end, value);
if (tc.Count > 0)
s = (DateTime)tc[0];
}
}
if (s.HasValue || e.HasValue)
this.Parent.Times = new CTimeCreated(s, e);
}
// Data value
var regex = new Regex(eventData, RegexOptions.IgnoreCase | RegexOptions.Compiled);
foreach (Match m in regex.Matches(value))
{
if (m.Groups["key"].Success && m.Groups["val"].Success)
this.Parent.Data.Add(m.Groups["key"].Value, m.Groups["val"].Value);
}
}
else
{
CheckSuppressValue(value);
// SuppressedIDs
this.Parent.SuppressedIDs.AddRange(GetParsedValue(evid, value).ConvertAll(o => new CID(-(int)o)));
}
}
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { return null; }
///
/// Generates an object from its XML representation.
///
/// The stream from which the object is deserialized.
public void ReadXml(XmlReader reader)
{
if (reader.MoveToContent() == XmlNodeType.Element)
{
if (reader.LocalName == "Suppress")
this.IsSelect = false;
this.Path = reader["Path"];
this.Value = reader.ReadString();
reader.ReadEndElement();
}
}
///
/// Converts an object into its XML representation.
///
/// The stream to which the object is serialized.
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Path", this.Path);
writer.WriteString(this.Value);
}
private List