#pragma warning disable CS1591 using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security;a using System.Text; using Emby.Dlna.Common; using MediaBrowser.Model.Dlna; namespace Emby.Dlna.Server { public class DescriptionXmlBuilder { private readonly DeviceProfile _profile; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly string _serverUdn; private readonly string _serverAddress; private readonly string _serverName; private readonly string _serverId; public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId) { if (string.IsNullOrEmpty(serverUdn)) { throw new ArgumentNullException(nameof(serverUdn)); } if (string.IsNullOrEmpty(serverAddress)) { throw new ArgumentNullException(nameof(serverAddress)); } _profile = profile; _serverUdn = serverUdn; _serverAddress = serverAddress; _serverName = serverName; _serverId = serverId; } private static bool EnableAbsoluteUrls => false; public string GetXml() { var builder = new StringBuilder(); builder.Append(""); builder.Append("'); builder.Append(""); builder.Append("1"); builder.Append("0"); builder.Append(""); if (!EnableAbsoluteUrls) { builder.Append("") .Append(SecurityElement.Escape(_serverAddress)) .Append(""); } AppendDeviceInfo(builder); builder.Append(""); return builder.ToString(); } private void AppendDeviceInfo(StringBuilder builder) { builder.Append(""); AppendDeviceProperties(builder); AppendIconList(builder); builder.Append("") .Append(SecurityElement.Escape(_serverAddress)) .Append("/web/index.html"); AppendServiceList(builder); builder.Append(""); } private void AppendDeviceProperties(StringBuilder builder) { builder.Append(""); builder.Append("DMS-1.50"); builder.Append("M-DMS-1.50"); builder.Append("urn:schemas-upnp-org:device:MediaServer:1"); builder.Append("") .Append(SecurityElement.Escape(GetFriendlyName())) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.Manufacturer ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.ModelDescription ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.ModelName ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.ModelNumber ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(_profile.ModelUrl ?? string.Empty)) .Append(""); if (string.IsNullOrEmpty(_profile.SerialNumber)) { builder.Append("") .Append(SecurityElement.Escape(_serverId)) .Append(""); } else { builder.Append("") .Append(SecurityElement.Escape(_profile.SerialNumber)) .Append(""); } builder.Append(""); builder.Append("uuid:") .Append(SecurityElement.Escape(_serverUdn)) .Append(""); if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags)) { builder.Append("") .Append(SecurityElement.Escape(_profile.SonyAggregationFlags)) .Append(""); } } private string GetFriendlyName() { if (string.IsNullOrEmpty(_profile.FriendlyName)) { return "Jellyfin - " + _serverName; } var characterList = new List(); foreach (var c in _serverName) { if (char.IsLetterOrDigit(c) || c == '-') { characterList.Add(c); } } var characters = characterList.ToArray(); var serverName = new string(characters); var name = _profile.FriendlyName?.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase); return name ?? string.Empty; } private void AppendIconList(StringBuilder builder) { builder.Append(""); foreach (var icon in GetIcons()) { builder.Append(""); builder.Append("") .Append(SecurityElement.Escape(icon.MimeType ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(icon.Width.ToString(_usCulture))) .Append(""); builder.Append("") .Append(SecurityElement.Escape(icon.Height.ToString(_usCulture))) .Append(""); builder.Append("") .Append(SecurityElement.Escape(icon.Depth ?? string.Empty)) .Append(""); builder.Append("") .Append(BuildUrl(icon.Url)) .Append(""); builder.Append(""); } builder.Append(""); } private void AppendServiceList(StringBuilder builder) { builder.Append(""); foreach (var service in GetServices()) { builder.Append(""); builder.Append("") .Append(SecurityElement.Escape(service.ServiceType ?? string.Empty)) .Append(""); builder.Append("") .Append(SecurityElement.Escape(service.ServiceId ?? string.Empty)) .Append(""); builder.Append("") .Append(BuildUrl(service.ScpdUrl, true)) .Append(""); builder.Append("") .Append(BuildUrl(service.ControlUrl, true)) .Append(""); builder.Append("") .Append(BuildUrl(service.EventSubUrl, true)) .Append(""); builder.Append(""); } builder.Append(""); } /// /// Builds a valid url for inclusion in the xml. /// /// Url to include. /// Optional. When set to true, the absolute url is always used. /// The url to use for the element. private string BuildUrl(string url, bool absoluteUrl = false) { if (string.IsNullOrEmpty(url)) { return string.Empty; } url = url.TrimStart('/'); url = "/dlna/" + _serverUdn + "/" + url; if (EnableAbsoluteUrls || absoluteUrl) { url = _serverAddress.TrimEnd('/') + url; } return SecurityElement.Escape(url); } private IEnumerable GetIcons() => new[] { new DeviceIcon { MimeType = "image/png", Depth = "24", Width = 240, Height = 240, Url = "icons/logo240.png" }, new DeviceIcon { MimeType = "image/jpeg", Depth = "24", Width = 240, Height = 240, Url = "icons/logo240.jpg" }, new DeviceIcon { MimeType = "image/png", Depth = "24", Width = 120, Height = 120, Url = "icons/logo120.png" }, new DeviceIcon { MimeType = "image/jpeg", Depth = "24", Width = 120, Height = 120, Url = "icons/logo120.jpg" }, new DeviceIcon { MimeType = "image/png", Depth = "24", Width = 48, Height = 48, Url = "icons/logo48.png" }, new DeviceIcon { MimeType = "image/jpeg", Depth = "24", Width = 48, Height = 48, Url = "icons/logo48.jpg" } }; private IEnumerable GetServices() { var list = new List(); list.Add(new DeviceService { ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1", ServiceId = "urn:upnp-org:serviceId:ContentDirectory", ScpdUrl = "contentdirectory/contentdirectory.xml", ControlUrl = "contentdirectory/control", EventSubUrl = "contentdirectory/events" }); list.Add(new DeviceService { ServiceType = "urn:schemas-upnp-org:service:ConnectionManager:1", ServiceId = "urn:upnp-org:serviceId:ConnectionManager", ScpdUrl = "connectionmanager/connectionmanager.xml", ControlUrl = "connectionmanager/control", EventSubUrl = "connectionmanager/events" }); if (_profile.EnableMSMediaReceiverRegistrar) { list.Add(new DeviceService { ServiceType = "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1", ServiceId = "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar", ScpdUrl = "mediareceiverregistrar/mediareceiverregistrar.xml", ControlUrl = "mediareceiverregistrar/control", EventSubUrl = "mediareceiverregistrar/events" }); } return list; } public override string ToString() { return GetXml(); } } }