jellyfin-server/Emby.Dlna/Service/BaseControlHandler.cs

252 lines
8.7 KiB
C#
Raw Normal View History

#pragma warning disable CS1591
using System;
2016-10-29 22:22:20 +00:00
using System.Collections.Generic;
2016-11-04 08:31:05 +00:00
using System.IO;
2016-10-29 22:22:20 +00:00
using System.Text;
2020-01-21 16:59:41 +00:00
using System.Threading.Tasks;
2016-10-29 22:22:20 +00:00
using System.Xml;
2016-11-04 08:31:05 +00:00
using Emby.Dlna.Didl;
2019-01-13 19:16:19 +00:00
using MediaBrowser.Controller.Configuration;
2016-12-04 21:30:38 +00:00
using MediaBrowser.Controller.Extensions;
2019-01-13 19:16:19 +00:00
using Microsoft.Extensions.Logging;
2016-10-29 22:22:20 +00:00
2016-10-29 22:34:54 +00:00
namespace Emby.Dlna.Service
2016-10-29 22:22:20 +00:00
{
public abstract class BaseControlHandler
{
2020-08-20 15:59:27 +00:00
private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
2016-10-29 22:22:20 +00:00
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
2016-10-29 22:22:20 +00:00
{
Config = config;
2020-01-21 16:59:41 +00:00
Logger = logger;
2016-10-29 22:22:20 +00:00
}
2020-08-20 15:01:04 +00:00
protected IServerConfigurationManager Config { get; }
protected ILogger Logger { get; }
2020-01-21 16:59:41 +00:00
public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
2016-10-29 22:22:20 +00:00
{
try
{
2020-01-21 16:59:41 +00:00
LogRequest(request);
2016-10-29 22:22:20 +00:00
2020-01-21 16:59:41 +00:00
var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
LogResponse(response);
2016-10-29 22:22:20 +00:00
return response;
}
catch (Exception ex)
{
2020-01-21 16:59:41 +00:00
Logger.LogError(ex, "Error processing control request");
2016-10-29 22:22:20 +00:00
2020-01-21 16:59:41 +00:00
return ControlErrorHandler.GetResponse(ex);
2016-10-29 22:22:20 +00:00
}
}
2020-01-21 16:59:41 +00:00
private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
2016-10-29 22:22:20 +00:00
{
2016-11-04 08:31:05 +00:00
ControlRequestInfo requestInfo = null;
2016-10-29 22:22:20 +00:00
2020-12-30 17:31:26 +00:00
using (var streamReader = new StreamReader(request.InputXml, Encoding.UTF8))
2016-10-29 22:22:20 +00:00
{
var readerSettings = new XmlReaderSettings()
{
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
Async = true
};
2016-11-04 08:31:05 +00:00
2020-09-13 13:36:10 +00:00
using var reader = XmlReader.Create(streamReader, readerSettings);
requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
2016-10-29 22:22:20 +00:00
}
2020-01-21 16:59:41 +00:00
Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
2016-10-29 22:22:20 +00:00
2016-11-04 08:31:05 +00:00
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
2020-01-28 16:50:25 +00:00
CloseOutput = false
2016-11-04 08:31:05 +00:00
};
2016-10-29 22:22:20 +00:00
2016-11-04 08:31:05 +00:00
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
2016-10-29 22:22:20 +00:00
2019-01-13 20:37:13 +00:00
using (var writer = XmlWriter.Create(builder, settings))
2016-10-29 22:22:20 +00:00
{
2016-11-04 08:31:05 +00:00
writer.WriteStartDocument(true);
2020-08-20 15:59:27 +00:00
writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
2016-11-04 08:31:05 +00:00
2020-08-20 15:59:27 +00:00
writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
2016-11-04 08:31:05 +00:00
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
2016-11-06 17:30:44 +00:00
writer.WriteFullEndElement();
writer.WriteFullEndElement();
2016-11-04 08:31:05 +00:00
2016-11-06 17:30:44 +00:00
writer.WriteFullEndElement();
2016-11-04 08:31:05 +00:00
writer.WriteEndDocument();
2016-10-29 22:22:20 +00:00
}
2020-01-27 22:34:40 +00:00
var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
2016-12-03 23:57:34 +00:00
2016-10-29 22:22:20 +00:00
var controlResponse = new ControlResponse
{
Xml = xml,
IsSuccessful = true
};
controlResponse.Headers.Add("EXT", string.Empty);
return controlResponse;
}
2020-01-21 16:59:41 +00:00
private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
2016-11-04 08:31:05 +00:00
{
2020-01-21 16:59:41 +00:00
await reader.MoveToContentAsync().ConfigureAwait(false);
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
// Loop through each element
2016-12-03 21:46:06 +00:00
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
2016-11-04 08:31:05 +00:00
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.LocalName)
{
case "Body":
{
2016-12-03 23:57:34 +00:00
if (!reader.IsEmptyElement)
{
2020-09-13 13:36:10 +00:00
using var subReader = reader.ReadSubtree();
return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
2016-12-03 23:57:34 +00:00
}
else
{
2020-01-21 16:59:41 +00:00
await reader.ReadAsync().ConfigureAwait(false);
2016-12-03 23:57:34 +00:00
}
2020-01-21 16:59:41 +00:00
2016-12-03 23:57:34 +00:00
break;
2016-11-04 08:31:05 +00:00
}
2020-06-15 21:43:52 +00:00
2016-11-04 08:31:05 +00:00
default:
{
2020-01-21 16:59:41 +00:00
await reader.SkipAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
break;
}
}
}
else
{
2020-01-21 16:59:41 +00:00
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
}
}
2020-09-25 17:40:10 +00:00
throw new EndOfStreamException("Stream ended but no body tag found.");
2016-11-04 08:31:05 +00:00
}
2020-01-21 16:59:41 +00:00
private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
2016-11-04 08:31:05 +00:00
{
2020-09-25 18:44:16 +00:00
string namespaceURI = null, localName = null;
2016-11-04 08:31:05 +00:00
2020-01-21 16:59:41 +00:00
await reader.MoveToContentAsync().ConfigureAwait(false);
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
// Loop through each element
2016-12-03 21:46:06 +00:00
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
2016-11-04 08:31:05 +00:00
{
if (reader.NodeType == XmlNodeType.Element)
{
2020-09-25 18:44:16 +00:00
localName = reader.LocalName;
namespaceURI = reader.NamespaceURI;
2016-11-04 08:31:05 +00:00
2016-12-03 23:57:34 +00:00
if (!reader.IsEmptyElement)
2016-11-04 08:31:05 +00:00
{
2020-09-25 18:44:16 +00:00
var result = new ControlRequestInfo(localName, namespaceURI);
2020-09-13 13:36:10 +00:00
using var subReader = reader.ReadSubtree();
await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
2020-11-07 18:59:32 +00:00
return result;
2016-12-03 23:57:34 +00:00
}
else
{
2020-01-21 16:59:41 +00:00
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
}
}
else
{
2020-01-21 16:59:41 +00:00
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
}
}
2020-09-25 18:51:37 +00:00
if (localName != null && namespaceURI != null)
2020-09-25 18:44:16 +00:00
{
return new ControlRequestInfo(localName, namespaceURI);
}
throw new EndOfStreamException("Stream ended but no control found.");
2016-11-04 08:31:05 +00:00
}
2020-01-21 16:59:41 +00:00
private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
2016-11-04 08:31:05 +00:00
{
2020-01-21 16:59:41 +00:00
await reader.MoveToContentAsync().ConfigureAwait(false);
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
// Loop through each element
2016-12-03 21:46:06 +00:00
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
2016-11-04 08:31:05 +00:00
{
if (reader.NodeType == XmlNodeType.Element)
{
2016-12-04 21:30:38 +00:00
// TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
2020-01-21 16:59:41 +00:00
headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
}
else
{
2020-01-21 16:59:41 +00:00
await reader.ReadAsync().ConfigureAwait(false);
2016-11-04 08:31:05 +00:00
}
}
}
2021-04-01 17:16:00 +00:00
protected abstract void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter);
2016-10-29 22:22:20 +00:00
private void LogRequest(ControlRequest request)
{
if (!Config.GetDlnaConfiguration().EnableDebugLog)
{
return;
}
2016-10-29 22:22:20 +00:00
Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
2016-10-29 22:22:20 +00:00
}
private void LogResponse(ControlResponse response)
{
if (!Config.GetDlnaConfiguration().EnableDebugLog)
{
return;
}
2016-10-29 22:22:20 +00:00
2020-01-21 16:59:41 +00:00
Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
2016-10-29 22:22:20 +00:00
}
2020-08-20 19:04:57 +00:00
private class ControlRequestInfo
{
2020-09-25 18:44:16 +00:00
public ControlRequestInfo(string localName, string namespaceUri)
{
LocalName = localName;
NamespaceURI = namespaceUri;
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
2020-08-20 19:04:57 +00:00
public string LocalName { get; set; }
public string NamespaceURI { get; set; }
2020-09-25 18:44:16 +00:00
public Dictionary<string, string> Headers { get; }
2020-08-20 19:04:57 +00:00
}
2016-10-29 22:22:20 +00:00
}
}