aboutsummaryrefslogtreecommitdiff
path: root/Emby.Dlna
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Dlna')
-rw-r--r--Emby.Dlna/Api/DlnaServerService.cs383
-rw-r--r--Emby.Dlna/Api/DlnaService.cs88
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs1
-rw-r--r--Emby.Dlna/ControlResponse.cs6
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs3
-rw-r--r--Emby.Dlna/DlnaManager.cs24
-rw-r--r--Emby.Dlna/Eventing/EventManager.cs14
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs6
-rw-r--r--Emby.Dlna/PlayTo/Device.cs4
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs182
-rw-r--r--Emby.Dlna/Service/ServiceXmlBuilder.cs34
11 files changed, 135 insertions, 610 deletions
diff --git a/Emby.Dlna/Api/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs
deleted file mode 100644
index 7fba2184a7..0000000000
--- a/Emby.Dlna/Api/DlnaServerService.cs
+++ /dev/null
@@ -1,383 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using Emby.Dlna.Main;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Dlna.Api
-{
- [Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")]
- [Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")]
- public class GetDescriptionXml
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
- [Route("/Dlna/{UuId}/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
- public class GetContentDirectory
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")]
- [Route("/Dlna/{UuId}/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")]
- public class GetConnnectionManager
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar.xml", "GET", Summary = "Gets dlna mediareceiverregistrar xml")]
- [Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar", "GET", Summary = "Gets dlna mediareceiverregistrar xml")]
- public class GetMediaReceiverRegistrar
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/contentdirectory/control", "POST", Summary = "Processes a control request")]
- public class ProcessContentDirectoryControlRequest : IRequiresRequestStream
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
-
- public Stream RequestStream { get; set; }
- }
-
- [Route("/Dlna/{UuId}/connectionmanager/control", "POST", Summary = "Processes a control request")]
- public class ProcessConnectionManagerControlRequest : IRequiresRequestStream
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
-
- public Stream RequestStream { get; set; }
- }
-
- [Route("/Dlna/{UuId}/mediareceiverregistrar/control", "POST", Summary = "Processes a control request")]
- public class ProcessMediaReceiverRegistrarControlRequest : IRequiresRequestStream
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string UuId { get; set; }
-
- public Stream RequestStream { get; set; }
- }
-
- [Route("/Dlna/{UuId}/mediareceiverregistrar/events", "SUBSCRIBE", Summary = "Processes an event subscription request")]
- [Route("/Dlna/{UuId}/mediareceiverregistrar/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")]
- public class ProcessMediaReceiverRegistrarEventRequest
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/contentdirectory/events", "SUBSCRIBE", Summary = "Processes an event subscription request")]
- [Route("/Dlna/{UuId}/contentdirectory/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")]
- public class ProcessContentDirectoryEventRequest
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/connectionmanager/events", "SUBSCRIBE", Summary = "Processes an event subscription request")]
- [Route("/Dlna/{UuId}/connectionmanager/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")]
- public class ProcessConnectionManagerEventRequest
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")]
- public string UuId { get; set; }
- }
-
- [Route("/Dlna/{UuId}/icons/{Filename}", "GET", Summary = "Gets a server icon")]
- [Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
- public class GetIcon
- {
- [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string UuId { get; set; }
-
- [ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Filename { get; set; }
- }
-
- public class DlnaServerService : IService, IRequiresRequest
- {
- private const string XMLContentType = "text/xml; charset=UTF-8";
-
- private readonly IDlnaManager _dlnaManager;
- private readonly IHttpResultFactory _resultFactory;
- private readonly IServerConfigurationManager _configurationManager;
-
- public IRequest Request { get; set; }
-
- private IContentDirectory ContentDirectory => DlnaEntryPoint.Current.ContentDirectory;
-
- private IConnectionManager ConnectionManager => DlnaEntryPoint.Current.ConnectionManager;
-
- private IMediaReceiverRegistrar MediaReceiverRegistrar => DlnaEntryPoint.Current.MediaReceiverRegistrar;
-
- public DlnaServerService(
- IDlnaManager dlnaManager,
- IHttpResultFactory httpResultFactory,
- IServerConfigurationManager configurationManager)
- {
- _dlnaManager = dlnaManager;
- _resultFactory = httpResultFactory;
- _configurationManager = configurationManager;
- }
-
- private string GetHeader(string name)
- {
- return Request.Headers[name];
- }
-
- public object Get(GetDescriptionXml request)
- {
- var url = Request.AbsoluteUri;
- var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
- var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, request.UuId, serverAddress);
-
- var cacheLength = TimeSpan.FromDays(1);
- var cacheKey = Request.RawUrl.GetMD5();
- var bytes = Encoding.UTF8.GetBytes(xml);
-
- return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Get(GetContentDirectory request)
- {
- var xml = ContentDirectory.GetServiceXml();
-
- return _resultFactory.GetResult(Request, xml, XMLContentType);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Get(GetMediaReceiverRegistrar request)
- {
- var xml = MediaReceiverRegistrar.GetServiceXml();
-
- return _resultFactory.GetResult(Request, xml, XMLContentType);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Get(GetConnnectionManager request)
- {
- var xml = ConnectionManager.GetServiceXml();
-
- return _resultFactory.GetResult(Request, xml, XMLContentType);
- }
-
- public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
- {
- var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false);
-
- return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
- }
-
- public async Task<object> Post(ProcessContentDirectoryControlRequest request)
- {
- var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false);
-
- return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
- }
-
- public async Task<object> Post(ProcessConnectionManagerControlRequest request)
- {
- var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false);
-
- return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
- }
-
- private Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
- {
- var id = GetPathValue(2).ToString();
-
- return service.ProcessControlRequestAsync(new ControlRequest
- {
- Headers = Request.Headers,
- InputXml = requestStream,
- TargetServerUuId = id,
- RequestedUrl = Request.AbsoluteUri
- });
- }
-
- // Copied from MediaBrowser.Api/BaseApiService.cs
- // TODO: Remove code duplication
- /// <summary>
- /// Gets the path segment at the specified index.
- /// </summary>
- /// <param name="index">The index of the path segment.</param>
- /// <returns>The path segment at the specified index.</returns>
- /// <exception cref="IndexOutOfRangeException" >Path doesn't contain enough segments.</exception>
- /// <exception cref="InvalidDataException" >Path doesn't start with the base url.</exception>
- protected internal ReadOnlySpan<char> GetPathValue(int index)
- {
- static void ThrowIndexOutOfRangeException()
- => throw new IndexOutOfRangeException("Path doesn't contain enough segments.");
-
- static void ThrowInvalidDataException()
- => throw new InvalidDataException("Path doesn't start with the base url.");
-
- ReadOnlySpan<char> path = Request.PathInfo;
-
- // Remove the protocol part from the url
- int pos = path.LastIndexOf("://");
- if (pos != -1)
- {
- path = path.Slice(pos + 3);
- }
-
- // Remove the query string
- pos = path.LastIndexOf('?');
- if (pos != -1)
- {
- path = path.Slice(0, pos);
- }
-
- // Remove the domain
- pos = path.IndexOf('/');
- if (pos != -1)
- {
- path = path.Slice(pos);
- }
-
- // Remove base url
- string baseUrl = _configurationManager.Configuration.BaseUrl;
- int baseUrlLen = baseUrl.Length;
- if (baseUrlLen != 0)
- {
- if (path.StartsWith(baseUrl, StringComparison.OrdinalIgnoreCase))
- {
- path = path.Slice(baseUrlLen);
- }
- else
- {
- // The path doesn't start with the base url,
- // how did we get here?
- ThrowInvalidDataException();
- }
- }
-
- // Remove leading /
- path = path.Slice(1);
-
- // Backwards compatibility
- const string Emby = "emby/";
- if (path.StartsWith(Emby, StringComparison.OrdinalIgnoreCase))
- {
- path = path.Slice(Emby.Length);
- }
-
- const string MediaBrowser = "mediabrowser/";
- if (path.StartsWith(MediaBrowser, StringComparison.OrdinalIgnoreCase))
- {
- path = path.Slice(MediaBrowser.Length);
- }
-
- // Skip segments until we are at the right index
- for (int i = 0; i < index; i++)
- {
- pos = path.IndexOf('/');
- if (pos == -1)
- {
- ThrowIndexOutOfRangeException();
- }
-
- path = path.Slice(pos + 1);
- }
-
- // Remove the rest
- pos = path.IndexOf('/');
- if (pos != -1)
- {
- path = path.Slice(0, pos);
- }
-
- return path;
- }
-
- public object Get(GetIcon request)
- {
- var contentType = "image/" + Path.GetExtension(request.Filename)
- .TrimStart('.')
- .ToLowerInvariant();
-
- var cacheLength = TimeSpan.FromDays(365);
- var cacheKey = Request.RawUrl.GetMD5();
-
- return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream));
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Subscribe(ProcessContentDirectoryEventRequest request)
- {
- return ProcessEventRequest(ContentDirectory);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Subscribe(ProcessConnectionManagerEventRequest request)
- {
- return ProcessEventRequest(ConnectionManager);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
- {
- return ProcessEventRequest(MediaReceiverRegistrar);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Unsubscribe(ProcessContentDirectoryEventRequest request)
- {
- return ProcessEventRequest(ContentDirectory);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Unsubscribe(ProcessConnectionManagerEventRequest request)
- {
- return ProcessEventRequest(ConnectionManager);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
- {
- return ProcessEventRequest(MediaReceiverRegistrar);
- }
-
- private object ProcessEventRequest(IEventManager eventManager)
- {
- var subscriptionId = GetHeader("SID");
-
- if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
- {
- var notificationType = GetHeader("NT");
-
- var callback = GetHeader("CALLBACK");
- var timeoutString = GetHeader("TIMEOUT");
-
- if (string.IsNullOrEmpty(notificationType))
- {
- return GetSubscriptionResponse(eventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callback));
- }
-
- return GetSubscriptionResponse(eventManager.CreateEventSubscription(notificationType, timeoutString, callback));
- }
-
- return GetSubscriptionResponse(eventManager.CancelEventSubscription(subscriptionId));
- }
-
- private object GetSubscriptionResponse(EventSubscriptionResponse response)
- {
- return _resultFactory.GetResult(Request, response.Content, response.ContentType, response.Headers);
- }
- }
-}
diff --git a/Emby.Dlna/Api/DlnaService.cs b/Emby.Dlna/Api/DlnaService.cs
deleted file mode 100644
index 5f984bb335..0000000000
--- a/Emby.Dlna/Api/DlnaService.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Dlna.Api
-{
- [Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
- public class GetProfileInfos : IReturn<DeviceProfileInfo[]>
- {
- }
-
- [Route("/Dlna/Profiles/{Id}", "DELETE", Summary = "Deletes a profile")]
- public class DeleteProfile : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "Profile Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Dlna/Profiles/Default", "GET", Summary = "Gets the default profile")]
- public class GetDefaultProfile : IReturn<DeviceProfile>
- {
- }
-
- [Route("/Dlna/Profiles/{Id}", "GET", Summary = "Gets a single profile")]
- public class GetProfile : IReturn<DeviceProfile>
- {
- [ApiMember(Name = "Id", Description = "Profile Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Id { get; set; }
- }
-
- [Route("/Dlna/Profiles/{Id}", "POST", Summary = "Updates a profile")]
- public class UpdateProfile : DeviceProfile, IReturnVoid
- {
- }
-
- [Route("/Dlna/Profiles", "POST", Summary = "Creates a profile")]
- public class CreateProfile : DeviceProfile, IReturnVoid
- {
- }
-
- [Authenticated(Roles = "Admin")]
- public class DlnaService : IService
- {
- private readonly IDlnaManager _dlnaManager;
-
- public DlnaService(IDlnaManager dlnaManager)
- {
- _dlnaManager = dlnaManager;
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Get(GetProfileInfos request)
- {
- return _dlnaManager.GetProfileInfos().ToArray();
- }
-
- public object Get(GetProfile request)
- {
- return _dlnaManager.GetProfile(request.Id);
- }
-
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
- public object Get(GetDefaultProfile request)
- {
- return _dlnaManager.GetDefaultProfile();
- }
-
- public void Delete(DeleteProfile request)
- {
- _dlnaManager.DeleteProfile(request.Id);
- }
-
- public void Post(UpdateProfile request)
- {
- _dlnaManager.UpdateProfile(request);
- }
-
- public void Post(CreateProfile request)
- {
- _dlnaManager.CreateProfile(request);
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 291de5245b..00821bf780 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -11,6 +11,7 @@ using System.Xml;
using Emby.Dlna.Didl;
using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs
index 140ef9b463..243b097867 100644
--- a/Emby.Dlna/ControlResponse.cs
+++ b/Emby.Dlna/ControlResponse.cs
@@ -16,5 +16,11 @@ namespace Emby.Dlna
public string Xml { get; set; }
public bool IsSuccessful { get; set; }
+
+ /// <inheritdoc />
+ public override string ToString()
+ {
+ return Xml;
+ }
}
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 66baa9512c..70e358019a 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -364,7 +364,8 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture));
}
- var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
+ var mediaProfile = _profile.GetVideoMediaProfile(
+ streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioBitrate,
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 5911a73ef1..269f7ee43a 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -122,15 +122,15 @@ namespace Emby.Dlna
var builder = new StringBuilder();
builder.AppendLine("No matching device profile found. The default will need to be used.");
- builder.AppendLine(string.Format("DeviceDescription:{0}", profile.DeviceDescription ?? string.Empty));
- builder.AppendLine(string.Format("FriendlyName:{0}", profile.FriendlyName ?? string.Empty));
- builder.AppendLine(string.Format("Manufacturer:{0}", profile.Manufacturer ?? string.Empty));
- builder.AppendLine(string.Format("ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty));
- builder.AppendLine(string.Format("ModelDescription:{0}", profile.ModelDescription ?? string.Empty));
- builder.AppendLine(string.Format("ModelName:{0}", profile.ModelName ?? string.Empty));
- builder.AppendLine(string.Format("ModelNumber:{0}", profile.ModelNumber ?? string.Empty));
- builder.AppendLine(string.Format("ModelUrl:{0}", profile.ModelUrl ?? string.Empty));
- builder.AppendLine(string.Format("SerialNumber:{0}", profile.SerialNumber ?? string.Empty));
+ builder.AppendFormat(CultureInfo.InvariantCulture, "DeviceDescription:{0}", profile.DeviceDescription ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "ModelDescription:{0}", profile.ModelDescription ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "ModelName:{0}", profile.ModelName ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "ModelNumber:{0}", profile.ModelNumber ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "ModelUrl:{0}", profile.ModelUrl ?? string.Empty).AppendLine();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "SerialNumber:{0}", profile.SerialNumber ?? string.Empty).AppendLine();
_logger.LogInformation(builder.ToString());
}
@@ -387,7 +387,7 @@ namespace Emby.Dlna
foreach (var name in _assembly.GetManifestResourceNames())
{
- if (!name.StartsWith(namespaceName))
+ if (!name.StartsWith(namespaceName, StringComparison.Ordinal))
{
continue;
}
@@ -406,7 +406,7 @@ namespace Emby.Dlna
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
- await stream.CopyToAsync(fileStream);
+ await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
@@ -509,7 +509,7 @@ namespace Emby.Dlna
return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
}
- class InternalProfileInfo
+ private class InternalProfileInfo
{
internal DeviceProfileInfo Info { get; set; }
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs
index 56c90c8b3a..7d02f5e960 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/EventManager.cs
@@ -152,11 +152,15 @@ namespace Emby.Dlna.Eventing
builder.Append("<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">");
foreach (var key in stateVariables.Keys)
{
- builder.Append("<e:property>");
- builder.Append("<" + key + ">");
- builder.Append(stateVariables[key]);
- builder.Append("</" + key + ">");
- builder.Append("</e:property>");
+ builder.Append("<e:property>")
+ .Append('<')
+ .Append(key)
+ .Append('>')
+ .Append(stateVariables[key])
+ .Append("</")
+ .Append(key)
+ .Append('>')
+ .Append("</e:property>");
}
builder.Append("</e:propertyset>");
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 9b9b57e973..a21d4cc11d 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -54,11 +54,11 @@ namespace Emby.Dlna.Main
private SsdpDevicePublisher _publisher;
private ISsdpCommunicationsServer _communicationsServer;
- internal IContentDirectory ContentDirectory { get; private set; }
+ public IContentDirectory ContentDirectory { get; private set; }
- internal IConnectionManager ConnectionManager { get; private set; }
+ public IConnectionManager ConnectionManager { get; private set; }
- internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
+ public IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
public static DlnaEntryPoint Current;
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index c5080b90f3..72834c69d1 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -4,12 +4,12 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Emby.Dlna.Common;
-using Emby.Dlna.Server;
using Emby.Dlna.Ssdp;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -334,7 +334,7 @@ namespace Emby.Dlna.PlayTo
return string.Empty;
}
- return DescriptionXmlBuilder.Escape(value);
+ return SecurityElement.Escape(value);
}
private Task SetPlay(TransportCommands avCommands, CancellationToken cancellationToken)
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index 7143c31094..bca9e81cd0 100644
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Security;
using System.Text;
using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
@@ -64,10 +65,10 @@ namespace Emby.Dlna.Server
foreach (var att in attributes)
{
- builder.AppendFormat(" {0}=\"{1}\"", att.Name, att.Value);
+ builder.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", att.Name, att.Value);
}
- builder.Append(">");
+ builder.Append('>');
builder.Append("<specVersion>");
builder.Append("<major>1</major>");
@@ -76,7 +77,9 @@ namespace Emby.Dlna.Server
if (!EnableAbsoluteUrls)
{
- builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
+ builder.Append("<URLBase>")
+ .Append(SecurityElement.Escape(_serverAddress))
+ .Append("</URLBase>");
}
AppendDeviceInfo(builder);
@@ -93,91 +96,14 @@ namespace Emby.Dlna.Server
AppendIconList(builder);
- builder.Append("<presentationURL>" + Escape(_serverAddress) + "/web/index.html</presentationURL>");
+ builder.Append("<presentationURL>")
+ .Append(SecurityElement.Escape(_serverAddress))
+ .Append("/web/index.html</presentationURL>");
AppendServiceList(builder);
builder.Append("</device>");
}
- private static readonly char[] s_escapeChars = new char[]
- {
- '<',
- '>',
- '"',
- '\'',
- '&'
- };
-
- private static readonly string[] s_escapeStringPairs = new[]
- {
- "<",
- "&lt;",
- ">",
- "&gt;",
- "\"",
- "&quot;",
- "'",
- "&apos;",
- "&",
- "&amp;"
- };
-
- private static string GetEscapeSequence(char c)
- {
- int num = s_escapeStringPairs.Length;
- for (int i = 0; i < num; i += 2)
- {
- string text = s_escapeStringPairs[i];
- string result = s_escapeStringPairs[i + 1];
- if (text[0] == c)
- {
- return result;
- }
- }
-
- return c.ToString(CultureInfo.InvariantCulture);
- }
-
- /// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
- /// <returns>The input string with invalid characters replaced.</returns>
- /// <param name="str">The string within which to escape invalid characters. </param>
- public static string Escape(string str)
- {
- if (str == null)
- {
- return null;
- }
-
- StringBuilder stringBuilder = null;
- int length = str.Length;
- int num = 0;
- while (true)
- {
- int num2 = str.IndexOfAny(s_escapeChars, num);
- if (num2 == -1)
- {
- break;
- }
-
- if (stringBuilder == null)
- {
- stringBuilder = new StringBuilder();
- }
-
- stringBuilder.Append(str, num, num2 - num);
- stringBuilder.Append(GetEscapeSequence(str[num2]));
- num = num2 + 1;
- }
-
- if (stringBuilder == null)
- {
- return str;
- }
-
- stringBuilder.Append(str, num, length - num);
- return stringBuilder.ToString();
- }
-
private void AppendDeviceProperties(StringBuilder builder)
{
builder.Append("<dlna:X_DLNACAP/>");
@@ -187,32 +113,54 @@ namespace Emby.Dlna.Server
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
- builder.Append("<friendlyName>" + Escape(GetFriendlyName()) + "</friendlyName>");
- builder.Append("<manufacturer>" + Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");
- builder.Append("<manufacturerURL>" + Escape(_profile.ManufacturerUrl ?? string.Empty) + "</manufacturerURL>");
-
- builder.Append("<modelDescription>" + Escape(_profile.ModelDescription ?? string.Empty) + "</modelDescription>");
- builder.Append("<modelName>" + Escape(_profile.ModelName ?? string.Empty) + "</modelName>");
-
- builder.Append("<modelNumber>" + Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>");
- builder.Append("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
+ builder.Append("<friendlyName>")
+ .Append(SecurityElement.Escape(GetFriendlyName()))
+ .Append("</friendlyName>");
+ builder.Append("<manufacturer>")
+ .Append(SecurityElement.Escape(_profile.Manufacturer ?? string.Empty))
+ .Append("</manufacturer>");
+ builder.Append("<manufacturerURL>")
+ .Append(SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty))
+ .Append("</manufacturerURL>");
+
+ builder.Append("<modelDescription>")
+ .Append(SecurityElement.Escape(_profile.ModelDescription ?? string.Empty))
+ .Append("</modelDescription>");
+ builder.Append("<modelName>")
+ .Append(SecurityElement.Escape(_profile.ModelName ?? string.Empty))
+ .Append("</modelName>");
+
+ builder.Append("<modelNumber>")
+ .Append(SecurityElement.Escape(_profile.ModelNumber ?? string.Empty))
+ .Append("</modelNumber>");
+ builder.Append("<modelURL>")
+ .Append(SecurityElement.Escape(_profile.ModelUrl ?? string.Empty))
+ .Append("</modelURL>");
if (string.IsNullOrEmpty(_profile.SerialNumber))
{
- builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>");
+ builder.Append("<serialNumber>")
+ .Append(SecurityElement.Escape(_serverId))
+ .Append("</serialNumber>");
}
else
{
- builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>");
+ builder.Append("<serialNumber>")
+ .Append(SecurityElement.Escape(_profile.SerialNumber))
+ .Append("</serialNumber>");
}
builder.Append("<UPC/>");
- builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
+ builder.Append("<UDN>uuid:")
+ .Append(SecurityElement.Escape(_serverUdn))
+ .Append("</UDN>");
if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
{
- builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");
+ builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">")
+ .Append(SecurityElement.Escape(_profile.SonyAggregationFlags))
+ .Append("</av:aggregationFlags>");
}
}
@@ -250,11 +198,21 @@ namespace Emby.Dlna.Server
{
builder.Append("<icon>");
- builder.Append("<mimetype>" + Escape(icon.MimeType ?? string.Empty) + "</mimetype>");
- builder.Append("<width>" + Escape(icon.Width.ToString(_usCulture)) + "</width>");
- builder.Append("<height>" + Escape(icon.Height.ToString(_usCulture)) + "</height>");
- builder.Append("<depth>" + Escape(icon.Depth ?? string.Empty) + "</depth>");
- builder.Append("<url>" + BuildUrl(icon.Url) + "</url>");
+ builder.Append("<mimetype>")
+ .Append(SecurityElement.Escape(icon.MimeType ?? string.Empty))
+ .Append("</mimetype>");
+ builder.Append("<width>")
+ .Append(SecurityElement.Escape(icon.Width.ToString(_usCulture)))
+ .Append("</width>");
+ builder.Append("<height>")
+ .Append(SecurityElement.Escape(icon.Height.ToString(_usCulture)))
+ .Append("</height>");
+ builder.Append("<depth>")
+ .Append(SecurityElement.Escape(icon.Depth ?? string.Empty))
+ .Append("</depth>");
+ builder.Append("<url>")
+ .Append(BuildUrl(icon.Url))
+ .Append("</url>");
builder.Append("</icon>");
}
@@ -270,11 +228,21 @@ namespace Emby.Dlna.Server
{
builder.Append("<service>");
- builder.Append("<serviceType>" + Escape(service.ServiceType ?? string.Empty) + "</serviceType>");
- builder.Append("<serviceId>" + Escape(service.ServiceId ?? string.Empty) + "</serviceId>");
- builder.Append("<SCPDURL>" + BuildUrl(service.ScpdUrl) + "</SCPDURL>");
- builder.Append("<controlURL>" + BuildUrl(service.ControlUrl) + "</controlURL>");
- builder.Append("<eventSubURL>" + BuildUrl(service.EventSubUrl) + "</eventSubURL>");
+ builder.Append("<serviceType>")
+ .Append(SecurityElement.Escape(service.ServiceType ?? string.Empty))
+ .Append("</serviceType>");
+ builder.Append("<serviceId>")
+ .Append(SecurityElement.Escape(service.ServiceId ?? string.Empty))
+ .Append("</serviceId>");
+ builder.Append("<SCPDURL>")
+ .Append(BuildUrl(service.ScpdUrl))
+ .Append("</SCPDURL>");
+ builder.Append("<controlURL>")
+ .Append(BuildUrl(service.ControlUrl))
+ .Append("</controlURL>");
+ builder.Append("<eventSubURL>")
+ .Append(BuildUrl(service.EventSubUrl))
+ .Append("</eventSubURL>");
builder.Append("</service>");
}
@@ -298,7 +266,7 @@ namespace Emby.Dlna.Server
url = _serverAddress.TrimEnd('/') + url;
}
- return Escape(url);
+ return SecurityElement.Escape(url);
}
private IEnumerable<DeviceIcon> GetIcons()
diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
index af557aa144..6c7d6f8462 100644
--- a/Emby.Dlna/Service/ServiceXmlBuilder.cs
+++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs
@@ -1,9 +1,9 @@
#pragma warning disable CS1591
using System.Collections.Generic;
+using System.Security;
using System.Text;
using Emby.Dlna.Common;
-using Emby.Dlna.Server;
namespace Emby.Dlna.Service
{
@@ -37,7 +37,9 @@ namespace Emby.Dlna.Service
{
builder.Append("<action>");
- builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>");
+ builder.Append("<name>")
+ .Append(SecurityElement.Escape(item.Name ?? string.Empty))
+ .Append("</name>");
builder.Append("<argumentList>");
@@ -45,9 +47,15 @@ namespace Emby.Dlna.Service
{
builder.Append("<argument>");
- builder.Append("<name>" + DescriptionXmlBuilder.Escape(argument.Name ?? string.Empty) + "</name>");
- builder.Append("<direction>" + DescriptionXmlBuilder.Escape(argument.Direction ?? string.Empty) + "</direction>");
- builder.Append("<relatedStateVariable>" + DescriptionXmlBuilder.Escape(argument.RelatedStateVariable ?? string.Empty) + "</relatedStateVariable>");
+ builder.Append("<name>")
+ .Append(SecurityElement.Escape(argument.Name ?? string.Empty))
+ .Append("</name>");
+ builder.Append("<direction>")
+ .Append(SecurityElement.Escape(argument.Direction ?? string.Empty))
+ .Append("</direction>");
+ builder.Append("<relatedStateVariable>")
+ .Append(SecurityElement.Escape(argument.RelatedStateVariable ?? string.Empty))
+ .Append("</relatedStateVariable>");
builder.Append("</argument>");
}
@@ -68,17 +76,25 @@ namespace Emby.Dlna.Service
{
var sendEvents = item.SendsEvents ? "yes" : "no";
- builder.Append("<stateVariable sendEvents=\"" + sendEvents + "\">");
+ builder.Append("<stateVariable sendEvents=\"")
+ .Append(sendEvents)
+ .Append("\">");
- builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>");
- builder.Append("<dataType>" + DescriptionXmlBuilder.Escape(item.DataType ?? string.Empty) + "</dataType>");
+ builder.Append("<name>")
+ .Append(SecurityElement.Escape(item.Name ?? string.Empty))
+ .Append("</name>");
+ builder.Append("<dataType>")
+ .Append(SecurityElement.Escape(item.DataType ?? string.Empty))
+ .Append("</dataType>");
if (item.AllowedValues.Length > 0)
{
builder.Append("<allowedValueList>");
foreach (var allowedValue in item.AllowedValues)
{
- builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>");
+ builder.Append("<allowedValue>")
+ .Append(SecurityElement.Escape(allowedValue))
+ .Append("</allowedValue>");
}
builder.Append("</allowedValueList>");