1 // Copyright 2007 Alp Toker <alp@atoker.com>
2 // This software is made available under the MIT License
3 // See COPYING for details
4 
5 using System;
6 using System.Text;
7 using System.Collections.Generic;
8 
9 namespace NDesk.DBus
10 {
11 	//delegate void MessageHandler (Message msg);
12 
13 	class MatchRule
14 	{
15 		public MessageType? MessageType;
16 		public string Interface;
17 		public string Member;
18 		public ObjectPath Path;
19 		public string Sender;
20 		public string Destination;
21 		public readonly SortedDictionary<int,string> Args = new SortedDictionary<int,string> ();
22 
MatchRule()23 		public MatchRule ()
24 		{
25 		}
26 
Append(StringBuilder sb, string key, string value)27 		void Append (StringBuilder sb, string key, string value)
28 		{
29 			if (sb.Length != 0)
30 				sb.Append (",");
31 
32 			sb.Append (key + "='");
33 			sb.Append (value);
34 			sb.Append ("'");
35 		}
36 
AppendArg(StringBuilder sb, int index, string value)37 		void AppendArg (StringBuilder sb, int index, string value)
38 		{
39 			Append (sb, "arg" + index, value);
40 		}
41 
Equals(object o)42 		public override bool Equals (object o)
43 		{
44 			MatchRule r = o as MatchRule;
45 
46 			if (r == null)
47 				return false;
48 
49 			if (r.MessageType != MessageType)
50 				return false;
51 
52 			if (r.Interface != Interface)
53 				return false;
54 
55 			if (r.Member != Member)
56 				return false;
57 
58 			//TODO: see why path comparison doesn't work
59 			if (r.Path.Value != Path.Value)
60 			//if (r.Path != Path)
61 				return false;
62 
63 			if (r.Sender != Sender)
64 				return false;
65 
66 			if (r.Destination != Destination)
67 				return false;
68 
69 			//FIXME: do args
70 
71 			return true;
72 		}
73 
GetHashCode()74 		public override int GetHashCode ()
75 		{
76 			//FIXME: not at all optimal
77 			return ToString ().GetHashCode ();
78 		}
79 
ToString()80 		public override string ToString ()
81 		{
82 			StringBuilder sb = new StringBuilder ();
83 
84 			if (MessageType != null)
85 				Append (sb, "type", MessageFilter.MessageTypeToString ((MessageType)MessageType));
86 
87 			if (Interface != null)
88 				Append (sb, "interface", Interface);
89 
90 			if (Member != null)
91 				Append (sb, "member", Member);
92 
93 			if (Path != null)
94 				//Append (sb, "path", Path.ToString ());
95 				Append (sb, "path", Path.Value);
96 
97 			if (Sender != null)
98 				Append (sb, "sender", Sender);
99 
100 			if (Destination != null)
101 				Append (sb, "destination", Destination);
102 
103 			if (Args != null) {
104 				foreach (KeyValuePair<int,string> pair in Args)
105 					AppendArg (sb, pair.Key, pair.Value);
106 			}
107 
108 			return sb.ToString ();
109 		}
110 
111 		//this is useful as a Predicate<Message> delegate
Matches(Message msg)112 		public bool Matches (Message msg)
113 		{
114 			if (MessageType != null)
115 				if (msg.Header.MessageType != MessageType)
116 					return false;
117 
118 			object value;
119 
120 			if (Interface != null)
121 				if (msg.Header.Fields.TryGetValue (FieldCode.Interface, out value))
122 					if ((string)value != Interface)
123 						return false;
124 
125 			if (Member != null)
126 				if (msg.Header.Fields.TryGetValue (FieldCode.Member, out value))
127 					if ((string)value != Member)
128 						return false;
129 
130 			if (Path != null)
131 				if (msg.Header.Fields.TryGetValue (FieldCode.Path, out value))
132 					//if ((ObjectPath)value != Path)
133 					if (((ObjectPath)value).Value != Path.Value)
134 						return false;
135 
136 			if (Sender != null)
137 				if (msg.Header.Fields.TryGetValue (FieldCode.Sender, out value))
138 					if ((string)value != Sender)
139 						return false;
140 
141 			if (Destination != null)
142 				if (msg.Header.Fields.TryGetValue (FieldCode.Destination, out value))
143 					if ((string)value != Destination)
144 						return false;
145 
146 			//FIXME: do args
147 
148 			return true;
149 		}
150 
151 		//this could be made more efficient
Parse(string text)152 		public static MatchRule Parse (string text)
153 		{
154 			MatchRule r = new MatchRule ();
155 
156 			foreach (string propStr in text.Split (',')) {
157 				string[] parts = propStr.Split ('=');
158 
159 				if (parts.Length < 2)
160 					throw new Exception ("No equals sign found");
161 				if (parts.Length > 2)
162 					throw new Exception ("Too many equals signs found");
163 
164 				string key = parts[0].Trim ();
165 				string value = parts[1].Trim ();
166 
167 				if (!value.StartsWith ("'") || !value.EndsWith ("'"))
168 					throw new Exception ("Too many equals signs found");
169 
170 				value = value.Substring (1, value.Length - 2);
171 
172 				if (key.StartsWith ("arg")) {
173 					int argnum = Int32.Parse (key.Remove (0, "arg".Length));
174 
175 					if (argnum < 0 || argnum > 63)
176 						throw new Exception ("arg match must be between 0 and 63 inclusive");
177 
178 					if (r.Args.ContainsKey (argnum))
179 						return null;
180 
181 					r.Args[argnum] = value;
182 
183 					continue;
184 				}
185 
186 				//TODO: more consistent error handling
187 				switch (key) {
188 					case "type":
189 						if (r.MessageType != null)
190 							return null;
191 						r.MessageType = MessageFilter.StringToMessageType (value);
192 						break;
193 					case "interface":
194 						if (r.Interface != null)
195 							return null;
196 						r.Interface = value;
197 						break;
198 					case "member":
199 						if (r.Member != null)
200 							return null;
201 						r.Member = value;
202 						break;
203 					case "path":
204 						if (r.Path != null)
205 							return null;
206 						r.Path = new ObjectPath (value);
207 						break;
208 					case "sender":
209 						if (r.Sender != null)
210 							return null;
211 						r.Sender = value;
212 						break;
213 					case "destination":
214 						if (r.Destination != null)
215 							return null;
216 						r.Destination = value;
217 						break;
218 					default:
219 						if (Protocol.Verbose)
220 							Console.Error.WriteLine ("Warning: Unrecognized match rule key: " + key);
221 						break;
222 				}
223 			}
224 
225 			return r;
226 		}
227 	}
228 }
229