Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
UPnPAction.cs
1using System;
2using System.IO;
4using System.Net.Http;
5using System.Text;
6using System.Threading.Tasks;
7using System.Xml;
9using Waher.Content;
11
13{
17 public class UPnPAction
18 {
19 private readonly Dictionary<string, UPnPArgument> argumentByName = new Dictionary<string, UPnPArgument>();
20 private readonly ServiceDescriptionDocument parent;
21 private readonly XmlElement xml;
22 private readonly UPnPArgument[] arguments;
23 private readonly string name;
24
25 internal UPnPAction(XmlElement Xml, ServiceDescriptionDocument Parent)
26 {
27 List<UPnPArgument> Arguments = new List<UPnPArgument>();
28
29 this.parent = Parent;
30 this.xml = Xml;
31
32 foreach (XmlNode N in Xml.ChildNodes)
33 {
34 switch (N.LocalName)
35 {
36 case "name":
37 this.name = N.InnerText;
38 break;
39
40 case "argumentList":
41 foreach (XmlNode N2 in N.ChildNodes)
42 {
43 if (N2.LocalName == "argument")
44 {
45 UPnPArgument Argument = new UPnPArgument((XmlElement)N2);
46 Arguments.Add(Argument);
47 this.argumentByName[Argument.Name] = Argument;
48 }
49 }
50 break;
51 }
52 }
53
54 this.arguments = Arguments.ToArray();
55 }
56
60 public XmlElement Xml => this.xml;
61
65 public string Name => this.name;
66
70 public UPnPArgument[] Arguments => this.arguments;
71
76
83 public object Invoke(out Dictionary<string, object> OutputValues, params KeyValuePair<string, object>[] InputValues)
84 {
85 return this.Invoke(out OutputValues, 10000, InputValues);
86 }
87
94 public object Invoke(Dictionary<string, object> InputValues, out Dictionary<string, object> OutputValues)
95 {
96 return this.Invoke(InputValues, out OutputValues, 10000);
97 }
98
106 public object Invoke(out Dictionary<string, object> OutputValues, int Timeout, params KeyValuePair<string, object>[] InputValues)
107 {
108 Dictionary<string, object> InputValues2 = new Dictionary<string, object>();
109
110 foreach (KeyValuePair<string, object> P in InputValues)
111 InputValues2[P.Key] = P.Value;
112
113 return this.Invoke(InputValues2, out OutputValues, Timeout);
114 }
115
123 public object Invoke(Dictionary<string, object> InputValues, out Dictionary<string, object> OutputValues, int Timeout)
124 {
125 KeyValuePair<object, Dictionary<string, object>> Result = this.InvokeAsync(InputValues, Timeout).Result;
126 OutputValues = Result.Value;
127 return Result.Key;
128 }
129
136 public Task<KeyValuePair<object, Dictionary<string, object>>> InvokeAsync(int Timeout, params KeyValuePair<string, object>[] InputValues)
137 {
138 Dictionary<string, object> InputValues2 = new Dictionary<string, object>();
139
140 foreach (KeyValuePair<string, object> P in InputValues)
141 InputValues2[P.Key] = P.Value;
142
143 return this.InvokeAsync(InputValues2, Timeout);
144 }
145
152 public async Task<KeyValuePair<object, Dictionary<string, object>>> InvokeAsync(Dictionary<string, object> InputValues, int Timeout)
153 {
154 Dictionary<string, object> OutputValues;
155 StringBuilder Soap = new StringBuilder();
156 UPnPStateVariable Variable;
157 object Result = null;
158 object First = null;
159
160 Soap.AppendLine("<?xml version=\"1.0\"?>");
161 Soap.AppendLine("<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">");
162 Soap.AppendLine("<s:Body>");
163 Soap.Append("<u:");
164 Soap.Append(this.name);
165 Soap.Append(" xmlns:u=\"");
166 Soap.Append(XmlAttributeEncode(this.parent.Service.ServiceType));
167 Soap.AppendLine("\">");
168
169 foreach (UPnPArgument Argument in this.arguments)
170 {
171 if (Argument.Direction == ArgumentDirection.In)
172 {
173 Soap.Append("<");
174 Soap.Append(Argument.Name);
175 Soap.Append(">");
176
177 if (InputValues.TryGetValue(Argument.Name, out object Value) &&
178 !((Variable = this.parent.GetVariable(Argument.RelatedStateVariable)) is null))
179 {
180 Soap.Append(XmlAttributeEncode(await Variable.ValueToXmlString(Value)));
181 }
182
183 Soap.Append("</");
184 Soap.Append(Argument.Name);
185 Soap.Append(">");
186 }
187 }
188
189 Soap.Append("</u:");
190 Soap.Append(this.name);
191 Soap.AppendLine(">");
192 Soap.AppendLine("</s:Body>");
193 Soap.AppendLine("</s:Envelope>");
194
195 using (HttpClient HttpClient = new HttpClient())
196 {
197 HttpClient.Timeout = TimeSpan.FromMilliseconds(Timeout);
198 HttpClient.DefaultRequestHeaders.ExpectContinue = false;
199
200 HttpContent Body = new StringContent(Soap.ToString(), Encoding.UTF8, "text/xml");
201 Body.Headers.Add("SOAPACTION", "\"" + this.parent.Service.ServiceType + "#" + this.name + "\"");
202
203 HttpResponseMessage Response = await HttpClient.PostAsync(this.parent.Service.ControlURI, Body);
204 Stream Stream = await Response.Content.ReadAsStreamAsync(); // Regardless of status code, we check for XML content.
205 byte[] Bin = await Stream.ReadAllAsync();
206
208 Response.Content.Headers.ContentType?.ToString() ?? XmlCodec.DefaultContentType,
209 Bin, this.parent.Service.ControlURI);
210
211 Decoded.AssertOk();
212
213 XmlDocument ResponseXml = Decoded.Decoded as XmlDocument
214 ?? throw new Exception("Expected XML response to UPnP action request.");
215
216 if (ResponseXml.DocumentElement is null ||
217 ResponseXml.DocumentElement.LocalName != "Envelope" ||
218 ResponseXml.DocumentElement.NamespaceURI != "http://schemas.xmlsoap.org/soap/envelope/")
219 {
220 throw new Exception("Unexpected XML response returned.");
221 }
222
223 XmlElement ResponseBody = GetChildElement(ResponseXml.DocumentElement, "Body", "http://schemas.xmlsoap.org/soap/envelope/")
224 ?? throw new Exception("Response body not found.");
225
226 XmlElement ActionResponse = GetChildElement(ResponseBody, this.name + "Response", this.parent.Service.ServiceType);
227 if (ActionResponse is null)
228 {
229 XmlElement ResponseFault = GetChildElement(ResponseBody, "Fault", "http://schemas.xmlsoap.org/soap/envelope/")
230 ?? throw new Exception("Unable to parse response.");
231
232 string FaultCode = string.Empty;
233 string FaultString = string.Empty;
234 string UPnPErrorCode = string.Empty;
235 string UPnPErrorDescription = string.Empty;
236
237 foreach (XmlNode N in ResponseFault.ChildNodes)
238 {
239 switch (N.LocalName)
240 {
241 case "faultcode":
242 FaultCode = N.InnerText;
243 break;
244
245 case "faultstring":
246 FaultString = N.InnerText;
247 break;
248
249 case "detail":
250 foreach (XmlNode N2 in N.ChildNodes)
251 {
252 switch (N2.LocalName)
253 {
254 case "UPnPError":
255 foreach (XmlNode N3 in N2.ChildNodes)
256 {
257 switch (N3.LocalName)
258 {
259 case "errorCode":
260 UPnPErrorCode = N3.InnerText;
261 break;
262
263 case "errorDescription":
264 UPnPErrorDescription = N3.InnerText;
265 break;
266 }
267 }
268 break;
269 }
270 }
271 break;
272 }
273 }
274
275 throw new UPnPException(FaultCode, FaultString, UPnPErrorCode, UPnPErrorDescription);
276 }
277
278 XmlElement E;
279
280 OutputValues = new Dictionary<string, object>();
281
282 foreach (XmlNode N in ActionResponse.ChildNodes)
283 {
284 E = N as XmlElement;
285 if (E is null)
286 continue;
287
288 if (this.argumentByName.TryGetValue(E.LocalName, out UPnPArgument Argument2))
289 {
290 if (!((Variable = this.parent.GetVariable(Argument2.RelatedStateVariable)) is null))
291 {
292 object Value2 = Variable.XmlStringToValue(E.InnerText);
293 OutputValues[E.LocalName] = Value2;
294
295 if (First is null)
296 First = Value2;
297
298 if (Argument2.ReturnValue && Result is null)
299 Result = Value2;
300 }
301 else
302 {
303 if (First is null)
304 First = E.InnerXml;
305
306 OutputValues[E.LocalName] = E.InnerXml;
307 }
308 }
309 else
310 {
311 if (First is null)
312 First = E.InnerXml;
313
314 OutputValues[E.LocalName] = E.InnerXml;
315 }
316 }
317 }
318
319 if (Result is null)
320 Result = First;
321
322 return new KeyValuePair<object, Dictionary<string, object>>(Result, OutputValues);
323 }
324
325 private static XmlElement GetChildElement(XmlElement E, string LocalName, string Namespace)
326 {
327 XmlElement E2;
328
329 foreach (XmlNode N in E.ChildNodes)
330 {
331 E2 = N as XmlElement;
332 if (E2 is null)
333 continue;
334
335 if (E2.LocalName == LocalName && E2.NamespaceURI == Namespace)
336 return E2;
337 }
338
339 return null;
340 }
341
347 public static string XmlAttributeEncode(string AttributeValue)
348 {
349 if (AttributeValue is null || AttributeValue.IndexOfAny(reservedCharacters) < 0)
350 return AttributeValue;
351
352 return AttributeValue.
353 Replace("&", "&amp;").
354 Replace("<", "&lt;").
355 Replace(">", "&gt;").
356 Replace("\"", "&quot;").
357 Replace("'", "&apos;");
358 }
359
360 private static readonly char[] reservedCharacters = new char[] { '&', '<', '>', '"', '\'' };
361
362 }
363}
Contains information about a response to a content request.
void AssertOk()
Asserts response is OK.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
XML encoder/decoder.
Definition: XmlCodec.cs:19
const string DefaultContentType
Default content type for XML documents.
Definition: XmlCodec.cs:30
Contains the information provided in a Service Description Document, downloaded from a service in the...
Contains information about an action.
Definition: UPnPAction.cs:18
object Invoke(out Dictionary< string, object > OutputValues, int Timeout, params KeyValuePair< string, object >[] InputValues)
Invokes the action.
Definition: UPnPAction.cs:106
Task< KeyValuePair< object, Dictionary< string, object > > > InvokeAsync(int Timeout, params KeyValuePair< string, object >[] InputValues)
Invokes the action.
Definition: UPnPAction.cs:136
object Invoke(Dictionary< string, object > InputValues, out Dictionary< string, object > OutputValues)
Invokes the action.
Definition: UPnPAction.cs:94
object Invoke(Dictionary< string, object > InputValues, out Dictionary< string, object > OutputValues, int Timeout)
Invokes the action.
Definition: UPnPAction.cs:123
UPnPArgument[] Arguments
Service Arguments.
Definition: UPnPAction.cs:70
object Invoke(out Dictionary< string, object > OutputValues, params KeyValuePair< string, object >[] InputValues)
Invokes the action.
Definition: UPnPAction.cs:83
XmlElement Xml
Underlying XML definition.
Definition: UPnPAction.cs:60
static string XmlAttributeEncode(string AttributeValue)
Encodes an XML attribute.
Definition: UPnPAction.cs:347
async Task< KeyValuePair< object, Dictionary< string, object > > > InvokeAsync(Dictionary< string, object > InputValues, int Timeout)
Invokes the action.
Definition: UPnPAction.cs:152
Contains information about an argument.
Definition: UPnPArgument.cs:28
string RelatedStateVariable
Related State Variable
Definition: UPnPArgument.cs:88
ArgumentDirection Direction
Argument Direction
Definition: UPnPArgument.cs:78
Contains information about a state variable.
ArgumentDirection
Direction of action arguments.
Definition: UPnPArgument.cs:12