2016-10-30 00:22:20 +02:00
|
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
|
|
using MediaBrowser.Controller.Dlna;
|
2016-10-30 00:34:54 +02:00
|
|
|
|
using Emby.Dlna.Server;
|
2016-10-30 00:22:20 +02:00
|
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
|
using System;
|
|
|
|
|
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.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml;
|
2016-11-04 09:31:05 +01:00
|
|
|
|
using Emby.Dlna.Didl;
|
2016-12-04 22:30:38 +01:00
|
|
|
|
using MediaBrowser.Controller.Extensions;
|
2016-11-04 09:31:05 +01:00
|
|
|
|
using MediaBrowser.Model.Xml;
|
2017-08-09 21:56:38 +02:00
|
|
|
|
using MediaBrowser.Model.Extensions;
|
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
|
|
|
|
|
{
|
|
|
|
|
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
2016-12-04 00:57:34 +01:00
|
|
|
|
|
2016-10-30 00:22:20 +02:00
|
|
|
|
protected readonly IServerConfigurationManager Config;
|
|
|
|
|
protected readonly ILogger Logger;
|
2016-11-04 09:31:05 +01:00
|
|
|
|
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
2016-10-30 00:22:20 +02:00
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
2016-10-30 00:22:20 +02:00
|
|
|
|
{
|
|
|
|
|
Config = config;
|
|
|
|
|
Logger = logger;
|
2016-11-04 09:31:05 +01:00
|
|
|
|
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
2016-10-30 00:22:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ControlResponse ProcessControlRequest(ControlRequest request)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
|
|
|
|
|
|
|
|
|
|
if (enableDebugLogging)
|
|
|
|
|
{
|
|
|
|
|
LogRequest(request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = ProcessControlRequestInternal(request);
|
|
|
|
|
|
|
|
|
|
if (enableDebugLogging)
|
|
|
|
|
{
|
|
|
|
|
LogResponse(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.ErrorException("Error processing control request", ex);
|
|
|
|
|
|
|
|
|
|
return new ControlErrorHandler().GetResponse(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ControlResponse ProcessControlRequestInternal(ControlRequest request)
|
|
|
|
|
{
|
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
|
|
|
|
{
|
2016-11-04 09:31:05 +01:00
|
|
|
|
var readerSettings = XmlReaderSettingsFactory.Create(false);
|
|
|
|
|
|
|
|
|
|
readerSettings.CheckCharacters = false;
|
|
|
|
|
readerSettings.IgnoreProcessingInstructions = true;
|
|
|
|
|
readerSettings.IgnoreComments = true;
|
|
|
|
|
|
|
|
|
|
using (var reader = XmlReader.Create(streamReader, readerSettings))
|
2016-10-30 00:22:20 +02:00
|
|
|
|
{
|
2016-11-04 09:31:05 +01:00
|
|
|
|
requestInfo = ParseRequest(reader);
|
2016-10-30 00:22:20 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
Logger.Debug("Received control request {0}", requestInfo.LocalName);
|
2016-10-30 00:22:20 +02:00
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
|
2016-10-30 00:22:20 +02:00
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
var settings = new XmlWriterSettings
|
|
|
|
|
{
|
|
|
|
|
Encoding = Encoding.UTF8,
|
|
|
|
|
CloseOutput = false
|
|
|
|
|
};
|
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
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
using (XmlWriter writer = XmlWriter.Create(builder, settings))
|
2016-10-30 00:22:20 +02:00
|
|
|
|
{
|
2016-11-04 09:31:05 +01:00
|
|
|
|
writer.WriteStartDocument(true);
|
|
|
|
|
|
|
|
|
|
writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
|
|
|
|
|
writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
|
|
|
|
|
|
|
|
|
|
writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
|
|
|
|
|
writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
|
|
|
|
|
foreach (var i in result)
|
|
|
|
|
{
|
|
|
|
|
writer.WriteStartElement(i.Key);
|
|
|
|
|
writer.WriteString(i.Value);
|
2016-11-06 18:30:44 +01:00
|
|
|
|
writer.WriteFullEndElement();
|
2016-11-04 09:31:05 +01:00
|
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
|
2016-12-04 00:57:34 +01:00
|
|
|
|
|
2016-10-30 00:22:20 +02:00
|
|
|
|
var controlResponse = new ControlResponse
|
|
|
|
|
{
|
|
|
|
|
Xml = xml,
|
|
|
|
|
IsSuccessful = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//Logger.Debug(xml);
|
|
|
|
|
|
|
|
|
|
controlResponse.Headers.Add("EXT", string.Empty);
|
|
|
|
|
|
|
|
|
|
return controlResponse;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-04 09:31:05 +01:00
|
|
|
|
private ControlRequestInfo ParseRequest(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
|
{
|
|
|
|
|
return ParseBodyTag(subReader);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-11-04 09:31:05 +01:00
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
reader.Skip();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new ControlRequestInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ControlRequestInfo ParseBodyTag(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
var result = new ControlRequestInfo();
|
|
|
|
|
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
|
{
|
2016-12-04 22:30:38 +01:00
|
|
|
|
ParseFirstBodyChild(subReader, result.Headers);
|
2016-12-04 00:57:34 +01:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
2016-11-04 09:31:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 22:30:38 +01:00
|
|
|
|
private void ParseFirstBodyChild(XmlReader reader, IDictionary<string,string> headers)
|
2016-11-04 09:31:05 +01:00
|
|
|
|
{
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
|
|
|
|
|
|
// 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?
|
|
|
|
|
headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString();
|
2016-11-04 09:31:05 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ControlRequestInfo
|
|
|
|
|
{
|
|
|
|
|
public string LocalName;
|
|
|
|
|
public string NamespaceURI;
|
2016-12-04 22:30:38 +01:00
|
|
|
|
public IDictionary<string, string> Headers = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
|
2016-11-04 09:31:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-04 22:30:38 +01:00
|
|
|
|
protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
|
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
|
|
|
|
|
2017-08-09 21:56:38 +02:00
|
|
|
|
var originalHeaders = request.Headers;
|
|
|
|
|
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count));
|
2016-10-30 00:22:20 +02:00
|
|
|
|
|
2017-08-09 21:56:38 +02:00
|
|
|
|
Logger.Debug("Control request. Headers: {0}", 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
|
|
|
|
|
2017-08-09 21:56:38 +02:00
|
|
|
|
var originalHeaders = response.Headers;
|
|
|
|
|
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray(originalHeaders.Count));
|
|
|
|
|
//builder.Append(response.Xml);
|
2016-10-30 00:22:20 +02:00
|
|
|
|
|
2017-08-09 21:56:38 +02:00
|
|
|
|
Logger.Debug("Control response. Headers: {0}", headers);
|
2016-10-30 00:22:20 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|