BaseControlHandler.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #pragma warning disable CS1591
  2. #pragma warning disable SA1600
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using System.Xml;
  9. using Emby.Dlna.Didl;
  10. using MediaBrowser.Controller.Configuration;
  11. using MediaBrowser.Controller.Extensions;
  12. using Microsoft.Extensions.Logging;
  13. namespace Emby.Dlna.Service
  14. {
  15. public abstract class BaseControlHandler
  16. {
  17. private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
  18. protected IServerConfigurationManager Config { get; }
  19. protected ILogger Logger { get; }
  20. protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
  21. {
  22. Config = config;
  23. Logger = logger;
  24. }
  25. public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
  26. {
  27. try
  28. {
  29. LogRequest(request);
  30. var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
  31. LogResponse(response);
  32. return response;
  33. }
  34. catch (Exception ex)
  35. {
  36. Logger.LogError(ex, "Error processing control request");
  37. return ControlErrorHandler.GetResponse(ex);
  38. }
  39. }
  40. private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
  41. {
  42. ControlRequestInfo requestInfo = null;
  43. using (var streamReader = new StreamReader(request.InputXml))
  44. {
  45. var readerSettings = new XmlReaderSettings()
  46. {
  47. ValidationType = ValidationType.None,
  48. CheckCharacters = false,
  49. IgnoreProcessingInstructions = true,
  50. IgnoreComments = true,
  51. Async = true
  52. };
  53. using (var reader = XmlReader.Create(streamReader, readerSettings))
  54. {
  55. requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
  56. }
  57. }
  58. Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
  59. var settings = new XmlWriterSettings
  60. {
  61. Encoding = Encoding.UTF8,
  62. CloseOutput = false,
  63. ConformanceLevel = ConformanceLevel.Fragment
  64. };
  65. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  66. using (var writer = XmlWriter.Create(builder, settings))
  67. {
  68. writer.WriteStartDocument(true);
  69. writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
  70. writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
  71. writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
  72. writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
  73. WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
  74. writer.WriteFullEndElement();
  75. writer.WriteFullEndElement();
  76. writer.WriteFullEndElement();
  77. writer.WriteEndDocument();
  78. }
  79. var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
  80. var controlResponse = new ControlResponse
  81. {
  82. Xml = xml,
  83. IsSuccessful = true
  84. };
  85. controlResponse.Headers.Add("EXT", string.Empty);
  86. return controlResponse;
  87. }
  88. private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
  89. {
  90. await reader.MoveToContentAsync().ConfigureAwait(false);
  91. await reader.ReadAsync().ConfigureAwait(false);
  92. // Loop through each element
  93. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  94. {
  95. if (reader.NodeType == XmlNodeType.Element)
  96. {
  97. switch (reader.LocalName)
  98. {
  99. case "Body":
  100. {
  101. if (!reader.IsEmptyElement)
  102. {
  103. using (var subReader = reader.ReadSubtree())
  104. {
  105. return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
  106. }
  107. }
  108. else
  109. {
  110. await reader.ReadAsync().ConfigureAwait(false);
  111. }
  112. break;
  113. }
  114. default:
  115. {
  116. await reader.SkipAsync().ConfigureAwait(false);
  117. break;
  118. }
  119. }
  120. }
  121. else
  122. {
  123. await reader.ReadAsync().ConfigureAwait(false);
  124. }
  125. }
  126. return new ControlRequestInfo();
  127. }
  128. private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
  129. {
  130. var result = new ControlRequestInfo();
  131. await reader.MoveToContentAsync().ConfigureAwait(false);
  132. await reader.ReadAsync().ConfigureAwait(false);
  133. // Loop through each element
  134. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  135. {
  136. if (reader.NodeType == XmlNodeType.Element)
  137. {
  138. result.LocalName = reader.LocalName;
  139. result.NamespaceURI = reader.NamespaceURI;
  140. if (!reader.IsEmptyElement)
  141. {
  142. using (var subReader = reader.ReadSubtree())
  143. {
  144. await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
  145. return result;
  146. }
  147. }
  148. else
  149. {
  150. await reader.ReadAsync().ConfigureAwait(false);
  151. }
  152. }
  153. else
  154. {
  155. await reader.ReadAsync().ConfigureAwait(false);
  156. }
  157. }
  158. return result;
  159. }
  160. private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
  161. {
  162. await reader.MoveToContentAsync().ConfigureAwait(false);
  163. await reader.ReadAsync().ConfigureAwait(false);
  164. // Loop through each element
  165. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  166. {
  167. if (reader.NodeType == XmlNodeType.Element)
  168. {
  169. // TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
  170. headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
  171. }
  172. else
  173. {
  174. await reader.ReadAsync().ConfigureAwait(false);
  175. }
  176. }
  177. }
  178. private class ControlRequestInfo
  179. {
  180. public string LocalName { get; set; }
  181. public string NamespaceURI { get; set; }
  182. public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  183. }
  184. protected abstract void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter);
  185. private void LogRequest(ControlRequest request)
  186. {
  187. if (!Config.GetDlnaConfiguration().EnableDebugLog)
  188. {
  189. return;
  190. }
  191. Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
  192. }
  193. private void LogResponse(ControlResponse response)
  194. {
  195. if (!Config.GetDlnaConfiguration().EnableDebugLog)
  196. {
  197. return;
  198. }
  199. Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
  200. }
  201. }
  202. }