BaseControlHandler.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  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 NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
  17. protected IServerConfigurationManager Config { get; }
  18. protected ILogger Logger { get; }
  19. protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
  20. {
  21. Config = config;
  22. Logger = logger;
  23. }
  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 result = GetResult(requestInfo.LocalName, requestInfo.Headers);
  59. var settings = new XmlWriterSettings
  60. {
  61. Encoding = Encoding.UTF8,
  62. CloseOutput = false
  63. };
  64. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  65. using (var writer = XmlWriter.Create(builder, settings))
  66. {
  67. writer.WriteStartDocument(true);
  68. writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
  69. writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
  70. writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
  71. writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
  72. foreach (var i in result)
  73. {
  74. writer.WriteStartElement(i.Key);
  75. writer.WriteString(i.Value);
  76. writer.WriteFullEndElement();
  77. }
  78. writer.WriteFullEndElement();
  79. writer.WriteFullEndElement();
  80. writer.WriteFullEndElement();
  81. writer.WriteEndDocument();
  82. }
  83. var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
  84. var controlResponse = new ControlResponse
  85. {
  86. Xml = xml,
  87. IsSuccessful = true
  88. };
  89. controlResponse.Headers.Add("EXT", string.Empty);
  90. return controlResponse;
  91. }
  92. private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
  93. {
  94. await reader.MoveToContentAsync().ConfigureAwait(false);
  95. await reader.ReadAsync().ConfigureAwait(false);
  96. // Loop through each element
  97. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  98. {
  99. if (reader.NodeType == XmlNodeType.Element)
  100. {
  101. switch (reader.LocalName)
  102. {
  103. case "Body":
  104. {
  105. if (!reader.IsEmptyElement)
  106. {
  107. using (var subReader = reader.ReadSubtree())
  108. {
  109. return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
  110. }
  111. }
  112. else
  113. {
  114. await reader.ReadAsync().ConfigureAwait(false);
  115. }
  116. break;
  117. }
  118. default:
  119. {
  120. await reader.SkipAsync().ConfigureAwait(false);
  121. break;
  122. }
  123. }
  124. }
  125. else
  126. {
  127. await reader.ReadAsync().ConfigureAwait(false);
  128. }
  129. }
  130. return new ControlRequestInfo();
  131. }
  132. private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
  133. {
  134. var result = new ControlRequestInfo();
  135. await reader.MoveToContentAsync().ConfigureAwait(false);
  136. await reader.ReadAsync().ConfigureAwait(false);
  137. // Loop through each element
  138. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  139. {
  140. if (reader.NodeType == XmlNodeType.Element)
  141. {
  142. result.LocalName = reader.LocalName;
  143. result.NamespaceURI = reader.NamespaceURI;
  144. if (!reader.IsEmptyElement)
  145. {
  146. using (var subReader = reader.ReadSubtree())
  147. {
  148. await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
  149. return result;
  150. }
  151. }
  152. else
  153. {
  154. await reader.ReadAsync().ConfigureAwait(false);
  155. }
  156. }
  157. else
  158. {
  159. await reader.ReadAsync().ConfigureAwait(false);
  160. }
  161. }
  162. return result;
  163. }
  164. private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
  165. {
  166. await reader.MoveToContentAsync().ConfigureAwait(false);
  167. await reader.ReadAsync().ConfigureAwait(false);
  168. // Loop through each element
  169. while (!reader.EOF && reader.ReadState == ReadState.Interactive)
  170. {
  171. if (reader.NodeType == XmlNodeType.Element)
  172. {
  173. // TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
  174. headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
  175. }
  176. else
  177. {
  178. await reader.ReadAsync().ConfigureAwait(false);
  179. }
  180. }
  181. }
  182. private class ControlRequestInfo
  183. {
  184. public string LocalName { get; set; }
  185. public string NamespaceURI { get; set; }
  186. public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  187. }
  188. protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
  189. private void LogRequest(ControlRequest request)
  190. {
  191. if (!Config.GetDlnaConfiguration().EnableDebugLog)
  192. {
  193. return;
  194. }
  195. Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
  196. }
  197. private void LogResponse(ControlResponse response)
  198. {
  199. if (!Config.GetDlnaConfiguration().EnableDebugLog)
  200. {
  201. return;
  202. }
  203. Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
  204. }
  205. }
  206. }