PageRenderTime 35ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Protocol/MatchRule.cs

https://github.com/oskarb/dbus-sharp
C# | 338 lines | 242 code | 57 blank | 39 comment | 43 complexity | 2c2c1f5cba4b7a400fb8f1c778067145 MD5 | raw file
  1. // Copyright 2007 Alp Toker <alp@atoker.com>
  2. // This software is made available under the MIT License
  3. // See COPYING for details
  4. using System;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using System.Collections.Generic;
  8. namespace DBus.Protocol
  9. {
  10. public class MatchRule
  11. {
  12. static readonly Regex argNRegex = new Regex (@"^arg(\d+)(path)?$");
  13. static readonly Regex matchRuleRegex = new Regex (@"(\w+)\s*=\s*'((\\\\|\\'|[^'\\])*)'", RegexOptions.Compiled);
  14. MessageType messageType = MessageType.All;
  15. readonly SortedList<FieldCode,MatchTest> fields = new SortedList<FieldCode,MatchTest> ();
  16. readonly HashSet<ArgMatchTest> args = new HashSet<ArgMatchTest> ();
  17. public MatchRule ()
  18. {
  19. }
  20. public HashSet<ArgMatchTest> Args {
  21. get {
  22. return this.args;
  23. }
  24. }
  25. public SortedList<FieldCode, MatchTest> Fields {
  26. get {
  27. return this.fields;
  28. }
  29. }
  30. public MessageType MessageType {
  31. get {
  32. return this.messageType;
  33. }
  34. set {
  35. this.messageType = value;
  36. }
  37. }
  38. static void Append (StringBuilder sb, string key, object value)
  39. {
  40. Append (sb, key, value.ToString ());
  41. }
  42. static void Append (StringBuilder sb, string key, string value)
  43. {
  44. if (sb.Length != 0)
  45. sb.Append (',');
  46. sb.Append (key);
  47. sb.Append ("='");
  48. sb.Append (value.Replace (@"\", @"\\").Replace (@"'", @"\'"));
  49. sb.Append ('\'');
  50. }
  51. static void AppendArg (StringBuilder sb, int index, string value)
  52. {
  53. Append (sb, "arg" + index, value);
  54. }
  55. static void AppendPathArg (StringBuilder sb, int index, ObjectPath value)
  56. {
  57. Append (sb, "arg" + index + "path", value.ToString ());
  58. }
  59. public override bool Equals (object o)
  60. {
  61. MatchRule r = o as MatchRule;
  62. if (o == null)
  63. return false;
  64. return ToString () == r.ToString ();
  65. }
  66. public override int GetHashCode ()
  67. {
  68. //FIXME: not at all optimal
  69. return ToString ().GetHashCode ();
  70. }
  71. public override string ToString ()
  72. {
  73. StringBuilder sb = new StringBuilder ();
  74. if (MessageType != MessageType.All)
  75. Append (sb, "type", MessageFilter.MessageTypeToString ((MessageType)MessageType));
  76. // Note that fdo D-Bus daemon sorts in a different order.
  77. // It shouldn't matter though as long as we're consistent.
  78. foreach (KeyValuePair<FieldCode,MatchTest> pair in Fields) {
  79. Append (sb, pair.Key.ToString ().ToLower (), pair.Value.Value);
  80. }
  81. // Sorting the list here is not ideal
  82. List<ArgMatchTest> tests = new List<ArgMatchTest> (Args);
  83. tests.Sort ( delegate (ArgMatchTest aa, ArgMatchTest bb) { return aa.ArgNum - bb.ArgNum; } );
  84. if (Args != null)
  85. foreach (ArgMatchTest test in tests)
  86. if (test.Signature == Signature.StringSig)
  87. AppendArg (sb, test.ArgNum, (string)test.Value);
  88. else if (test.Signature == Signature.ObjectPathSig)
  89. AppendPathArg (sb, test.ArgNum, (ObjectPath)test.Value);
  90. return sb.ToString ();
  91. }
  92. public static void Test (HashSet<ArgMatchTest> a, Message msg)
  93. {
  94. List<Signature> sigs = new List<Signature> ();
  95. sigs.AddRange (msg.Signature.GetParts ());
  96. if (sigs.Count == 0) {
  97. a.Clear ();
  98. return;
  99. }
  100. a.RemoveWhere ( delegate (ArgMatchTest t) { return t.ArgNum >= sigs.Count || t.Signature != sigs[t.ArgNum]; } );
  101. // Sorting the list here is not ideal
  102. List<ArgMatchTest> tests = new List<ArgMatchTest> (a);
  103. tests.Sort ( delegate (ArgMatchTest aa, ArgMatchTest bb) { return aa.ArgNum - bb.ArgNum; } );
  104. if (tests.Count == 0) {
  105. a.Clear ();
  106. return;
  107. }
  108. MessageReader reader = new MessageReader (msg);
  109. int argNum = 0;
  110. foreach (ArgMatchTest test in tests) {
  111. if (argNum > test.ArgNum) {
  112. // This test cannot pass because a previous test already did.
  113. // So we already know it will fail without even trying.
  114. // This logic will need to be changed to support wildcards.
  115. a.Remove (test);
  116. continue;
  117. }
  118. while (argNum != test.ArgNum) {
  119. Signature sig = sigs[argNum];
  120. if (!reader.StepOver (sig))
  121. throw new Exception ();
  122. argNum++;
  123. }
  124. // TODO: Avoid re-reading values
  125. if (!reader.PeekValue (test.Signature[0]).Equals (test.Value)) {
  126. a.Remove (test);
  127. continue;
  128. }
  129. argNum++;
  130. }
  131. }
  132. public bool MatchesHeader (Message msg)
  133. {
  134. if (MessageType != MessageType.All)
  135. if (msg.Header.MessageType != MessageType)
  136. return false;
  137. foreach (KeyValuePair<FieldCode,MatchTest> pair in Fields) {
  138. object value;
  139. if (!msg.Header.TryGetField (pair.Key, out value))
  140. return false;
  141. if (!pair.Value.Value.Equals (value))
  142. return false;
  143. }
  144. return true;
  145. }
  146. public static MatchRule Parse (string text)
  147. {
  148. if (text.Length > ProtocolInformation.MaxMatchRuleLength)
  149. throw new Exception ("Match rule length exceeds maximum " + ProtocolInformation.MaxMatchRuleLength + " characters");
  150. MatchRule r = new MatchRule ();
  151. // TODO: Stricter validation. Tighten up the regex.
  152. // It currently succeeds and silently drops malformed test parts.
  153. for (Match m = matchRuleRegex.Match (text) ; m.Success ; m = m.NextMatch ()) {
  154. string key = m.Groups[1].Value;
  155. string value = m.Groups[2].Value;
  156. // This unescaping may not be perfect..
  157. value = value.Replace (@"\\", @"\");
  158. value = value.Replace (@"\'", @"'");
  159. if (key.StartsWith ("arg")) {
  160. Match mArg = argNRegex.Match (key);
  161. if (!mArg.Success)
  162. return null;
  163. int argNum = (int)UInt32.Parse (mArg.Groups[1].Value);
  164. if (argNum < 0 || argNum >= ProtocolInformation.MaxMatchRuleArgs)
  165. throw new Exception ("arg match must be between 0 and " + (ProtocolInformation.MaxMatchRuleArgs - 1) + " inclusive");
  166. //if (r.Args.ContainsKey (argNum))
  167. // return null;
  168. string argType = mArg.Groups[2].Value;
  169. if (argType == "path")
  170. r.Args.Add (new ArgMatchTest (argNum, new ObjectPath (value)));
  171. else
  172. r.Args.Add (new ArgMatchTest (argNum, value));
  173. continue;
  174. }
  175. //TODO: more consistent error handling
  176. switch (key) {
  177. case "type":
  178. if (r.MessageType != MessageType.All)
  179. return null;
  180. r.MessageType = MessageFilter.StringToMessageType (value);
  181. break;
  182. case "interface":
  183. r.Fields[FieldCode.Interface] = new MatchTest (value);
  184. break;
  185. case "member":
  186. r.Fields[FieldCode.Member] = new MatchTest (value);
  187. break;
  188. case "path":
  189. r.Fields[FieldCode.Path] = new MatchTest (new ObjectPath (value));
  190. break;
  191. case "sender":
  192. r.Fields[FieldCode.Sender] = new MatchTest (value);
  193. break;
  194. case "destination":
  195. r.Fields[FieldCode.Destination] = new MatchTest (value);
  196. break;
  197. default:
  198. if (ProtocolInformation.Verbose)
  199. Console.Error.WriteLine ("Warning: Unrecognized match rule key: " + key);
  200. break;
  201. }
  202. }
  203. return r;
  204. }
  205. }
  206. public class HeaderTest : MatchTest
  207. {
  208. public FieldCode Field;
  209. public HeaderTest (FieldCode field, object value)
  210. {
  211. Field = field;
  212. Signature = Signature.GetSig (value.GetType ());
  213. Value = value;
  214. }
  215. }
  216. public struct ArgMatchTest
  217. {
  218. public int ArgNum;
  219. public Signature Signature;
  220. public object Value;
  221. public ArgMatchTest (int argNum, string value)
  222. {
  223. ArgNum = argNum;
  224. Signature = Signature.StringSig;
  225. Value = value;
  226. }
  227. public ArgMatchTest (int argNum, ObjectPath value)
  228. {
  229. ArgNum = argNum;
  230. Signature = Signature.ObjectPathSig;
  231. Value = value;
  232. }
  233. public override int GetHashCode ()
  234. {
  235. return Signature.GetHashCode () ^ Value.GetHashCode () ^ ArgNum;
  236. }
  237. }
  238. /*
  239. class ArgMatchTest : MatchTest
  240. {
  241. public int ArgNum;
  242. public ArgMatchTest (int argNum, string value) : base (value)
  243. {
  244. ArgNum = argNum;
  245. }
  246. public ArgMatchTest (int argNum, ObjectPath value) : base (value)
  247. {
  248. ArgNum = argNum;
  249. }
  250. public override int GetHashCode ()
  251. {
  252. return base.GetHashCode () ^ ArgNum;
  253. }
  254. }
  255. */
  256. public class MatchTest
  257. {
  258. public Signature Signature;
  259. public object Value;
  260. public override int GetHashCode ()
  261. {
  262. return Signature.GetHashCode () ^ Value.GetHashCode ();
  263. }
  264. protected MatchTest ()
  265. {
  266. }
  267. public MatchTest (string value)
  268. {
  269. Signature = Signature.StringSig;
  270. Value = value;
  271. }
  272. public MatchTest (ObjectPath value)
  273. {
  274. Signature = Signature.ObjectPathSig;
  275. Value = value;
  276. }
  277. }
  278. }