BaseControlHandler.cs 8.3 KB

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