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