Merge branch 'master' into master

This commit is contained in:
whooo 2019-08-11 12:42:19 +02:00 committed by GitHub
commit 9556561a77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
160 changed files with 1678 additions and 5249 deletions

View file

@ -190,7 +190,7 @@ jobs:
- task: CmdLine@2
displayName: Execute ABI compatibility check tool
inputs:
script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName)'
script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines'
workingDirectory: $(System.ArtifactsDirectory) # Optional
#failOnStderr: false # Optional

View file

@ -1,20 +0,0 @@
---
name: Enhancement request
about: Suggest an modification to an existing feature
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View file

@ -1,14 +0,0 @@
---
name: Feature request
about: Suggest a new feature
title: ''
labels: feature
assignees: ''
---
**Describe the feature you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

22
.github/stale.yml vendored Normal file
View file

@ -0,0 +1,22 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- regression
- security
- dotnet-3.0-future
- roadmap
- future
- feature
- enhancement
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Issues go stale after 90d of inactivity. Mark the issue as fresh by adding a comment or commit. Stale issues close after an additional 14d of inactivity.
If this issue is safe to close now please do so.
If you have any questions you can reach us on [Matrix or Social Media](https://jellyfin.readthedocs.io/en/latest/getting-help/).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View file

@ -27,6 +27,7 @@
- [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h)
- [Khinenw](https://github.com/HelloWorld017)
# Emby Contributors

View file

@ -21,10 +21,10 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
&& mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media

View file

@ -26,10 +26,10 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
&& mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media

View file

@ -26,10 +26,10 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
ARG JELLYFIN_WEB_VERSION=10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
ARG JELLYFIN_WEB_VERSION=v10.3.7
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
&& mv jellyfin-web-* /jellyfin/jellyfin-web
EXPOSE 8096
VOLUME /cache /config /media

View file

@ -181,19 +181,6 @@ namespace Emby.Dlna.Didl
writer.WriteFullEndElement();
}
private string GetMimeType(string input)
{
var mime = MimeTypes.GetMimeType(input);
// TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
{
mime = "video/mpeg";
}
return mime;
}
private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
if (streamInfo == null)
@ -384,7 +371,7 @@ namespace Emby.Dlna.Didl
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? GetMimeType(filename)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
writer.WriteAttributeString("protocolInfo", string.Format(
@ -520,7 +507,7 @@ namespace Emby.Dlna.Didl
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? GetMimeType(filename)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
@ -545,17 +532,10 @@ namespace Emby.Dlna.Didl
}
public static bool IsIdRoot(string id)
{
if (string.IsNullOrWhiteSpace(id)
=> string.IsNullOrWhiteSpace(id)
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
// Samsung sometimes uses 1 as root
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase);
public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{
@ -971,7 +951,7 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
GetMimeType("file." + format),
MimeTypes.GetMimeType("file." + format),
contentFeatures
));
@ -1102,7 +1082,7 @@ namespace Emby.Dlna.Didl
public static string GetClientId(Guid idValue, StubType? stubType)
{
var id = idValue.ToString("N");
var id = idValue.ToString("N", CultureInfo.InvariantCulture);
if (stubType.HasValue)
{
@ -1116,7 +1096,7 @@ namespace Emby.Dlna.Didl
{
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
_serverAddress,
info.ItemId.ToString("N"),
info.ItemId.ToString("N", CultureInfo.InvariantCulture),
info.Type,
info.ImageTag,
format,

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
@ -300,7 +301,7 @@ namespace Emby.Dlna
profile = ReserializeProfile(tempProfile);
profile.Id = path.ToLowerInvariant().GetMD5().ToString("N");
profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
@ -352,7 +353,7 @@ namespace Emby.Dlna
Info = new DeviceProfileInfo
{
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"),
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
Name = _fileSystem.GetFileNameWithoutExtension(file),
Type = type
}

View file

@ -55,7 +55,7 @@ namespace Emby.Dlna.Eventing
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
{
var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
var id = "uuid:" + Guid.NewGuid().ToString("N");
var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
// Remove logging for now because some devices are sending this very frequently
// TODO re-enable with dlna debug logging setting

View file

@ -1,4 +1,6 @@
using System;
using System.Net.Sockets;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
@ -247,7 +249,7 @@ namespace Emby.Dlna.Main
foreach (var address in addresses)
{
if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
// Not support IPv6 right now
continue;
@ -306,7 +308,7 @@ namespace Emby.Dlna.Main
{
guid = text.GetMD5();
}
return guid.ToString("N");
return guid.ToString("N", CultureInfo.InvariantCulture);
}
private void SetProperies(SsdpDevice device, string fullDeviceType)

View file

@ -1,5 +1,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@ -14,7 +16,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@ -141,7 +142,7 @@ namespace Emby.Dlna.PlayTo
return usn;
}
return usn.GetMD5().ToString("N");
return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
@ -156,7 +157,7 @@ namespace Emby.Dlna.PlayTo
}
else
{
uuid = location.GetMD5().ToString("N");
uuid = location.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, null, uri.OriginalString, null);
@ -172,7 +173,7 @@ namespace Emby.Dlna.PlayTo
_sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
string serverAddress;
if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Any))
if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IPAddress.Any) || info.LocalIpAddress.Equals(IPAddress.IPv6Any))
{
serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
}

View file

@ -9,7 +9,7 @@ namespace Emby.Dlna.Profiles
{
Name = "Dish Hopper-Joey";
ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
Identification = new DeviceIdentification
{

View file

@ -28,7 +28,7 @@
<MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<ProtocolInfo>http-get:*:video/mp2t:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
<ProtocolInfo>http-get:*:video/mp2t:http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
<TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders>

View file

@ -454,14 +454,14 @@ namespace Emby.Drawing
// Optimization
if (imageEnhancers.Length == 0)
{
return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N");
return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
// Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
cacheKeys.Add(originalImagePath + dateModified.Ticks);
return string.Join("|", cacheKeys).GetMD5().ToString("N");
return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@ -480,7 +480,7 @@ namespace Emby.Drawing
{
try
{
string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N");
string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -101,7 +102,7 @@ namespace Emby.Notifications
var config = GetConfiguration();
return _userManager.Users
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy))
.Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i.Policy))
.Select(i => i.Id);
}
@ -197,7 +198,7 @@ namespace Emby.Notifications
return _services.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Name.GetMD5().ToString("N")
Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture)
}).OrderBy(i => i.Name);
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -75,7 +76,6 @@ namespace Emby.Server.Implementations.Activity
_sessionManager.AuthenticationFailed += OnAuthenticationFailed;
_sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
_sessionManager.SessionEnded += OnSessionEnded;
_sessionManager.PlaybackStart += OnPlaybackStart;
_sessionManager.PlaybackStopped += OnPlaybackStopped;
@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.Activity
{
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
Type = "SubtitleDownloadFailure",
ItemId = e.Item.Id.ToString("N"),
ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
ShortOverview = e.Exception.Message
});
}

View file

@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Activity
}
else
{
statement.TryBind("@UserId", entry.UserId.ToString("N"));
statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
}
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Activity
}
else
{
statement.TryBind("@UserId", entry.UserId.ToString("N"));
statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
}
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());

View file

@ -10,6 +10,8 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
public abstract class BaseApplicationPaths : IApplicationPaths
{
private string _dataPath;
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
@ -30,27 +32,27 @@ namespace Emby.Server.Implementations.AppBase
}
/// <summary>
/// Gets the path to the program data folder
/// Gets the path to the program data folder.
/// </summary>
/// <value>The program data path.</value>
public string ProgramDataPath { get; private set; }
public string ProgramDataPath { get; }
/// <summary>
/// Gets the path to the web UI resources folder
/// Gets the path to the web UI resources folder.
/// </summary>
/// <value>The web UI resources path.</value>
public string WebPath { get; set; }
public string WebPath { get; }
/// <summary>
/// Gets the path to the system folder
/// Gets the path to the system folder.
/// </summary>
/// <value>The path to the system folder.</value>
public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
/// <summary>
/// Gets the folder path to the data directory
/// Gets the folder path to the data directory.
/// </summary>
/// <value>The data directory.</value>
private string _dataPath;
public string DataPath
{
get => _dataPath;
@ -58,8 +60,9 @@ namespace Emby.Server.Implementations.AppBase
}
/// <summary>
/// Gets the magic strings used for virtual path manipulation.
/// Gets the magic string used for virtual path manipulation.
/// </summary>
/// <value>The magic string used for virtual path manipulation.</value>
public string VirtualDataPath { get; } = "%AppDataPath%";
/// <summary>
@ -69,43 +72,43 @@ namespace Emby.Server.Implementations.AppBase
public string ImageCachePath => Path.Combine(CachePath, "images");
/// <summary>
/// Gets the path to the plugin directory
/// Gets the path to the plugin directory.
/// </summary>
/// <value>The plugins path.</value>
public string PluginsPath => Path.Combine(ProgramDataPath, "plugins");
/// <summary>
/// Gets the path to the plugin configurations directory
/// Gets the path to the plugin configurations directory.
/// </summary>
/// <value>The plugin configurations path.</value>
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
/// <summary>
/// Gets the path to the log directory
/// Gets the path to the log directory.
/// </summary>
/// <value>The log directory path.</value>
public string LogDirectoryPath { get; private set; }
public string LogDirectoryPath { get; }
/// <summary>
/// Gets the path to the application configuration root directory
/// Gets the path to the application configuration root directory.
/// </summary>
/// <value>The configuration directory path.</value>
public string ConfigurationDirectoryPath { get; private set; }
public string ConfigurationDirectoryPath { get; }
/// <summary>
/// Gets the path to the system configuration file
/// Gets the path to the system configuration file.
/// </summary>
/// <value>The system configuration file path.</value>
public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
/// <summary>
/// Gets the folder path to the cache directory
/// Gets or sets the folder path to the cache directory.
/// </summary>
/// <value>The cache directory.</value>
public string CachePath { get; set; }
/// <summary>
/// Gets the folder path to the temp directory within the cache folder
/// Gets the folder path to the temp directory within the cache folder.
/// </summary>
/// <value>The temp directory.</value>
public string TempDirectory => Path.Combine(CachePath, "temp");

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -19,11 +20,44 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
public abstract class BaseConfigurationManager : IConfigurationManager
{
private readonly IFileSystem _fileSystem;
private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
private ConfigurationStore[] _configurationStores = Array.Empty<ConfigurationStore>();
private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>();
/// <summary>
/// Gets the type of the configuration.
/// The _configuration loaded.
/// </summary>
/// <value>The type of the configuration.</value>
protected abstract Type ConfigurationType { get; }
private bool _configurationLoaded;
/// <summary>
/// The _configuration sync lock.
/// </summary>
private object _configurationSyncLock = new object();
/// <summary>
/// The _configuration.
/// </summary>
private BaseApplicationConfiguration _configuration;
/// <summary>
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
/// </summary>
/// <param name="applicationPaths">The application paths.</param>
/// <param name="loggerFactory">The logger factory.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <param name="fileSystem">The file system</param>
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
{
CommonApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
Logger = loggerFactory.CreateLogger(GetType().Name);
UpdateCachePath();
}
/// <summary>
/// Occurs when [configuration updated].
@ -40,6 +74,12 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
/// <summary>
/// Gets the type of the configuration.
/// </summary>
/// <value>The type of the configuration.</value>
protected abstract Type ConfigurationType { get; }
/// <summary>
/// Gets the logger.
/// </summary>
@ -56,20 +96,7 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
/// <value>The application paths.</value>
public IApplicationPaths CommonApplicationPaths { get; private set; }
public readonly IFileSystem FileSystem;
/// <summary>
/// The _configuration loaded
/// </summary>
private bool _configurationLoaded;
/// <summary>
/// The _configuration sync lock
/// </summary>
private object _configurationSyncLock = new object();
/// <summary>
/// The _configuration
/// </summary>
private BaseApplicationConfiguration _configuration;
/// <summary>
/// Gets the system configuration
/// </summary>
@ -90,26 +117,6 @@ namespace Emby.Server.Implementations.AppBase
}
}
private ConfigurationStore[] _configurationStores = { };
private IConfigurationFactory[] _configurationFactories = { };
/// <summary>
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
/// </summary>
/// <param name="applicationPaths">The application paths.</param>
/// <param name="loggerFactory">The logger factory.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <param name="fileSystem">The file system</param>
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILoggerFactory loggerFactory, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
{
CommonApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
FileSystem = fileSystem;
Logger = loggerFactory.CreateLogger(GetType().Name);
UpdateCachePath();
}
public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
{
_configurationFactories = factories.ToArray();
@ -171,6 +178,7 @@ namespace Emby.Server.Implementations.AppBase
private void UpdateCachePath()
{
string cachePath;
// If the configuration file has no entry (i.e. not set in UI)
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
{
@ -207,12 +215,16 @@ namespace Emby.Server.Implementations.AppBase
var newPath = newConfig.CachePath;
if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath, StringComparison.Ordinal))
{
// Validate
if (!Directory.Exists(newPath))
{
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
throw new FileNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
"{0} does not exist.",
newPath));
}
EnsureWriteAccess(newPath);
@ -223,11 +235,9 @@ namespace Emby.Server.Implementations.AppBase
{
var file = Path.Combine(path, Guid.NewGuid().ToString());
File.WriteAllText(file, string.Empty);
FileSystem.DeleteFile(file);
_fileSystem.DeleteFile(file);
}
private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
private string GetConfigurationFile(string key)
{
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");

View file

@ -7,6 +7,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
@ -107,9 +108,9 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using ServiceStack;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
@ -385,7 +386,7 @@ namespace Emby.Server.Implementations
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
NetworkManager.NetworkChanged += NetworkManager_NetworkChanged;
NetworkManager.NetworkChanged += OnNetworkChanged;
}
public string ExpandVirtualPath(string path)
@ -409,7 +410,7 @@ namespace Emby.Server.Implementations
return ServerConfigurationManager.Configuration.LocalNetworkSubnets;
}
private void NetworkManager_NetworkChanged(object sender, EventArgs e)
private void OnNetworkChanged(object sender, EventArgs e)
{
_validAddressResults.Clear();
}
@ -417,10 +418,10 @@ namespace Emby.Server.Implementations
public string ApplicationVersion { get; } = typeof(ApplicationHost).Assembly.GetName().Version.ToString(3);
/// <summary>
/// Gets the current application user agent
/// Gets the current application user agent.
/// </summary>
/// <value>The application user agent.</value>
public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion;
public string ApplicationUserAgent => Name.Replace(' ', '-') + "/" + ApplicationVersion;
/// <summary>
/// Gets the email address for use within a comment section of a user agent field.
@ -428,14 +429,11 @@ namespace Emby.Server.Implementations
/// </summary>
public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org";
private string _productName;
/// <summary>
/// Gets the current application name
/// Gets the current application name.
/// </summary>
/// <value>The application name.</value>
public string ApplicationProductName
=> _productName ?? (_productName = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName);
public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName;
private DeviceId _deviceId;
@ -469,8 +467,8 @@ namespace Emby.Server.Implementations
/// <summary>
/// Creates an instance of type and resolves all constructor dependencies
/// </summary>
/// /// <typeparam name="T">The type</typeparam>
/// <returns>T</returns>
/// /// <typeparam name="T">The type.</typeparam>
/// <returns>T.</returns>
public T CreateInstance<T>()
=> ActivatorUtilities.CreateInstance<T>(_serviceProvider);
@ -603,10 +601,15 @@ namespace Emby.Server.Implementations
foreach (var plugin in Plugins)
{
pluginBuilder.AppendLine(string.Format("{0} {1}", plugin.Name, plugin.Version));
pluginBuilder.AppendLine(
string.Format(
CultureInfo.InvariantCulture,
"{0} {1}",
plugin.Name,
plugin.Version));
}
Logger.LogInformation("Plugins: {plugins}", pluginBuilder.ToString());
Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString());
}
DiscoverTypes();
@ -628,7 +631,7 @@ namespace Emby.Server.Implementations
if (EnableHttps && Certificate != null)
{
options.ListenAnyIP(HttpsPort, listenOptions => { listenOptions.UseHttps(Certificate); });
options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
}
})
.UseContentRoot(contentRoot)
@ -642,6 +645,7 @@ namespace Emby.Server.Implementations
app.UseWebSockets();
app.UseResponseCompression();
// TODO app.UseMiddleware<WebSocketMiddleware>();
app.Use(ExecuteWebsocketHandlerAsync);
app.Use(ExecuteHttpHandlerAsync);
@ -675,7 +679,7 @@ namespace Emby.Server.Implementations
var localPath = context.Request.Path.ToString();
var req = new WebSocketSharpRequest(request, response, request.Path, Logger);
await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false);
await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
}
public static IStreamHelper StreamHelper { get; set; }
@ -784,7 +788,7 @@ namespace Emby.Server.Implementations
HttpServer = new HttpListenerHost(
this,
LoggerFactory,
LoggerFactory.CreateLogger<HttpListenerHost>(),
ServerConfigurationManager,
_configuration,
NetworkManager,
@ -872,7 +876,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, SessionManager, NetworkManager);
AuthService = new AuthService(authContext, ServerConfigurationManager, SessionManager, NetworkManager);
serviceCollection.AddSingleton(AuthService);
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
@ -1043,8 +1047,8 @@ namespace Emby.Server.Implementations
.Cast<IServerEntryPoint>()
.ToList();
await Task.WhenAll(StartEntryPoints(entries, true));
await Task.WhenAll(StartEntryPoints(entries, false));
await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false);
await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false);
}
/// <summary>
@ -1219,7 +1223,7 @@ namespace Emby.Server.Implementations
// Generate self-signed cert
var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns);
var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx");
var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".pfx");
const string Password = "embycert";
return new CertificateInfo
@ -1457,15 +1461,10 @@ namespace Emby.Server.Implementations
};
}
public WakeOnLanInfo[] GetWakeOnLanInfo()
{
return NetworkManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo
{
MacAddress = i
})
.ToArray();
}
public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
=> NetworkManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i))
.ToList();
public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
{
@ -1481,6 +1480,7 @@ namespace Emby.Server.Implementations
{
wanAddress = GetWanApiUrl(ServerConfigurationManager.Configuration.WanDdns);
}
return new PublicSystemInfo
{
Version = ApplicationVersion,
@ -1546,14 +1546,32 @@ namespace Emby.Server.Implementations
return null;
}
public string GetLocalApiUrl(IpAddressInfo ipAddress)
/// <summary>
/// Removes the scope id from IPv6 addresses.
/// </summary>
/// <param name="address">The IPv6 address.</param>
/// <returns>The IPv6 address without the scope id.</returns>
private string RemoveScopeId(string address)
{
if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
var index = address.IndexOf('%');
if (index == -1)
{
return GetLocalApiUrl("[" + ipAddress.Address + "]");
return address;
}
return GetLocalApiUrl(ipAddress.Address);
return address.Substring(0, index);
}
public string GetLocalApiUrl(IPAddress ipAddress)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
var str = RemoveScopeId(ipAddress.ToString());
return GetLocalApiUrl("[" + str + "]");
}
return GetLocalApiUrl(ipAddress.ToString());
}
public string GetLocalApiUrl(string host)
@ -1564,19 +1582,22 @@ namespace Emby.Server.Implementations
host,
HttpsPort.ToString(CultureInfo.InvariantCulture));
}
return string.Format("http://{0}:{1}",
host,
HttpPort.ToString(CultureInfo.InvariantCulture));
}
public string GetWanApiUrl(IpAddressInfo ipAddress)
public string GetWanApiUrl(IPAddress ipAddress)
{
if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return GetWanApiUrl("[" + ipAddress.Address + "]");
var str = RemoveScopeId(ipAddress.ToString());
return GetWanApiUrl("[" + str + "]");
}
return GetWanApiUrl(ipAddress.Address);
return GetWanApiUrl(ipAddress.ToString());
}
public string GetWanApiUrl(string host)
@ -1587,17 +1608,18 @@ namespace Emby.Server.Implementations
host,
ServerConfigurationManager.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture));
}
return string.Format("http://{0}:{1}",
host,
ServerConfigurationManager.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture));
}
public Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken)
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
{
return GetLocalIpAddressesInternal(true, 0, cancellationToken);
}
private async Task<List<IpAddressInfo>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
{
var addresses = ServerConfigurationManager
.Configuration
@ -1611,13 +1633,13 @@ namespace Emby.Server.Implementations
addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
}
var resultList = new List<IpAddressInfo>();
var resultList = new List<IPAddress>();
foreach (var address in addresses)
{
if (!allowLoopback)
{
if (address.Equals(IpAddressInfo.Loopback) || address.Equals(IpAddressInfo.IPv6Loopback))
if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback))
{
continue;
}
@ -1638,7 +1660,7 @@ namespace Emby.Server.Implementations
return resultList;
}
private IpAddressInfo NormalizeConfiguredLocalAddress(string address)
private IPAddress NormalizeConfiguredLocalAddress(string address)
{
var index = address.Trim('/').IndexOf('/');
@ -1647,7 +1669,7 @@ namespace Emby.Server.Implementations
address = address.Substring(index + 1);
}
if (NetworkManager.TryParseIpAddress(address.Trim('/'), out IpAddressInfo result))
if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))
{
return result;
}
@ -1657,10 +1679,10 @@ namespace Emby.Server.Implementations
private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken)
private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
{
if (address.Equals(IpAddressInfo.Loopback) ||
address.Equals(IpAddressInfo.IPv6Loopback))
if (address.Equals(IPAddress.Loopback) ||
address.Equals(IPAddress.IPv6Loopback))
{
return true;
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -206,7 +207,7 @@ namespace Emby.Server.Implementations.Channels
try
{
return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N"));
return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture));
}
catch
{
@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.Channels
IncludeItemTypes = new[] { typeof(Channel).Name },
OrderBy = new ValueTuple<string, SortOrder>[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }
}).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray();
}).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
}
public ChannelFeatures GetChannelFeatures(string id)
@ -552,7 +553,7 @@ namespace Emby.Server.Implementations.Channels
SupportsSortOrderToggle = features.SupportsSortOrderToggle,
SupportsLatestMedia = supportsLatest,
Name = channel.Name,
Id = channel.Id.ToString("N"),
Id = channel.Id.ToString("N", CultureInfo.InvariantCulture),
SupportsContentDownloading = features.SupportsContentDownloading,
AutoRefreshLevels = features.AutoRefreshLevels
};
@ -740,7 +741,7 @@ namespace Emby.Server.Implementations.Channels
bool sortDescending,
CancellationToken cancellationToken)
{
var userId = user == null ? null : user.Id.ToString("N");
var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture);
var cacheLength = CacheLength;
var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
@ -836,7 +837,7 @@ namespace Emby.Server.Implementations.Channels
ChannelItemSortField? sortField,
bool sortDescending)
{
var channelId = GetInternalChannelId(channel.Name).ToString("N");
var channelId = GetInternalChannelId(channel.Name).ToString("N", CultureInfo.InvariantCulture);
var userCacheKey = string.Empty;
@ -846,10 +847,10 @@ namespace Emby.Server.Implementations.Channels
userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
}
var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N");
var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N", CultureInfo.InvariantCulture);
filename += userCacheKey;
var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N");
var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N", CultureInfo.InvariantCulture);
if (sortField.HasValue)
{
@ -860,7 +861,7 @@ namespace Emby.Server.Implementations.Channels
filename += "-sortDescending";
}
filename = filename.GetMD5().ToString("N");
filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture);
return Path.Combine(_config.ApplicationPaths.CachePath,
"channels",

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Collections
public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
{
AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)));
AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)));
}
private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)

View file

@ -6,8 +6,8 @@ namespace Emby.Server.Implementations
{
public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
{
{"HttpListenerHost:DefaultRedirectPath", "web/index.html"},
{"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"}
{ "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }
};
}
}

View file

@ -8,7 +8,7 @@ using MediaBrowser.Model.Cryptography;
namespace Emby.Server.Implementations.Cryptography
{
public class CryptographyProvider : ICryptoProvider
public class CryptographyProvider : ICryptoProvider, IDisposable
{
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
{
@ -28,26 +28,28 @@ namespace Emby.Server.Implementations.Cryptography
"System.Security.Cryptography.SHA512"
};
public string DefaultHashMethod => "PBKDF2";
private RandomNumberGenerator _randomNumberGenerator;
private const int _defaultIterations = 1000;
private bool _disposed = false;
public CryptographyProvider()
{
//FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
//Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
//there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
//Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1
// FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
// Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
// there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
// Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1
_randomNumberGenerator = RandomNumberGenerator.Create();
}
public Guid GetMD5(string str)
{
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
}
public string DefaultHashMethod => "PBKDF2";
[Obsolete("Use System.Security.Cryptography.MD5 directly")]
public Guid GetMD5(string str)
=> new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
[Obsolete("Use System.Security.Cryptography.SHA1 directly")]
public byte[] ComputeSHA1(byte[] bytes)
{
using (var provider = SHA1.Create())
@ -56,6 +58,7 @@ namespace Emby.Server.Implementations.Cryptography
}
}
[Obsolete("Use System.Security.Cryptography.MD5 directly")]
public byte[] ComputeMD5(Stream str)
{
using (var provider = MD5.Create())
@ -64,6 +67,7 @@ namespace Emby.Server.Implementations.Cryptography
}
}
[Obsolete("Use System.Security.Cryptography.MD5 directly")]
public byte[] ComputeMD5(byte[] bytes)
{
using (var provider = MD5.Create())
@ -73,9 +77,7 @@ namespace Emby.Server.Implementations.Cryptography
}
public IEnumerable<string> GetSupportedHashMethods()
{
return _supportedHashMethods;
}
=> _supportedHashMethods;
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
{
@ -93,14 +95,10 @@ namespace Emby.Server.Implementations.Cryptography
}
public byte[] ComputeHash(string hashMethod, byte[] bytes)
{
return ComputeHash(hashMethod, bytes, Array.Empty<byte>());
}
=> ComputeHash(hashMethod, bytes, Array.Empty<byte>());
public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
{
return ComputeHash(DefaultHashMethod, bytes);
}
=> ComputeHash(DefaultHashMethod, bytes);
public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
{
@ -125,37 +123,27 @@ namespace Emby.Server.Implementations.Cryptography
}
}
}
else
{
throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
}
throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
}
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
{
return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
}
=> PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
public byte[] ComputeHash(PasswordHash hash)
{
int iterations = _defaultIterations;
if (!hash.Parameters.ContainsKey("iterations"))
{
hash.Parameters.Add("iterations", _defaultIterations.ToString(CultureInfo.InvariantCulture));
hash.Parameters.Add("iterations", iterations.ToString(CultureInfo.InvariantCulture));
}
else
else if (!int.TryParse(hash.Parameters["iterations"], out iterations))
{
try
{
iterations = int.Parse(hash.Parameters["iterations"]);
}
catch (Exception e)
{
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e);
}
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}");
}
return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations);
return PBKDF2(hash.Id, hash.Hash, hash.Salt, iterations);
}
public byte[] GenerateSalt()
@ -164,5 +152,29 @@ namespace Emby.Server.Implementations.Cryptography
_randomNumberGenerator.GetBytes(salt);
return salt;
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_randomNumberGenerator.Dispose();
}
_randomNumberGenerator = null;
_disposed = true;
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Data
return new DisplayPreferences
{
Id = guidId.ToString("N")
Id = guidId.ToString("N", CultureInfo.InvariantCulture)
};
}
}

View file

@ -696,7 +696,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@EndDate");
}
saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N"));
saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture));
if (item is IHasProgramAttributes hasProgramAttributes)
{
@ -851,7 +851,7 @@ namespace Emby.Server.Implementations.Data
}
else
{
saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N"));
saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N", CultureInfo.InvariantCulture));
}
if (item is Trailer trailer && trailer.TrailerTypes.Length > 0)
@ -3548,12 +3548,12 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ChannelId=@ChannelId");
if (statement != null)
{
statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N"));
statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
}
}
else if (query.ChannelIds.Length > 1)
{
var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N") + "'"));
var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
whereClauses.Add($"ChannelId in ({inClause})");
}
@ -4537,12 +4537,12 @@ namespace Emby.Server.Implementations.Data
}
if (statement != null)
{
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N"));
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
}
}
else if (queryTopParentIds.Length > 1)
{
var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N") + "'"));
var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
if (enableItemsByName && includedItemByNameTypes.Count == 1)
{
@ -4574,7 +4574,7 @@ namespace Emby.Server.Implementations.Data
}
if (query.AncestorIds.Length > 1)
{
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N") + "'"));
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
}
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
@ -4637,7 +4637,7 @@ namespace Emby.Server.Implementations.Data
foreach (var folderId in query.BoxSetLibraryFolders)
{
folderIdQueries.Add("data like '%" + folderId.ToString("N") + "%'");
folderIdQueries.Add("data like '%" + folderId.ToString("N", CultureInfo.InvariantCulture) + "%'");
}
whereClauses.Add("(" + string.Join(" OR ", folderIdQueries) + ")");
@ -5161,7 +5161,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var ancestorId = ancestorIds[i];
statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob());
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N"));
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
}
statement.Reset();
@ -5579,6 +5579,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
counts.TrailerCount = value;
}
counts.ItemCount += value;
}

View file

@ -1,8 +1,8 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Devices
@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.Devices
private static string GetNewId()
{
return Guid.NewGuid().ToString("N");
return Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
}
private string GetDeviceId()

View file

@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Devices
private string GetDevicePath(string id)
{
return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N"));
return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N", CultureInfo.InvariantCulture));
}
public ContentUploadHistory GetCameraUploadHistory(string deviceId)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.DisplayPreferencesId))
{
dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N");
dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N", CultureInfo.InvariantCulture);
}
if (user != null)
@ -444,7 +445,7 @@ namespace Emby.Server.Implementations.Dto
/// <exception cref="ArgumentNullException">item</exception>
public string GetDtoId(BaseItem item)
{
return item.Id.ToString("N");
return item.Id.ToString("N", CultureInfo.InvariantCulture);
}
private static void SetBookProperties(BaseItemDto dto, Book item)
@ -608,7 +609,7 @@ namespace Emby.Server.Implementations.Dto
if (dictionary.TryGetValue(person.Name, out Person entity))
{
baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
baseItemPerson.Id = entity.Id.ToString("N");
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
list.Add(baseItemPerson);
}
}
@ -893,7 +894,7 @@ namespace Emby.Server.Implementations.Dto
//var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
//{
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N") }
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
//});
//dto.ArtistItems = artistItems.Items
@ -903,7 +904,7 @@ namespace Emby.Server.Implementations.Dto
// return new NameIdPair
// {
// Name = artist.Name,
// Id = artist.Id.ToString("N")
// Id = artist.Id.ToString("N", CultureInfo.InvariantCulture)
// };
// })
// .ToList();
@ -946,7 +947,7 @@ namespace Emby.Server.Implementations.Dto
//var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
//{
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N") }
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
//});
//dto.AlbumArtists = artistItems.Items
@ -956,7 +957,7 @@ namespace Emby.Server.Implementations.Dto
// return new NameIdPair
// {
// Name = artist.Name,
// Id = artist.Id.ToString("N")
// Id = artist.Id.ToString("N", CultureInfo.InvariantCulture)
// };
// })
// .ToList();
@ -1044,7 +1045,7 @@ namespace Emby.Server.Implementations.Dto
}
else
{
string id = item.Id.ToString("N");
string id = item.Id.ToString("N", CultureInfo.InvariantCulture);
mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.MediaStreams)
.ToArray();

View file

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.4.0.126" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
@ -43,6 +44,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

View file

@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.EntryPoints
_lastProgressMessageTimes[item.Id] = DateTime.UtcNow;
var dict = new Dictionary<string, string>();
dict["ItemId"] = item.Id.ToString("N");
dict["ItemId"] = item.Id.ToString("N", CultureInfo.InvariantCulture);
dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture);
try
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints
foreach (var collectionFolder in collectionFolders)
{
var collectionFolderDict = new Dictionary<string, string>();
collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N");
collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture);
collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture);
try
@ -378,15 +378,15 @@ namespace Emby.Server.Implementations.EntryPoints
return new LibraryUpdateInfo
{
ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(),
ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(),
ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(),
FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(),
FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture)).Distinct().ToArray(),
CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray()
};
@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.EntryPoints
var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren);
foreach (var folder in allUserRootChildren)
{
list.Add(folder.Id.ToString("N"));
list.Add(folder.Id.ToString("N", CultureInfo.InvariantCulture));
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="e">The e.</param>
void userManager_UserDeleted(object sender, GenericEventArgs<User> e)
{
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N"));
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture));
}
void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -8,7 +9,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@ -125,12 +125,12 @@ namespace Emby.Server.Implementations.EntryPoints
.Select(i =>
{
var dto = _userDataManager.GetUserDataDto(i, user);
dto.ItemId = i.Id.ToString("N");
dto.ItemId = i.Id.ToString("N", CultureInfo.InvariantCulture);
return dto;
})
.ToArray();
var userIdString = userId.ToString("N");
var userIdString = userId.ToString("N", CultureInfo.InvariantCulture);
return new UserDataChangeInfo
{

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@ -195,7 +196,7 @@ namespace Emby.Server.Implementations.HttpClientManager
}
var url = options.Url;
var urlHash = url.ToLowerInvariant().GetMD5().ToString("N");
var urlHash = url.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);

View file

@ -1,50 +1,43 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpServer
{
public class FileWriter : IHttpResult
{
private static readonly CultureInfo UsCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private static readonly string[] _skipLogExtensions = {
".js",
".html",
".css"
};
private readonly IStreamHelper _streamHelper;
private ILogger Logger { get; set; }
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private string RangeHeader { get; set; }
private bool IsHeadRequest { get; set; }
private long RangeStart { get; set; }
private long RangeEnd { get; set; }
private long RangeLength { get; set; }
public long TotalContentLength { get; set; }
public Action OnComplete { get; set; }
public Action OnError { get; set; }
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
public List<Cookie> Cookies { get; private set; }
public FileShareMode FileShare { get; set; }
/// <summary>
/// The _options
/// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
/// <summary>
/// Gets the options.
/// </summary>
/// <value>The options.</value>
public IDictionary<string, string> Headers => _options;
public string Path { get; set; }
/// <summary>
/// The _requested ranges
/// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges;
public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem, IStreamHelper streamHelper)
{
@ -57,7 +50,7 @@ namespace Emby.Server.Implementations.HttpServer
_fileSystem = fileSystem;
Path = path;
Logger = logger;
_logger = logger;
RangeHeader = rangeHeader;
Headers[HeaderNames.ContentType] = contentType;
@ -80,6 +73,88 @@ namespace Emby.Server.Implementations.HttpServer
Cookies = new List<Cookie>();
}
private string RangeHeader { get; set; }
private bool IsHeadRequest { get; set; }
private long RangeStart { get; set; }
private long RangeEnd { get; set; }
private long RangeLength { get; set; }
public long TotalContentLength { get; set; }
public Action OnComplete { get; set; }
public Action OnError { get; set; }
public List<Cookie> Cookies { get; private set; }
public FileShareMode FileShare { get; set; }
/// <summary>
/// Gets the options.
/// </summary>
/// <value>The options.</value>
public IDictionary<string, string> Headers => _options;
public string Path { get; set; }
/// <summary>
/// Gets the requested ranges.
/// </summary>
/// <value>The requested ranges.</value>
protected List<KeyValuePair<long, long?>> RequestedRanges
{
get
{
if (_requestedRanges == null)
{
_requestedRanges = new List<KeyValuePair<long, long?>>();
// Example: bytes=0-,32-63
var ranges = RangeHeader.Split('=')[1].Split(',');
foreach (var range in ranges)
{
var vals = range.Split('-');
long start = 0;
long? end = null;
if (!string.IsNullOrEmpty(vals[0]))
{
start = long.Parse(vals[0], UsCulture);
}
if (!string.IsNullOrEmpty(vals[1]))
{
end = long.Parse(vals[1], UsCulture);
}
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
}
}
return _requestedRanges;
}
}
public string ContentType { get; set; }
public IRequest RequestContext { get; set; }
public object Response { get; set; }
public int Status { get; set; }
public HttpStatusCode StatusCode
{
get => (HttpStatusCode)Status;
set => Status = (int)value;
}
/// <summary>
/// Sets the range values.
/// </summary>
@ -106,59 +181,10 @@ namespace Emby.Server.Implementations.HttpServer
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
Headers[HeaderNames.ContentRange] = rangeString;
Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
_logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
}
/// <summary>
/// The _requested ranges
/// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges;
/// <summary>
/// Gets the requested ranges.
/// </summary>
/// <value>The requested ranges.</value>
protected List<KeyValuePair<long, long?>> RequestedRanges
{
get
{
if (_requestedRanges == null)
{
_requestedRanges = new List<KeyValuePair<long, long?>>();
// Example: bytes=0-,32-63
var ranges = RangeHeader.Split('=')[1].Split(',');
foreach (var range in ranges)
{
var vals = range.Split('-');
long start = 0;
long? end = null;
if (!string.IsNullOrEmpty(vals[0]))
{
start = long.Parse(vals[0], UsCulture);
}
if (!string.IsNullOrEmpty(vals[1]))
{
end = long.Parse(vals[1], UsCulture);
}
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
}
}
return _requestedRanges;
}
}
private static readonly string[] SkipLogExtensions = {
".js",
".html",
".css"
};
public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken)
public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken)
{
try
{
@ -176,16 +202,16 @@ namespace Emby.Server.Implementations.HttpServer
{
var extension = System.IO.Path.GetExtension(path);
if (extension == null || !SkipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
if (extension == null || !_skipLogExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
Logger.LogDebug("Transmit file {0}", path);
_logger.LogDebug("Transmit file {0}", path);
}
offset = 0;
count = 0;
}
await response.TransmitFile(path, offset, count, FileShare, _fileSystem, _streamHelper, cancellationToken).ConfigureAwait(false);
await TransmitFile(response.Body, path, offset, count, FileShare, cancellationToken).ConfigureAwait(false);
}
finally
{
@ -193,18 +219,32 @@ namespace Emby.Server.Implementations.HttpServer
}
}
public string ContentType { get; set; }
public IRequest RequestContext { get; set; }
public object Response { get; set; }
public int Status { get; set; }
public HttpStatusCode StatusCode
public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
get => (HttpStatusCode)Status;
set => Status = (int)value;
var fileOpenOptions = FileOpenOptions.SequentialScan;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileOpenOptions |= FileOpenOptions.Asynchronous;
}
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
{
if (offset > 0)
{
fs.Position = offset;
}
if (count > 0)
{
await _streamHelper.CopyToAsync(fs, stream, count, cancellationToken).ConfigureAwait(false);
}
else
{
await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
}
}
}

View file

@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Net;
@ -30,11 +29,7 @@ namespace Emby.Server.Implementations.HttpServer
{
public class HttpListenerHost : IHttpServer, IDisposable
{
private string DefaultRedirectPath { get; set; }
public string[] UrlPrefixes { get; private set; }
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
private readonly IServerApplicationHost _appHost;
@ -42,18 +37,15 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IXmlSerializer _xmlSerializer;
private readonly IHttpListener _socketListener;
private readonly Func<Type, Func<string, object>> _funcParseFn;
public Action<IRequest, IResponse, object>[] ResponseFilters { get; set; }
private readonly string _defaultRedirectPath;
private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
public static HttpListenerHost Instance { get; protected set; }
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
private bool _disposed = false;
public HttpListenerHost(
IServerApplicationHost applicationHost,
ILoggerFactory loggerFactory,
ILogger<HttpListenerHost> logger,
IServerConfigurationManager config,
IConfiguration configuration,
INetworkManager networkManager,
@ -62,9 +54,9 @@ namespace Emby.Server.Implementations.HttpServer
IHttpListener socketListener)
{
_appHost = applicationHost;
Logger = loggerFactory.CreateLogger("HttpServer");
_logger = logger;
_config = config;
DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
@ -74,12 +66,20 @@ namespace Emby.Server.Implementations.HttpServer
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();
ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
}
public Action<IRequest, HttpResponse, object>[] ResponseFilters { get; set; }
public static HttpListenerHost Instance { get; protected set; }
public string[] UrlPrefixes { get; private set; }
public string GlobalResponse { get; set; }
protected ILogger Logger { get; }
public ServiceController ServiceController { get; private set; }
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
public object CreateInstance(Type type)
{
@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer
/// and no more processing should be done.
/// </summary>
/// <returns></returns>
public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
public void ApplyRequestFilters(IRequest req, HttpResponse res, object requestDto)
{
//Exec all RequestFilter attributes with Priority < 0
var attributes = GetRequestFilterAttributes(requestDto.GetType());
@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, Logger)
var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger)
{
OnReceive = ProcessWebSocketMessageReceived,
Url = e.Url,
@ -215,16 +215,16 @@ namespace Emby.Server.Implementations.HttpServer
if (logExceptionStackTrace)
{
Logger.LogError(ex, "Error processing request");
_logger.LogError(ex, "Error processing request");
}
else if (logExceptionMessage)
{
Logger.LogError(ex.Message);
_logger.LogError(ex.Message);
}
var httpRes = httpReq.Response;
if (httpRes.OriginalResponse.HasStarted)
if (httpRes.HasStarted)
{
return;
}
@ -233,11 +233,11 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.StatusCode = statusCode;
httpRes.ContentType = "text/html";
await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
}
catch (Exception errorEx)
{
Logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
_logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
}
}
@ -431,7 +431,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 503;
httpRes.ContentType = "text/plain";
await Write(httpRes, "Server shutting down").ConfigureAwait(false);
await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false);
return;
}
@ -439,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 400;
httpRes.ContentType = "text/plain";
await Write(httpRes, "Invalid host").ConfigureAwait(false);
await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false);
return;
}
@ -447,7 +447,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 403;
httpRes.ContentType = "text/plain";
await Write(httpRes, "Forbidden").ConfigureAwait(false);
await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false);
return;
}
@ -460,28 +460,27 @@ namespace Emby.Server.Implementations.HttpServer
if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
{
httpRes.StatusCode = 200;
httpRes.AddHeader("Access-Control-Allow-Origin", "*");
httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
httpRes.Headers.Add("Access-Control-Allow-Origin", "*");
httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
httpRes.ContentType = "text/plain";
await Write(httpRes, string.Empty).ConfigureAwait(false);
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
return;
}
urlToLog = GetUrlToLog(urlString);
Logger.LogDebug("HTTP {HttpMethod} {Url} UserAgent: {UserAgent} \nHeaders: {@Headers}", urlToLog, httpReq.UserAgent ?? string.Empty, httpReq.HttpMethod, httpReq.Headers);
if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, DefaultRedirectPath);
httpRes.Redirect(_defaultRedirectPath);
return;
}
if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath);
httpRes.Redirect("emby/" + _defaultRedirectPath);
return;
}
@ -494,9 +493,10 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{
await Write(httpRes,
await httpRes.WriteAsync(
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
newUrl + "\">" + newUrl + "</a></body></html>",
cancellationToken).ConfigureAwait(false);
return;
}
}
@ -511,34 +511,35 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{
await Write(httpRes,
await httpRes.WriteAsync(
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
newUrl + "\">" + newUrl + "</a></body></html>").ConfigureAwait(false);
newUrl + "\">" + newUrl + "</a></body></html>",
cancellationToken).ConfigureAwait(false);
return;
}
}
if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, DefaultRedirectPath);
httpRes.Redirect(_defaultRedirectPath);
return;
}
if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, "../" + DefaultRedirectPath);
httpRes.Redirect("../" + _defaultRedirectPath);
return;
}
if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, DefaultRedirectPath);
httpRes.Redirect(_defaultRedirectPath);
return;
}
if (string.IsNullOrEmpty(localPath))
{
RedirectToUrl(httpRes, "/" + DefaultRedirectPath);
httpRes.Redirect("/" + _defaultRedirectPath);
return;
}
@ -546,12 +547,12 @@ namespace Emby.Server.Implementations.HttpServer
{
if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, "index.html#!/dashboard.html");
httpRes.Redirect("index.html#!/dashboard.html");
}
if (localPath.EndsWith("web/home.html", StringComparison.OrdinalIgnoreCase))
{
RedirectToUrl(httpRes, "index.html");
httpRes.Redirect("index.html");
}
}
@ -562,7 +563,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.StatusCode = 503;
httpRes.ContentType = "text/html";
await Write(httpRes, GlobalResponse).ConfigureAwait(false);
await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false);
return;
}
}
@ -571,7 +572,7 @@ namespace Emby.Server.Implementations.HttpServer
if (handler != null)
{
await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, cancellationToken).ConfigureAwait(false);
await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false);
}
else
{
@ -598,11 +599,7 @@ namespace Emby.Server.Implementations.HttpServer
var elapsed = stopWatch.Elapsed;
if (elapsed.TotalMilliseconds > 500)
{
Logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
}
else
{
Logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
_logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
}
}
}
@ -619,18 +616,11 @@ namespace Emby.Server.Implementations.HttpServer
return new ServiceHandler(restPath, contentType);
}
Logger.LogError("Could not find handler for {PathInfo}", pathInfo);
_logger.LogError("Could not find handler for {PathInfo}", pathInfo);
return null;
}
private static Task Write(IResponse response, string text)
{
var bOutput = Encoding.UTF8.GetBytes(text);
response.OriginalResponse.ContentLength = bOutput.Length;
return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
}
private void RedirectToSecureUrl(IHttpRequest httpReq, IResponse httpRes, string url)
private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url)
{
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
{
@ -640,23 +630,11 @@ namespace Emby.Server.Implementations.HttpServer
Scheme = "https"
};
url = builder.Uri.ToString();
RedirectToUrl(httpRes, url);
}
else
{
RedirectToUrl(httpRes, url);
}
}
public static void RedirectToUrl(IResponse httpRes, string url)
{
httpRes.StatusCode = 302;
httpRes.AddHeader("Location", url);
httpRes.Redirect(url);
}
public ServiceController ServiceController { get; private set; }
/// <summary>
/// Adds the rest handlers.
/// </summary>
@ -672,9 +650,9 @@ namespace Emby.Server.Implementations.HttpServer
var types = services.Select(r => r.GetType());
ServiceController.Init(this, types);
ResponseFilters = new Action<IRequest, IResponse, object>[]
ResponseFilters = new Action<IRequest, HttpResponse, object>[]
{
new ResponseFilter(Logger).FilterResponse
new ResponseFilter(_logger).FilterResponse
};
}
@ -772,24 +750,23 @@ namespace Emby.Server.Implementations.HttpServer
return "emby/emby/" + path;
}
private bool _disposed;
private readonly object _disposeLock = new object();
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
lock (_disposeLock)
if (disposing)
{
if (_disposed) return;
_disposed = true;
if (disposing)
{
Stop();
}
Stop();
}
_disposed = true;
}
/// <summary>
@ -803,7 +780,7 @@ namespace Emby.Server.Implementations.HttpServer
return Task.CompletedTask;
}
Logger.LogDebug("Websocket message received: {0}", result.MessageType);
_logger.LogDebug("Websocket message received: {0}", result.MessageType);
IEnumerable<Task> GetTasks()
{
@ -815,10 +792,5 @@ namespace Emby.Server.Implementations.HttpServer
return Task.WhenAll(GetTasks());
}
public void Dispose()
{
Dispose(true);
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.Globalization;
using System.Text;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@ -9,7 +10,7 @@ namespace Emby.Server.Implementations.HttpServer
{
public class ResponseFilter
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private readonly ILogger _logger;
public ResponseFilter(ILogger logger)
@ -23,12 +24,12 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="req">The req.</param>
/// <param name="res">The res.</param>
/// <param name="dto">The dto.</param>
public void FilterResponse(IRequest req, IResponse res, object dto)
public void FilterResponse(IRequest req, HttpResponse res, object dto)
{
// Try to prevent compatibility view
res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
res.AddHeader("Access-Control-Allow-Origin", "*");
res.Headers.Add("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
res.Headers.Add("Access-Control-Allow-Origin", "*");
if (dto is Exception exception)
{
@ -39,7 +40,7 @@ namespace Emby.Server.Implementations.HttpServer
var error = exception.Message.Replace(Environment.NewLine, " ");
error = RemoveControlCharacters(error);
res.AddHeader("X-Application-Error-Code", error);
res.Headers.Add("X-Application-Error-Code", error);
}
}
@ -54,12 +55,11 @@ namespace Emby.Server.Implementations.HttpServer
if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
&& !string.IsNullOrEmpty(contentLength))
{
var length = long.Parse(contentLength, UsCulture);
var length = long.Parse(contentLength, _usCulture);
if (length > 0)
{
res.OriginalResponse.ContentLength = length;
res.SendChunked = false;
res.ContentLength = length;
}
}
}
@ -72,9 +72,12 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns>System.String.</returns>
public static string RemoveControlCharacters(string inString)
{
if (inString == null) return null;
if (inString == null)
{
return null;
}
var newString = new StringBuilder();
var newString = new StringBuilder(inString.Length);
foreach (var ch in inString)
{
@ -83,6 +86,7 @@ namespace Emby.Server.Implementations.HttpServer
newString.Append(ch);
}
}
return newString.ToString();
}
}

View file

@ -3,7 +3,6 @@ using System.Linq;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
@ -13,28 +12,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthService : IAuthService
{
private readonly IAuthorizationContext _authorizationContext;
private readonly ISessionManager _sessionManager;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
public AuthService(IUserManager userManager, IAuthorizationContext authorizationContext, IServerConfigurationManager config, ISessionManager sessionManager, INetworkManager networkManager)
public AuthService(
IAuthorizationContext authorizationContext,
IServerConfigurationManager config,
ISessionManager sessionManager,
INetworkManager networkManager)
{
AuthorizationContext = authorizationContext;
_authorizationContext = authorizationContext;
_config = config;
SessionManager = sessionManager;
UserManager = userManager;
NetworkManager = networkManager;
_sessionManager = sessionManager;
_networkManager = networkManager;
}
public IUserManager UserManager { get; private set; }
public IAuthorizationContext AuthorizationContext { get; private set; }
public ISessionManager SessionManager { get; private set; }
public INetworkManager NetworkManager { get; private set; }
/// <summary>
/// Redirect the client to a specific URL if authentication failed.
/// If this property is null, simply `401 Unauthorized` is returned.
/// </summary>
public string HtmlRedirect { get; set; }
public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
{
ValidateUser(request, authAttribtues);
@ -43,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
{
// This code is executed before the service
var auth = AuthorizationContext.GetAuthorizationInfo(request);
var auth = _authorizationContext.GetAuthorizationInfo(request);
if (!IsExemptFromAuthenticationToken(authAttribtues, request))
{
@ -80,7 +74,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device))
{
SessionManager.LogSessionActivity(auth.Client,
_sessionManager.LogSessionActivity(auth.Client,
auth.Version,
auth.DeviceId,
auth.Device,
@ -89,7 +83,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
}
}
private void ValidateUserAccess(User user, IRequest request,
private void ValidateUserAccess(
User user,
IRequest request,
IAuthenticationAttributes authAttribtues,
AuthorizationInfo auth)
{
@ -101,7 +97,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
};
}
if (!user.Policy.EnableRemoteAccess && !NetworkManager.IsInLocalNetwork(request.RemoteIp))
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
throw new SecurityException("User account has been disabled.")
{
@ -109,11 +105,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
};
}
if (!user.Policy.IsAdministrator &&
!authAttribtues.EscapeParentalControl &&
!user.IsParentalScheduleAllowed())
if (!user.Policy.IsAdministrator
&& !authAttribtues.EscapeParentalControl
&& !user.IsParentalScheduleAllowed())
{
request.Response.AddHeader("X-Application-Error-Code", "ParentalControl");
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
throw new SecurityException("This user account is not allowed access at this time.")
{
@ -183,6 +179,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
};
}
}
if (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{
if (user == null || !user.Policy.EnableContentDeletion)
@ -193,6 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
};
}
}
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{
if (user == null || !user.Policy.EnableContentDownloading)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -555,7 +556,7 @@ namespace Emby.Server.Implementations.IO
throw new ArgumentNullException(nameof(file2));
}
var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N"));
var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
// Copying over will fail against hidden files
SetHidden(file1, false);

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.Images
ImageType imageType,
CancellationToken cancellationToken)
{
var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
Directory.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension));
string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0);

View file

@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library
@ -17,12 +16,10 @@ namespace Emby.Server.Implementations.Library
{
private readonly ILibraryManager _libraryManager;
private bool _ignoreDotPrefix;
/// <summary>
/// Any folder named in this list will be ignored - can be added to at runtime for extensibility
/// Any folder named in this list will be ignored
/// </summary>
public static readonly string[] IgnoreFolders =
private static readonly string[] _ignoreFolders =
{
"metadata",
"ps3_update",
@ -43,25 +40,14 @@ namespace Emby.Server.Implementations.Library
"$RECYCLE.BIN",
"System Volume Information",
".grab",
// macos
".AppleDouble"
};
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
_ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
}
/// <summary>
/// Shoulds the ignore.
/// </summary>
/// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
/// <inheritdoc />
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
{
// Don't ignore top level folders
@ -73,46 +59,17 @@ namespace Emby.Server.Implementations.Library
var filename = fileInfo.Name;
var path = fileInfo.FullName;
// Handle mac .DS_Store
// https://github.com/MediaBrowser/MediaBrowser/issues/427
if (_ignoreDotPrefix)
// Ignore hidden files on UNIX
if (Environment.OSVersion.Platform != PlatformID.Win32NT
&& filename[0] == '.')
{
if (filename.IndexOf('.') == 0)
{
return true;
}
return true;
}
// Ignore hidden files and folders
//if (fileInfo.IsHidden)
//{
// if (parent == null)
// {
// var parentFolderName = Path.GetFileName(_fileSystem.GetDirectoryName(path));
// if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
// {
// return false;
// }
// if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
// {
// return false;
// }
// }
// // Sometimes these are marked hidden
// if (_fileSystem.IsRootPath(path))
// {
// return false;
// }
// return true;
//}
if (fileInfo.IsDirectory)
{
// Ignore any folders in our list
if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return true;
}
@ -120,8 +77,9 @@ namespace Emby.Server.Implementations.Library
if (parent != null)
{
// Ignore trailer folders but allow it at the collection level
if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) &&
!(parent is AggregateFolder) && !(parent is UserRootFolder))
if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)
&& !(parent is AggregateFolder)
&& !(parent is UserRootFolder))
{
return true;
}
@ -142,14 +100,15 @@ namespace Emby.Server.Implementations.Library
if (parent != null)
{
// Don't resolve these into audio files
if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename))
if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename)
&& _libraryManager.IsAudioFile(filename))
{
return true;
}
}
// Ignore samples
Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase);
Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase);
return m.Success;
}

View file

@ -11,9 +11,9 @@ namespace Emby.Server.Implementations.Library
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{
private readonly ICryptoProvider _cryptographyProvider;
public DefaultAuthenticationProvider(ICryptoProvider crypto)
public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
{
_cryptographyProvider = crypto;
_cryptographyProvider = cryptographyProvider;
}
public string Name => "Default";
@ -28,17 +28,17 @@ namespace Emby.Server.Implementations.Library
throw new NotImplementedException();
}
// This is the verson that we need to use for local users. Because reasons.
// This is the version that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{
bool success = false;
if (resolvedUser == null)
{
throw new Exception("Invalid username or password");
throw new ArgumentNullException(nameof(resolvedUser));
}
// As long as jellyfin supports passwordless users, we need this little block here to accomodate
if (IsPasswordEmpty(resolvedUser, password))
if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password))
{
return Task.FromResult(new ProviderAuthenticationResult
{
@ -50,37 +50,24 @@ namespace Emby.Server.Implementations.Library
byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
PasswordHash readyHash = new PasswordHash(resolvedUser.Password);
byte[] calculatedHash;
string calculatedHashString;
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) || _cryptographyProvider.DefaultHashMethod == readyHash.Id)
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
|| _cryptographyProvider.DefaultHashMethod == readyHash.Id)
{
if (string.IsNullOrEmpty(readyHash.Salt))
{
calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes);
calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty);
}
else
{
calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.SaltBytes);
calculatedHashString = BitConverter.ToString(calculatedHash).Replace("-", string.Empty);
}
byte[] calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, readyHash.Salt);
if (calculatedHashString == readyHash.Hash)
if (calculatedHash.SequenceEqual(readyHash.Hash))
{
success = true;
// throw new Exception("Invalid username or password");
}
}
else
{
throw new Exception(string.Format($"Requested crypto method not available in provider: {readyHash.Id}"));
throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}");
}
// var success = string.Equals(GetPasswordHash(resolvedUser), GetHashedString(resolvedUser, password), StringComparison.OrdinalIgnoreCase);
if (!success)
{
throw new Exception("Invalid username or password");
throw new AuthenticationException("Invalid username or password");
}
return Task.FromResult(new ProviderAuthenticationResult
@ -98,29 +85,22 @@ namespace Emby.Server.Implementations.Library
return;
}
if (!user.Password.Contains("$"))
if (user.Password.IndexOf('$') == -1)
{
string hash = user.Password;
user.Password = string.Format("$SHA1${0}", hash);
}
if (user.EasyPassword != null && !user.EasyPassword.Contains("$"))
if (user.EasyPassword != null
&& user.EasyPassword.IndexOf('$') == -1)
{
string hash = user.EasyPassword;
user.EasyPassword = string.Format("$SHA1${0}", hash);
}
}
public Task<bool> HasPassword(User user)
{
var hasConfiguredPassword = !IsPasswordEmpty(user, GetPasswordHash(user));
return Task.FromResult(hasConfiguredPassword);
}
private bool IsPasswordEmpty(User user, string password)
{
return (string.IsNullOrEmpty(user.Password) && string.IsNullOrEmpty(password));
}
public bool HasPassword(User user)
=> !string.IsNullOrEmpty(user.Password);
public Task ChangePassword(User user, string newPassword)
{
@ -129,30 +109,24 @@ namespace Emby.Server.Implementations.Library
if (string.IsNullOrEmpty(user.Password))
{
PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt();
newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes);
newPasswordHash.Salt = _cryptographyProvider.GenerateSalt();
newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash);
newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash);
user.Password = newPasswordHash.ToString();
return Task.CompletedTask;
}
PasswordHash passwordHash = new PasswordHash(user.Password);
if (passwordHash.Id == "SHA1" && string.IsNullOrEmpty(passwordHash.Salt))
if (passwordHash.Id == "SHA1"
&& passwordHash.Salt.Length == 0)
{
passwordHash.SaltBytes = _cryptographyProvider.GenerateSalt();
passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes);
passwordHash.Salt = _cryptographyProvider.GenerateSalt();
passwordHash.Id = _cryptographyProvider.DefaultHashMethod;
passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash);
passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash);
}
else if (newPassword != null)
{
passwordHash.Hash = GetHashedString(user, newPassword);
}
if (string.IsNullOrWhiteSpace(passwordHash.Hash))
{
throw new ArgumentNullException(nameof(passwordHash.Hash));
passwordHash.Hash = GetHashed(user, newPassword);
}
user.Password = passwordHash.ToString();
@ -160,11 +134,6 @@ namespace Emby.Server.Implementations.Library
return Task.CompletedTask;
}
public string GetPasswordHash(User user)
{
return user.Password;
}
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{
ConvertPasswordFormat(user);
@ -190,13 +159,13 @@ namespace Emby.Server.Implementations.Library
return string.IsNullOrEmpty(user.EasyPassword)
? null
: (new PasswordHash(user.EasyPassword)).Hash;
: PasswordHash.ConvertToByteString(new PasswordHash(user.EasyPassword).Hash);
}
public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash)
internal byte[] GetHashedChangeAuth(string newPassword, PasswordHash passwordHash)
{
passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword);
return _cryptographyProvider.ComputeHash(passwordHash);
}
/// <summary>
@ -215,10 +184,10 @@ namespace Emby.Server.Implementations.Library
passwordHash = new PasswordHash(user.Password);
}
if (passwordHash.SaltBytes != null)
if (passwordHash.Salt != null)
{
// the password is modern format with PBKDF and we should take advantage of that
passwordHash.HashBytes = Encoding.UTF8.GetBytes(str);
passwordHash.Hash = Encoding.UTF8.GetBytes(str);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
}
else
@ -227,5 +196,31 @@ namespace Emby.Server.Implementations.Library
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str)));
}
}
public byte[] GetHashed(User user, string str)
{
PasswordHash passwordHash;
if (string.IsNullOrEmpty(user.Password))
{
passwordHash = new PasswordHash(_cryptographyProvider);
}
else
{
ConvertPasswordFormat(user);
passwordHash = new PasswordHash(user.Password);
}
if (passwordHash.Salt != null)
{
// the password is modern format with PBKDF and we should take advantage of that
passwordHash.Hash = Encoding.UTF8.GetBytes(str);
return _cryptographyProvider.ComputeHash(passwordHash);
}
else
{
// the password has no salt and should be called with the older method for safety
return _cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str));
}
}
}
}

View file

@ -1,132 +1,125 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Library
{
public class DefaultPasswordResetProvider : IPasswordResetProvider
{
public string Name => "Default Password Reset Provider";
public bool IsEnabled => true;
private readonly string _passwordResetFileBase;
private readonly string _passwordResetFileBaseDir;
private readonly string _passwordResetFileBaseName = "passwordreset";
private readonly IJsonSerializer _jsonSerializer;
private readonly IUserManager _userManager;
private readonly ICryptoProvider _crypto;
public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider)
{
_passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath;
_passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName);
_jsonSerializer = jsonSerializer;
_userManager = userManager;
_crypto = cryptoProvider;
}
public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
SerializablePasswordReset spr;
HashSet<string> usersreset = new HashSet<string>();
foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
{
using (var str = File.OpenRead(resetfile))
{
spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
}
if (spr.ExpirationDate < DateTime.Now)
{
File.Delete(resetfile);
}
else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase))
{
var resetUser = _userManager.GetUserByName(spr.UserName);
if (resetUser == null)
{
throw new Exception($"User with a username of {spr.UserName} not found");
}
await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
usersreset.Add(resetUser.Name);
File.Delete(resetfile);
}
}
if (usersreset.Count < 1)
{
throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
}
else
{
return new PinRedeemResult
{
Success = true,
UsersReset = usersreset.ToArray()
};
}
}
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
{
string pin = string.Empty;
using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create())
{
byte[] bytes = new byte[4];
cryptoRandom.GetBytes(bytes);
pin = BitConverter.ToString(bytes);
}
DateTime expireTime = DateTime.Now.AddMinutes(30);
string filePath = _passwordResetFileBase + user.InternalId + ".json";
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = filePath,
UserName = user.Name
};
try
{
using (FileStream fileStream = File.OpenWrite(filePath))
{
_jsonSerializer.SerializeToStream(spr, fileStream);
await fileStream.FlushAsync().ConfigureAwait(false);
}
}
catch (Exception e)
{
throw new Exception($"Error serializing or writing password reset for {user.Name} to location: {filePath}", e);
}
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime,
PinFile = filePath
};
}
private class SerializablePasswordReset : PasswordPinCreationResult
{
public string Pin { get; set; }
public string UserName { get; set; }
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Library
{
public class DefaultPasswordResetProvider : IPasswordResetProvider
{
public string Name => "Default Password Reset Provider";
public bool IsEnabled => true;
private readonly string _passwordResetFileBase;
private readonly string _passwordResetFileBaseDir;
private readonly string _passwordResetFileBaseName = "passwordreset";
private readonly IJsonSerializer _jsonSerializer;
private readonly IUserManager _userManager;
private readonly ICryptoProvider _crypto;
public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider)
{
_passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath;
_passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName);
_jsonSerializer = jsonSerializer;
_userManager = userManager;
_crypto = cryptoProvider;
}
public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
SerializablePasswordReset spr;
HashSet<string> usersreset = new HashSet<string>();
foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
{
using (var str = File.OpenRead(resetfile))
{
spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
}
if (spr.ExpirationDate < DateTime.Now)
{
File.Delete(resetfile);
}
else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase))
{
var resetUser = _userManager.GetUserByName(spr.UserName);
if (resetUser == null)
{
throw new Exception($"User with a username of {spr.UserName} not found");
}
await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
usersreset.Add(resetUser.Name);
File.Delete(resetfile);
}
}
if (usersreset.Count < 1)
{
throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
}
else
{
return new PinRedeemResult
{
Success = true,
UsersReset = usersreset.ToArray()
};
}
}
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
{
string pin = string.Empty;
using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create())
{
byte[] bytes = new byte[4];
cryptoRandom.GetBytes(bytes);
pin = BitConverter.ToString(bytes);
}
DateTime expireTime = DateTime.Now.AddMinutes(30);
string filePath = _passwordResetFileBase + user.InternalId + ".json";
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = filePath,
UserName = user.Name
};
using (FileStream fileStream = File.OpenWrite(filePath))
{
_jsonSerializer.SerializeToStream(spr, fileStream);
await fileStream.FlushAsync().ConfigureAwait(false);
}
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime,
PinFile = filePath
};
}
private class SerializablePasswordReset : PasswordPinCreationResult
{
public string Pin { get; set; }
public string UserName { get; set; }
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.Library
EnableStreamSharing = false;
_closeFn = closeFn;
ConsumerCount = 1;
UniqueId = Guid.NewGuid().ToString("N");
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
}
public Task Close()

View file

@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities;
@ -16,12 +13,12 @@ namespace Emby.Server.Implementations.Library
public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
{
throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
}
public Task<bool> HasPassword(User user)
public bool HasPassword(User user)
{
return Task.FromResult(true);
return true;
}
public Task ChangePassword(User user, string newPassword)
@ -31,7 +28,7 @@ namespace Emby.Server.Implementations.Library
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{
// Nothing here
// Nothing here
}
public string GetPasswordHash(User user)

View file

@ -1187,12 +1187,12 @@ namespace Emby.Server.Implementations.Library
if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
{
info.PrimaryImageItemId = libraryFolder.Id.ToString("N");
info.PrimaryImageItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
}
if (libraryFolder != null)
{
info.ItemId = libraryFolder.Id.ToString("N");
info.ItemId = libraryFolder.Id.ToString("N", CultureInfo.InvariantCulture);
info.LibraryOptions = GetLibraryOptions(libraryFolder);
if (refreshQueue != null)
@ -2135,12 +2135,12 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N");
var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
var id = GetNewItemId(idValues, typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView;
@ -2271,7 +2271,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(name));
}
var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N");
var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
if (!string.IsNullOrEmpty(uniqueId))
{
@ -2280,7 +2280,7 @@ namespace Emby.Server.Implementations.Library
var id = GetNewItemId(idValues, typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView;

View file

@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library
var now = DateTime.UtcNow;
MediaInfo mediaInfo = null;
var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json");
var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json");
if (!string.IsNullOrEmpty(cacheKey))
{

View file

@ -269,7 +269,7 @@ namespace Emby.Server.Implementations.Library
private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter;
var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimeter;
if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
@ -626,7 +626,7 @@ namespace Emby.Server.Implementations.Library
var now = DateTime.UtcNow;
MediaInfo mediaInfo = null;
var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N") + ".json");
var cacheFilePath = string.IsNullOrEmpty(cacheKey) ? null : Path.Combine(_appPaths.CachePath, "mediainfo", cacheKey.GetMD5().ToString("N", CultureInfo.InvariantCulture) + ".json");
if (!string.IsNullOrEmpty(cacheKey))
{
@ -854,7 +854,7 @@ namespace Emby.Server.Implementations.Library
var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase));
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase));
var splitIndex = key.IndexOf(LiveStreamIdDelimeter);
var keyId = key.Substring(splitIndex + 1);

View file

@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers.Movies
@ -28,7 +27,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <value>The priority.</value>
public override ResolverPriority Priority => ResolverPriority.Third;
public MultiItemResolverResult ResolveMultiple(Folder parent,
public MultiItemResolverResult ResolveMultiple(
Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService)
@ -46,7 +46,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return result;
}
private MultiItemResolverResult ResolveMultipleInternal(Folder parent,
private MultiItemResolverResult ResolveMultipleInternal(
Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService)
@ -91,7 +92,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName)
private MultiItemResolverResult ResolveVideos<T>(
Folder parent,
IEnumerable<FileSystemMetadata> fileSystemEntries,
IDirectoryService directoryService,
bool suppportMultiEditions,
string collectionType,
bool parseName)
where T : Video, new()
{
var files = new List<FileSystemMetadata>();
@ -104,8 +111,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
// This is a hack but currently no better way to resolve a sometimes ambiguous situation
if (string.IsNullOrEmpty(collectionType))
{
if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) ||
string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase)
|| string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
{
return null;
}
@ -115,11 +122,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
leftOver.Add(child);
}
else if (IsIgnored(child.Name))
{
}
else
else if (!IsIgnored(child.Name))
{
files.Add(child);
}
@ -168,7 +171,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private static bool IsIgnored(string filename)
{
// Ignore samples
Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase);
Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase);
return m.Success;
}

View file

@ -152,7 +152,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>System.String.</returns>
private static string GetCacheKey(long internalUserId, Guid itemId)
{
return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N");
return internalUserId.ToString(CultureInfo.InvariantCulture) + "-" + itemId.ToString("N", CultureInfo.InvariantCulture);
}
public UserItemData GetUserData(User user, BaseItem item)

View file

@ -266,6 +266,7 @@ namespace Emby.Server.Implementations.Library
builder.Append(c);
}
}
return builder.ToString();
}
@ -286,17 +287,17 @@ namespace Emby.Server.Implementations.Library
if (user != null)
{
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.Item1;
updatedUsername = authResult.Item2;
success = authResult.Item3;
authenticationProvider = authResult.authenticationProvider;
updatedUsername = authResult.username;
success = authResult.success;
}
else
{
// user is null
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.Item1;
updatedUsername = authResult.Item2;
success = authResult.Item3;
authenticationProvider = authResult.authenticationProvider;
updatedUsername = authResult.username;
success = authResult.success;
if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider))
{
@ -331,22 +332,25 @@ namespace Emby.Server.Implementations.Library
if (user == null)
{
throw new SecurityException("Invalid username or password entered.");
throw new AuthenticationException("Invalid username or password entered.");
}
if (user.Policy.IsDisabled)
{
throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
throw new AuthenticationException(string.Format(
CultureInfo.InvariantCulture,
"The {0} account is currently disabled. Please consult with your administrator.",
user.Name));
}
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
{
throw new SecurityException("Forbidden.");
throw new AuthenticationException("Forbidden.");
}
if (!user.IsParentalScheduleAllowed())
{
throw new SecurityException("User is not allowed access at this time.");
throw new AuthenticationException("User is not allowed access at this time.");
}
// Update LastActivityDate and LastLoginDate, then save
@ -357,6 +361,7 @@ namespace Emby.Server.Implementations.Library
user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
UpdateUser(user);
}
UpdateInvalidLoginAttemptCount(user, 0);
}
else
@ -429,7 +434,7 @@ namespace Emby.Server.Implementations.Library
return providers;
}
private async Task<Tuple<string, bool>> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
private async Task<(string username, bool success)> AuthenticateWithProvider(IAuthenticationProvider provider, string username, string password, User resolvedUser)
{
try
{
@ -444,23 +449,23 @@ namespace Emby.Server.Implementations.Library
authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false);
}
if(authenticationResult.Username != username)
if (authenticationResult.Username != username)
{
_logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
username = authenticationResult.Username;
}
return new Tuple<string, bool>(username, true);
return (username, true);
}
catch (Exception ex)
catch (AuthenticationException ex)
{
_logger.LogError(ex, "Error authenticating with provider {provider}", provider.Name);
_logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name);
return new Tuple<string, bool>(username, false);
return (username, false);
}
}
private async Task<Tuple<IAuthenticationProvider, string, bool>> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(string username, string password, string hashedPassword, User user, string remoteEndPoint)
{
string updatedUsername = null;
bool success = false;
@ -475,15 +480,15 @@ namespace Emby.Server.Implementations.Library
if (password == null)
{
// legacy
success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
success = string.Equals(user.Password, hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
}
else
{
foreach (var provider in GetAuthenticationProviders(user))
{
var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
updatedUsername = providerAuthResult.Item1;
success = providerAuthResult.Item2;
updatedUsername = providerAuthResult.username;
success = providerAuthResult.success;
if (success)
{
@ -510,7 +515,7 @@ namespace Emby.Server.Implementations.Library
}
}
return new Tuple<IAuthenticationProvider, string, bool>(authenticationProvider, username, success);
return (authenticationProvider, username, success);
}
private void UpdateInvalidLoginAttemptCount(User user, int newValue)
@ -593,7 +598,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(user));
}
bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result;
bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user);
bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using MediaBrowser.Controller.Channels;
@ -117,7 +118,7 @@ namespace Emby.Server.Implementations.Library
if (!query.IncludeHidden)
{
list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N"))).ToList();
list = list.Where(i => !user.Configuration.MyMediaExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList();
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
@ -127,7 +128,7 @@ namespace Emby.Server.Implementations.Library
return list
.OrderBy(i =>
{
var index = orders.IndexOf(i.Id.ToString("N"));
var index = orders.IndexOf(i.Id.ToString("N", CultureInfo.InvariantCulture));
if (index == -1)
{
@ -136,7 +137,7 @@ namespace Emby.Server.Implementations.Library
{
if (!view.DisplayParentId.Equals(Guid.Empty))
{
index = orders.IndexOf(view.DisplayParentId.ToString("N"));
index = orders.IndexOf(view.DisplayParentId.ToString("N", CultureInfo.InvariantCulture));
}
}
}
@ -269,7 +270,7 @@ namespace Emby.Server.Implementations.Library
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToList();
}

View file

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators
continue;
}
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
_libraryManager.DeleteItem(item, new DeleteOptions
{

View file

@ -1,7 +1,7 @@
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
_libraryManager.DeleteItem(item, new DeleteOptions
{

View file

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
@ -76,7 +77,7 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N"), item.Name, item.GetType().Name);
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
_libraryManager.DeleteItem(item, new DeleteOptions
{

View file

@ -681,7 +681,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
timer.Id = Guid.NewGuid().ToString("N");
timer.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
LiveTvProgram programInfo = null;
@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken)
{
info.Id = Guid.NewGuid().ToString("N");
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
// populate info.seriesID
var program = GetProgramInfoFromCache(info.ProgramId);
@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var json = _jsonSerializer.SerializeToString(mediaSource);
mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id;
mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
//if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
//{
@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var timer = new TimerInfo
{
ChannelId = channelId,
Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N"),
Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N", CultureInfo.InvariantCulture),
StartDate = parent.StartDate,
EndDate = parent.EndDate.Value,
ProgramId = parent.ExternalId,

View file

@ -211,7 +211,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source),
OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,
CommunityRating = program.StarRating,
SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N")
SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
if (string.IsNullOrWhiteSpace(program.ProgramId))
@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);
}
programInfo.ShowId = uniqueString.GetMD5().ToString("N");
programInfo.ShowId = uniqueString.GetMD5().ToString("N", CultureInfo.InvariantCulture);
// If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped
if (programInfo.IsSeries

View file

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -52,7 +53,7 @@ namespace Emby.Server.Implementations.LiveTv
ExternalId = info.Id,
ChannelId = GetInternalChannelId(service.Name, info.ChannelId),
Status = info.Status,
SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N"),
SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture),
PrePaddingSeconds = info.PrePaddingSeconds,
PostPaddingSeconds = info.PostPaddingSeconds,
IsPostPaddingRequired = info.IsPostPaddingRequired,
@ -69,7 +70,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(info.ProgramId))
{
dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N");
dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture);
}
if (program != null)
@ -107,7 +108,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var dto = new SeriesTimerInfoDto
{
Id = GetInternalSeriesTimerId(info.Id).ToString("N"),
Id = GetInternalSeriesTimerId(info.Id).ToString("N", CultureInfo.InvariantCulture),
Overview = info.Overview,
EndDate = info.EndDate,
Name = info.Name,
@ -139,7 +140,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(info.ProgramId))
{
dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N");
dto.ProgramId = GetInternalProgramId(info.ProgramId).ToString("N", CultureInfo.InvariantCulture);
}
dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray());
@ -169,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
dto.ParentThumbItemId = librarySeries.Id.ToString("N");
dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -185,7 +186,7 @@ namespace Emby.Server.Implementations.LiveTv
{
_imageProcessor.GetImageCacheTag(librarySeries, image)
};
dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -213,7 +214,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
dto.ParentPrimaryImageItemId = program.Id.ToString("N");
dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -232,7 +233,7 @@ namespace Emby.Server.Implementations.LiveTv
{
_imageProcessor.GetImageCacheTag(program, image)
};
dto.ParentBackdropItemId = program.Id.ToString("N");
dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -263,7 +264,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
dto.ParentThumbItemId = librarySeries.Id.ToString("N");
dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -279,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv
{
_imageProcessor.GetImageCacheTag(librarySeries, image)
};
dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -320,7 +321,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
dto.ParentPrimaryImageItemId = program.Id.ToString("N");
dto.ParentPrimaryImageItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -339,7 +340,7 @@ namespace Emby.Server.Implementations.LiveTv
{
_imageProcessor.GetImageCacheTag(program, image)
};
dto.ParentBackdropItemId = program.Id.ToString("N");
dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
{
@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var name = ServiceName + externalId + InternalVersionNumber;
return name.ToLowerInvariant().GetMD5().ToString("N");
return name.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
public Guid GetInternalSeriesTimerId(string externalId)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv
}
info.RequiresClosing = true;
var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_";
info.LiveStreamId = idPrefix + info.Id;
@ -820,7 +821,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
@ -997,7 +998,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(timer.SeriesTimerId))
{
program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
.ToString("N");
.ToString("N", CultureInfo.InvariantCulture);
foundSeriesTimer = true;
}
@ -1018,7 +1019,7 @@ namespace Emby.Server.Implementations.LiveTv
if (seriesTimer != null)
{
program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id)
.ToString("N");
.ToString("N", CultureInfo.InvariantCulture);
}
}
}
@ -1472,7 +1473,7 @@ namespace Emby.Server.Implementations.LiveTv
dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
? null
: _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N");
: _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N", CultureInfo.InvariantCulture);
dto.TimerId = string.IsNullOrEmpty(info.Id)
? null
@ -2027,7 +2028,7 @@ namespace Emby.Server.Implementations.LiveTv
info.StartDate = program.StartDate;
info.Name = program.Name;
info.Overview = program.Overview;
info.ProgramId = programDto.Id.ToString("N");
info.ProgramId = programDto.Id.ToString("N", CultureInfo.InvariantCulture);
info.ExternalProgramId = program.ExternalId;
if (program.EndDate.HasValue)
@ -2088,7 +2089,7 @@ namespace Emby.Server.Implementations.LiveTv
if (service is ISupportsNewTimerIds supportsNewTimerIds)
{
newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N");
newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N", CultureInfo.InvariantCulture);
}
else
{
@ -2192,7 +2193,7 @@ namespace Emby.Server.Implementations.LiveTv
info.EnabledUsers = _userManager.Users
.Where(IsLiveTvEnabled)
.Select(i => i.Id.ToString("N"))
.Select(i => i.Id.ToString("N", CultureInfo.InvariantCulture))
.ToArray();
return info;
@ -2219,7 +2220,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var parts = id.Split(new[] { '_' }, 2);
var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase));
var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), parts[0], StringComparison.OrdinalIgnoreCase));
if (service == null)
{
@ -2269,7 +2270,7 @@ namespace Emby.Server.Implementations.LiveTv
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N");
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
list.Add(info);
config.TunerHosts = list.ToArray();
}
@ -2312,7 +2313,7 @@ namespace Emby.Server.Implementations.LiveTv
if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
{
info.Id = Guid.NewGuid().ToString("N");
info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
list.Add(info);
config.ListingProviders = list.ToArray();
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var openKeys = new List<string>();
openKeys.Add(item.GetType().Name);
openKeys.Add(item.Id.ToString("N"));
openKeys.Add(item.Id.ToString("N", CultureInfo.InvariantCulture));
openKeys.Add(source.Id ?? string.Empty);
source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
}

View file

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@ -11,7 +13,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@ -20,7 +21,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
using (var manager = new HdHomerunManager(_socketFactory, Logger))
{
// Legacy HdHomeruns are IPv4 only
var ipInfo = _networkManager.ParseIpAddress(uri.Host);
var ipInfo = IPAddress.Parse(uri.Host);
for (int i = 0; i < model.TunerCount; ++i)
{
@ -461,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
id = "native";
}
id += "_" + channelId.GetMD5().ToString("N") + "_" + url.GetMD5().ToString("N");
id += "_" + channelId.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_" + url.GetMD5().ToString("N", CultureInfo.InvariantCulture);
var mediaSource = new MediaSourceInfo
{
@ -675,13 +675,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try
{
await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken);
await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested)
{
var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
var deviceIp = response.RemoteEndPoint.IpAddress.Address;
var deviceIp = response.RemoteEndPoint.Address.ToString();
// check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte
if (response.ReceivedBytes > 13 && response.Buffer[1] == 3)

View file

@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private uint? _lockkey = null;
private int _activeTuner = -1;
private readonly ISocketFactory _socketFactory;
private IpAddressInfo _remoteIp;
private IPAddress _remoteIp;
private ILogger _logger;
private ISocket _currentTcpSocket;
@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
public async Task<bool> CheckTunerAvailability(IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken)
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
{
using (var socket = _socketFactory.CreateTcpSocket(remoteIp, HdHomeRunPort))
{
@ -122,9 +122,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
private static async Task<bool> CheckTunerAvailability(ISocket socket, IpAddressInfo remoteIp, int tuner, CancellationToken cancellationToken)
private static async Task<bool> CheckTunerAvailability(ISocket socket, IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
{
var ipEndPoint = new IpEndPointInfo(remoteIp, HdHomeRunPort);
var ipEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken);
@ -137,7 +137,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
}
public async Task StartStreaming(IpAddressInfo remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
public async Task StartStreaming(IPAddress remoteIp, IPAddress localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
{
_remoteIp = remoteIp;
@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var lockKeyValue = _lockkey.Value;
var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
for (int i = 0; i < numTuners; ++i)
{
@ -217,7 +217,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
foreach (Tuple<string, string> command in commandList)
{
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
await tcpClient.SendToAsync(channelMsg, 0, channelMsg.Length, new IPEndPoint(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal))
@ -242,7 +242,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
_logger.LogInformation("HdHomerunManager.ReleaseLockkey {0}", lockKeyValue);
var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort);
var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false);

View file

@ -25,7 +25,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly int _numTuners;
private readonly INetworkManager _networkManager;
public HdHomerunUdpStream(MediaSourceInfo mediaSource, TunerHostInfo tunerHostInfo, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, MediaBrowser.Model.Net.ISocketFactory socketFactory, INetworkManager networkManager)
public HdHomerunUdpStream(
MediaSourceInfo mediaSource,
TunerHostInfo tunerHostInfo,
string originalStreamId,
IHdHomerunChannelCommands channelCommands,
int numTuners,
IFileSystem fileSystem,
IHttpClient httpClient,
ILogger logger,
IServerApplicationPaths appPaths,
IServerApplicationHost appHost,
MediaBrowser.Model.Net.ISocketFactory socketFactory,
INetworkManager networkManager)
: base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
{
_appHost = appHost;
@ -58,7 +70,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
var remoteAddress = IPAddress.Parse(uri.Host);
var embyRemoteAddress = _networkManager.ParseIpAddress(uri.Host);
IPAddress localAddress = null;
using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{
@ -81,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
// send url to start streaming
await hdHomerunManager.StartStreaming(embyRemoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, openCancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
MediaSource = mediaSource;
Logger = logger;
EnableStreamSharing = true;
UniqueId = Guid.NewGuid().ToString("N");
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
if (tuner != null)
{

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private string GetFullChannelIdPrefix(TunerHostInfo info)
{
return ChannelIdPrefix + info.Url.GetMD5().ToString("N");
return ChannelIdPrefix + info.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Name = Name,
SourceType = Type,
Status = LiveTvTunerStatus.Available,
Id = i.Url.GetMD5().ToString("N"),
Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture),
Url = i.Url
})
.ToList();
@ -173,7 +174,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
ReadAtNativeFramerate = false,
Id = channel.Path.GetMD5().ToString("N"),
Id = channel.Path.GetMD5().ToString("N", CultureInfo.InvariantCulture),
IsInfiniteStream = true,
IsRemote = isRemote,

View file

@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
UserAgent = _appHost.ApplicationUserAgent
});
}
return Task.FromResult((Stream)File.OpenRead(url));
}
@ -92,11 +93,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var channel = GetChannelnfo(extInf, tunerHostId, line);
if (string.IsNullOrWhiteSpace(channel.Id))
{
channel.Id = channelIdPrefix + line.GetMD5().ToString("N");
channel.Id = channelIdPrefix + line.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
else
{
channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N");
channel.Id = channelIdPrefix + channel.Id.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
channel.Path = line;

View file

@ -18,11 +18,11 @@
"HeaderAlbumArtists": "Albumkunstnere",
"HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteAlbums": "Favoritalbummer",
"HeaderFavoriteArtists": "Favoritkunstnere",
"HeaderFavoriteEpisodes": "Favorit-afsnit",
"HeaderFavoriteShows": "Favorit-serier",
"HeaderFavoriteSongs": "Favorit-sange",
"HeaderFavoriteEpisodes": "Favoritepisoder",
"HeaderFavoriteShows": "Favoritserier",
"HeaderFavoriteSongs": "Favoritsange",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Næste",
"HeaderRecordingGroups": "Optagelsesgrupper",

View file

@ -19,9 +19,9 @@
"HeaderCameraUploads": "Kamera feltöltések",
"HeaderContinueWatching": "Folyamatban lévő filmek",
"HeaderFavoriteAlbums": "Kedvenc Albumok",
"HeaderFavoriteArtists": "Kedvenc Művészek",
"HeaderFavoriteArtists": "Kedvenc Előadók",
"HeaderFavoriteEpisodes": "Kedvenc Epizódok",
"HeaderFavoriteShows": "Kedvenc Műsorok",
"HeaderFavoriteShows": "Kedvenc Sorozatok",
"HeaderFavoriteSongs": "Kedvenc Dalok",
"HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",

View file

@ -18,11 +18,11 @@
"HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderCameraUploads": "Przekazane obrazy",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Albumy ulubione",
"HeaderFavoriteArtists": "Wykonawcy ulubieni",
"HeaderFavoriteEpisodes": "Odcinki ulubione",
"HeaderFavoriteShows": "Seriale ulubione",
"HeaderFavoriteSongs": "Utwory ulubione",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
"HeaderFavoriteEpisodes": "Ulubione odcinki",
"HeaderFavoriteShows": "Ulubione seriale",
"HeaderFavoriteSongs": "Ulubione utwory",
"HeaderLiveTV": "Telewizja",
"HeaderNextUp": "Do obejrzenia",
"HeaderRecordingGroups": "Grupy nagrań",
@ -41,26 +41,26 @@
"Movies": "Filmy",
"Music": "Muzyka",
"MusicVideos": "Teledyski",
"NameInstallFailed": "Instalacja {0} nieudana.",
"NameInstallFailed": "Instalacja {0} nieudana",
"NameSeasonNumber": "Sezon {0}",
"NameSeasonUnknown": "Sezon nieznany",
"NameSeasonUnknown": "Nieznany sezon",
"NewVersionIsAvailable": "Nowa wersja serwera Jellyfin jest dostępna do pobrania.",
"NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji",
"NotificationOptionApplicationUpdateInstalled": "Zainstalowano aktualizację aplikacji",
"NotificationOptionApplicationUpdateInstalled": "Zaktualizowano aplikację",
"NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
"NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia mobilnego",
"NotificationOptionInstallationFailed": "Niepowodzenie instalacji",
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia przenośnego",
"NotificationOptionInstallationFailed": "Nieudana instalacja",
"NotificationOptionNewLibraryContent": "Dodano nową zawartość",
"NotificationOptionPluginError": "Awaria wtyczki",
"NotificationOptionPluginInstalled": "Zainstalowano wtyczkę",
"NotificationOptionPluginUninstalled": "Odinstalowano wtyczkę",
"NotificationOptionPluginUpdateInstalled": "Zainstalowano aktualizację wtyczki",
"NotificationOptionPluginUpdateInstalled": "Zaktualizowano wtyczkę",
"NotificationOptionServerRestartRequired": "Wymagane ponowne uruchomienie serwera",
"NotificationOptionTaskFailed": "Awaria zaplanowanego zadania",
"NotificationOptionUserLockedOut": "Użytkownik zablokowany",
"NotificationOptionVideoPlayback": "Rozpoczęto odtwarzanie wideo",
"NotificationOptionVideoPlaybackStopped": "Odtwarzanie wideo zatrzymane",
"NotificationOptionVideoPlaybackStopped": "Zatrzymano odtwarzanie wideo",
"Photos": "Zdjęcia",
"Playlists": "Listy odtwarzania",
"Plugin": "Wtyczka",
@ -73,7 +73,7 @@
"ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia",
"Shows": "Seriale",
"Songs": "Utwory",
"StartupEmbyServerIsLoading": "Twa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
"StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
"SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem",
"SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}",
"SubtitlesDownloadedForItem": "Pobrano napisy dla {0}",
@ -91,7 +91,7 @@
"UserPolicyUpdatedWithName": "Zmieniono zasady użytkowania dla {0}",
"UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}",
"ValueHasBeenAddedToLibrary": "{0} został dodany to biblioteki mediów",
"ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów",
"ValueSpecialEpisodeName": "Specjalne - {0}",
"VersionNumber": "Wersja {0}"
}

View file

@ -21,8 +21,8 @@
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Shows Favoritos",
"HeaderFavoriteSongs": "Musicas Favoritas",
"HeaderFavoriteShows": "Séries Favoritas",
"HeaderFavoriteSongs": "Músicas Favoritas",
"HeaderLiveTV": "TV ao Vivo",
"HeaderNextUp": "Próximos",
"HeaderRecordingGroups": "Grupos de Gravação",
@ -32,19 +32,19 @@
"ItemRemovedWithName": "{0} foi removido da biblioteca",
"LabelIpAddressValue": "Endereço IP: {0}",
"LabelRunningTimeValue": "Tempo de execução: {0}",
"Latest": "Recente",
"MessageApplicationUpdated": "O servidor Jellyfin foi atualizado",
"MessageApplicationUpdatedTo": "O Servidor Jellyfin foi atualizado para {0}",
"Latest": "Recentes",
"MessageApplicationUpdated": "Servidor Jellyfin atualizado",
"MessageApplicationUpdatedTo": "Servidor Jellyfin atualizado para {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada",
"MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
"MixedContent": "Conteúdo misto",
"Movies": "Filmes",
"Music": "Música",
"MusicVideos": "Vídeos musicais",
"MusicVideos": "Clipes",
"NameInstallFailed": "A instalação de {0} falhou",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconhecida",
"NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para download.",
"NewVersionIsAvailable": "Uma nova versão do Servidor Jellyfin está disponível para download.",
"NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível",
"NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada",
"NotificationOptionAudioPlayback": "Reprodução de áudio iniciada",

View file

@ -16,14 +16,14 @@ namespace Emby.Server.Implementations.Net
// but that wasn't really the point so kept to YAGNI principal for now, even if the
// interfaces are a bit ugly, specific and make assumptions.
public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort)
public ISocket CreateTcpSocket(IPAddress remoteAddress, int remotePort)
{
if (remotePort < 0)
{
throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort));
}
var addressFamily = remoteAddress.AddressFamily == IpAddressFamily.InterNetwork
var addressFamily = remoteAddress.AddressFamily == AddressFamily.InterNetwork
? AddressFamily.InterNetwork
: AddressFamily.InterNetworkV6;
@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Net
try
{
return new UdpSocket(retVal, new IpEndPointInfo(remoteAddress, remotePort));
return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort));
}
catch
{
@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Net
/// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
/// </summary>
/// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort)
public ISocket CreateSsdpUdpSocket(IPAddress localIpAddress, int localPort)
{
if (localPort < 0)
{
@ -115,10 +115,8 @@ namespace Emby.Server.Implementations.Net
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
var localIp = NetworkManager.ToIPAddress(localIpAddress);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp));
return new UdpSocket(retVal, localPort, localIp);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIpAddress));
return new UdpSocket(retVal, localPort, localIpAddress);
}
catch
{

View file

@ -3,7 +3,6 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Networking;
using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Net
@ -19,7 +18,7 @@ namespace Emby.Server.Implementations.Net
public Socket Socket => _socket;
public IpAddressInfo LocalIPAddress { get; }
public IPAddress LocalIPAddress { get; }
private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
{
@ -40,7 +39,7 @@ namespace Emby.Server.Implementations.Net
_socket = socket;
_localPort = localPort;
LocalIPAddress = NetworkManager.ToIpAddressInfo(ip);
LocalIPAddress = ip;
_socket.Bind(new IPEndPoint(ip, _localPort));
@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Net
{
Buffer = e.Buffer,
ReceivedBytes = e.BytesTransferred,
RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint),
RemoteEndPoint = e.RemoteEndPoint as IPEndPoint,
LocalIPAddress = LocalIPAddress
});
}
@ -100,12 +99,12 @@ namespace Emby.Server.Implementations.Net
}
}
public UdpSocket(Socket socket, IpEndPointInfo endPoint)
public UdpSocket(Socket socket, IPEndPoint endPoint)
{
if (socket == null) throw new ArgumentNullException(nameof(socket));
_socket = socket;
_socket.Connect(NetworkManager.ToIPEndPoint(endPoint));
_socket.Connect(endPoint);
InitReceiveSocketAsyncEventArgs();
}
@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.Net
return new SocketReceiveResult
{
ReceivedBytes = receivedBytes,
RemoteEndPoint = ToIpEndPointInfo((IPEndPoint)remoteEndPoint),
RemoteEndPoint = (IPEndPoint)remoteEndPoint,
Buffer = buffer,
LocalIPAddress = LocalIPAddress
};
@ -191,7 +190,7 @@ namespace Emby.Server.Implementations.Net
return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken);
}
public Task SendToAsync(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken)
public Task SendToAsync(byte[] buffer, int offset, int size, IPEndPoint endPoint, CancellationToken cancellationToken)
{
ThrowIfDisposed();
@ -227,13 +226,11 @@ namespace Emby.Server.Implementations.Net
return taskCompletion.Task;
}
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IpEndPointInfo endPoint, AsyncCallback callback, object state)
public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, IPEndPoint endPoint, AsyncCallback callback, object state)
{
ThrowIfDisposed();
var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint);
return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state);
return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, endPoint, callback, state);
}
public int EndSendTo(IAsyncResult result)
@ -268,15 +265,5 @@ namespace Emby.Server.Implementations.Net
_disposed = true;
}
private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
{
if (endpoint == null)
{
return null;
}
return NetworkManager.ToIpEndPointInfo(endpoint);
}
}
}

View file

@ -1,167 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
namespace Emby.Server.Implementations.Networking.IPNetwork
{
/// <summary>
/// Extension methods to convert <see cref="BigInteger"/>
/// instances to hexadecimal, octal, and binary strings.
/// </summary>
public static class BigIntegerExtensions
{
/// <summary>
/// Converts a <see cref="BigInteger"/> to a binary string.
/// </summary>
/// <param name="bigint">A <see cref="BigInteger"/>.</param>
/// <returns>
/// A <see cref="string"/> containing a binary
/// representation of the supplied <see cref="BigInteger"/>.
/// </returns>
public static string ToBinaryString(this BigInteger bigint)
{
var bytes = bigint.ToByteArray();
var idx = bytes.Length - 1;
// Create a StringBuilder having appropriate capacity.
var base2 = new StringBuilder(bytes.Length * 8);
// Convert first byte to binary.
var binary = Convert.ToString(bytes[idx], 2);
// Ensure leading zero exists if value is positive.
if (binary[0] != '0' && bigint.Sign == 1)
{
base2.Append('0');
}
// Append binary string to StringBuilder.
base2.Append(binary);
// Convert remaining bytes adding leading zeros.
for (idx--; idx >= 0; idx--)
{
base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0'));
}
return base2.ToString();
}
/// <summary>
/// Converts a <see cref="BigInteger"/> to a hexadecimal string.
/// </summary>
/// <param name="bigint">A <see cref="BigInteger"/>.</param>
/// <returns>
/// A <see cref="string"/> containing a hexadecimal
/// representation of the supplied <see cref="BigInteger"/>.
/// </returns>
public static string ToHexadecimalString(this BigInteger bigint)
{
return bigint.ToString("X");
}
/// <summary>
/// Converts a <see cref="BigInteger"/> to a octal string.
/// </summary>
/// <param name="bigint">A <see cref="BigInteger"/>.</param>
/// <returns>
/// A <see cref="string"/> containing an octal
/// representation of the supplied <see cref="BigInteger"/>.
/// </returns>
public static string ToOctalString(this BigInteger bigint)
{
var bytes = bigint.ToByteArray();
var idx = bytes.Length - 1;
// Create a StringBuilder having appropriate capacity.
var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8);
// Calculate how many bytes are extra when byte array is split
// into three-byte (24-bit) chunks.
var extra = bytes.Length % 3;
// If no bytes are extra, use three bytes for first chunk.
if (extra == 0)
{
extra = 3;
}
// Convert first chunk (24-bits) to integer value.
int int24 = 0;
for (; extra != 0; extra--)
{
int24 <<= 8;
int24 += bytes[idx--];
}
// Convert 24-bit integer to octal without adding leading zeros.
var octal = Convert.ToString(int24, 8);
// Ensure leading zero exists if value is positive.
if (octal[0] != '0')
{
if (bigint.Sign == 1)
{
base8.Append('0');
}
}
// Append first converted chunk to StringBuilder.
base8.Append(octal);
// Convert remaining 24-bit chunks, adding leading zeros.
for (; idx >= 0; idx -= 3)
{
int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2];
base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0'));
}
return base8.ToString();
}
/// <summary>
///
/// Reverse a Positive BigInteger ONLY
/// Bitwise ~ operator
///
/// Input : FF FF FF FF
/// Width : 4
/// Result : 00 00 00 00
///
///
/// Input : 00 00 00 00
/// Width : 4
/// Result : FF FF FF FF
///
/// Input : FF FF FF FF
/// Width : 8
/// Result : FF FF FF FF 00 00 00 00
///
///
/// Input : 00 00 00 00
/// Width : 8
/// Result : FF FF FF FF FF FF FF FF
///
/// </summary>
/// <param name="input"></param>
/// <param name="width"></param>
/// <returns></returns>
public static BigInteger PositiveReverse(this BigInteger input, int width)
{
var result = new List<byte>();
var bytes = input.ToByteArray();
var work = new byte[width];
Array.Copy(bytes, 0, work, 0, bytes.Length - 1); // Length -1 : positive BigInteger
for (int i = 0; i < work.Length; i++)
{
result.Add((byte)(~work[i]));
}
result.Add(0); // positive BigInteger
return new BigInteger(result.ToArray());
}
}
}

View file

@ -1,94 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Numerics;
namespace Emby.Server.Implementations.Networking.IPNetwork
{
public class IPAddressCollection : IEnumerable<IPAddress>, IEnumerator<IPAddress>
{
private IPNetwork _ipnetwork;
private BigInteger _enumerator;
internal IPAddressCollection(IPNetwork ipnetwork)
{
this._ipnetwork = ipnetwork;
this._enumerator = -1;
}
#region Count, Array, Enumerator
public BigInteger Count => this._ipnetwork.Total;
public IPAddress this[BigInteger i]
{
get
{
if (i >= this.Count)
{
throw new ArgumentOutOfRangeException(nameof(i));
}
byte width = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? (byte)32 : (byte)128;
var ipn = this._ipnetwork.Subnet(width);
return ipn[i].Network;
}
}
#endregion
#region IEnumerable Members
IEnumerator<IPAddress> IEnumerable<IPAddress>.GetEnumerator()
{
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
#region IEnumerator<IPNetwork> Members
public IPAddress Current => this[this._enumerator];
#endregion
#region IDisposable Members
public void Dispose()
{
// nothing to dispose
return;
}
#endregion
#region IEnumerator Members
object IEnumerator.Current => this.Current;
public bool MoveNext()
{
this._enumerator++;
if (this._enumerator >= this.Count)
{
return false;
}
return true;
}
public void Reset()
{
this._enumerator = -1;
}
#endregion
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,129 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
namespace Emby.Server.Implementations.Networking.IPNetwork
{
public class IPNetworkCollection : IEnumerable<IPNetwork>, IEnumerator<IPNetwork>
{
private BigInteger _enumerator;
private byte _cidrSubnet;
private IPNetwork _ipnetwork;
private byte _cidr => this._ipnetwork.Cidr;
private BigInteger _broadcast => IPNetwork.ToBigInteger(this._ipnetwork.Broadcast);
private BigInteger _lastUsable => IPNetwork.ToBigInteger(this._ipnetwork.LastUsable);
private BigInteger _network => IPNetwork.ToBigInteger(this._ipnetwork.Network);
#if TRAVISCI
public
#else
internal
#endif
IPNetworkCollection(IPNetwork ipnetwork, byte cidrSubnet)
{
int maxCidr = ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 32 : 128;
if (cidrSubnet > maxCidr)
{
throw new ArgumentOutOfRangeException(nameof(cidrSubnet));
}
if (cidrSubnet < ipnetwork.Cidr)
{
throw new ArgumentException("cidr");
}
this._cidrSubnet = cidrSubnet;
this._ipnetwork = ipnetwork;
this._enumerator = -1;
}
#region Count, Array, Enumerator
public BigInteger Count
{
get
{
var count = BigInteger.Pow(2, this._cidrSubnet - this._cidr);
return count;
}
}
public IPNetwork this[BigInteger i]
{
get
{
if (i >= this.Count)
{
throw new ArgumentOutOfRangeException(nameof(i));
}
var last = this._ipnetwork.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6
? this._lastUsable : this._broadcast;
var increment = (last - this._network) / this.Count;
var uintNetwork = this._network + ((increment + 1) * i);
var ipn = new IPNetwork(uintNetwork, this._ipnetwork.AddressFamily, this._cidrSubnet);
return ipn;
}
}
#endregion
#region IEnumerable Members
IEnumerator<IPNetwork> IEnumerable<IPNetwork>.GetEnumerator()
{
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
#region IEnumerator<IPNetwork> Members
public IPNetwork Current => this[this._enumerator];
#endregion
#region IDisposable Members
public void Dispose()
{
// nothing to dispose
return;
}
#endregion
#region IEnumerator Members
object IEnumerator.Current => this.Current;
public bool MoveNext()
{
this._enumerator++;
if (this._enumerator >= this.Count)
{
return false;
}
return true;
}
public void Reset()
{
this._enumerator = -1;
}
#endregion
#endregion
}
}

View file

@ -1,24 +0,0 @@
Copyright (c) 2015, lduchosal
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -9,55 +9,38 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations.Networking
{
public class NetworkManager : INetworkManager
{
protected ILogger Logger { get; private set; }
private readonly ILogger _logger;
public event EventHandler NetworkChanged;
public Func<string[]> LocalSubnetsFn { get; set; }
private IPAddress[] _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
public NetworkManager(ILoggerFactory loggerFactory)
public NetworkManager(ILogger<NetworkManager> logger)
{
Logger = loggerFactory.CreateLogger(nameof(NetworkManager));
_logger = logger;
// In FreeBSD these events cause a crash
if (OperatingSystem.Id != OperatingSystemId.BSD)
{
try
{
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error binding to NetworkAddressChanged event");
}
try
{
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error binding to NetworkChange_NetworkAvailabilityChanged event");
}
}
NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
}
private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
public Func<string[]> LocalSubnetsFn { get; set; }
public event EventHandler NetworkChanged;
private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
Logger.LogDebug("NetworkAvailabilityChanged");
_logger.LogDebug("NetworkAvailabilityChanged");
OnNetworkChanged();
}
private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
private void OnNetworkAddressChanged(object sender, EventArgs e)
{
Logger.LogDebug("NetworkAddressChanged");
_logger.LogDebug("NetworkAddressChanged");
OnNetworkChanged();
}
@ -68,39 +51,35 @@ namespace Emby.Server.Implementations.Networking
_localIpAddresses = null;
_macAddresses = null;
}
if (NetworkChanged != null)
{
NetworkChanged(this, EventArgs.Empty);
}
}
private IpAddressInfo[] _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
public IpAddressInfo[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
{
lock (_localIpAddressSyncLock)
{
if (_localIpAddresses == null)
{
var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).Result.Select(ToIpAddressInfo).ToArray();
var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray();
_localIpAddresses = addresses;
return addresses;
}
return _localIpAddresses;
}
}
private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
private List<IPAddress> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
{
var list = GetIPsDefault(ignoreVirtualInterface)
.ToList();
var list = GetIPsDefault(ignoreVirtualInterface).ToList();
if (list.Count == 0)
{
list.AddRange(await GetLocalIpAddressesFallback().ConfigureAwait(false));
list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
}
var listClone = list.ToList();
@ -116,9 +95,8 @@ namespace Emby.Server.Implementations.Networking
private static bool FilterIpAddress(IPAddress address)
{
var addressString = address.ToString();
if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
if (address.IsIPv6LinkLocal
|| address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@ -279,7 +257,7 @@ namespace Emby.Server.Implementations.Networking
if (normalizedSubnet.IndexOf('/') != -1)
{
var ipnetwork = IPNetwork.IPNetwork.Parse(normalizedSubnet);
var ipnetwork = IPNetwork.Parse(normalizedSubnet);
if (ipnetwork.Contains(address))
{
return true;
@ -351,13 +329,13 @@ namespace Emby.Server.Implementations.Networking
try
{
var host = uri.DnsSafeHost;
Logger.LogDebug("Resolving host {0}", host);
_logger.LogDebug("Resolving host {0}", host);
address = GetIpAddresses(host).Result.FirstOrDefault();
if (address != null)
{
Logger.LogDebug("{0} resolved to {1}", host, address);
_logger.LogDebug("{0} resolved to {1}", host, address);
return IsInLocalNetworkInternal(address.ToString(), false);
}
@ -368,7 +346,7 @@ namespace Emby.Server.Implementations.Networking
}
catch (Exception ex)
{
Logger.LogError(ex, "Error resolving hostname");
_logger.LogError(ex, "Error resolving hostname");
}
}
}
@ -381,56 +359,41 @@ namespace Emby.Server.Implementations.Networking
return Dns.GetHostAddressesAsync(hostName);
}
private List<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
{
NetworkInterface[] interfaces;
IEnumerable<NetworkInterface> interfaces;
try
{
var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
interfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(i => validStatuses.Contains(i.OperationalStatus))
.ToArray();
.Where(x => x.OperationalStatus == OperationalStatus.Up
|| x.OperationalStatus == OperationalStatus.Unknown);
}
catch (Exception ex)
catch (NetworkInformationException ex)
{
Logger.LogError(ex, "Error in GetAllNetworkInterfaces");
return new List<IPAddress>();
_logger.LogError(ex, "Error in GetAllNetworkInterfaces");
return Enumerable.Empty<IPAddress>();
}
return interfaces.SelectMany(network =>
{
var ipProperties = network.GetIPProperties();
try
// Try to exclude virtual adapters
// http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
var addr = ipProperties.GatewayAddresses.FirstOrDefault();
if (addr == null
|| (ignoreVirtualInterface
&& (addr.Address.Equals(IPAddress.Any) || addr.Address.Equals(IPAddress.IPv6Any))))
{
// suppress logging because it might be causing nas device wake up
//logger.LogDebug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
var ipProperties = network.GetIPProperties();
// Try to exclude virtual adapters
// http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
var addr = ipProperties.GatewayAddresses.FirstOrDefault();
if (addr == null || ignoreVirtualInterface && string.Equals(addr.Address.ToString(), "0.0.0.0", StringComparison.OrdinalIgnoreCase))
{
return new List<IPAddress>();
}
return ipProperties.UnicastAddresses
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6)
.ToList();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error querying network interface");
return new List<IPAddress>();
return Enumerable.Empty<IPAddress>();
}
return ipProperties.UnicastAddresses
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6);
}).GroupBy(i => i.ToString())
.Select(x => x.First())
.ToList();
.Select(x => x.First());
}
private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
@ -462,47 +425,27 @@ namespace Emby.Server.Implementations.Networking
var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
using (var udpClient = new UdpClient(localEndPoint))
{
var port = ((IPEndPoint)(udpClient.Client.LocalEndPoint)).Port;
var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
return port;
}
}
private List<string> _macAddresses;
public List<string> GetMacAddresses()
private List<PhysicalAddress> _macAddresses;
public List<PhysicalAddress> GetMacAddresses()
{
if (_macAddresses == null)
{
_macAddresses = GetMacAddressesInternal();
_macAddresses = GetMacAddressesInternal().ToList();
}
return _macAddresses;
}
private static List<string> GetMacAddressesInternal()
{
return NetworkInterface.GetAllNetworkInterfaces()
private static IEnumerable<PhysicalAddress> GetMacAddressesInternal()
=> NetworkInterface.GetAllNetworkInterfaces()
.Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(i =>
{
try
{
var physicalAddress = i.GetPhysicalAddress();
if (physicalAddress == null)
{
return null;
}
return physicalAddress.ToString();
}
catch (Exception)
{
//TODO Log exception.
return null;
}
})
.Where(i => i != null)
.ToList();
}
.Select(x => x.GetPhysicalAddress())
.Where(x => x != null && x != PhysicalAddress.None);
/// <summary>
/// Parses the specified endpointstring.
@ -612,32 +555,10 @@ namespace Emby.Server.Implementations.Networking
return hosts[0];
}
public IpAddressInfo ParseIpAddress(string ipAddress)
public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
{
if (TryParseIpAddress(ipAddress, out var info))
{
return info;
}
throw new ArgumentException("Invalid ip address: " + ipAddress);
}
public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo)
{
if (IPAddress.TryParse(ipAddress, out var address))
{
ipAddressInfo = ToIpAddressInfo(address);
return true;
}
ipAddressInfo = null;
return false;
}
public bool IsInSameSubnet(IpAddressInfo address1, IpAddressInfo address2, IpAddressInfo subnetMask)
{
IPAddress network1 = GetNetworkAddress(ToIPAddress(address1), ToIPAddress(subnetMask));
IPAddress network2 = GetNetworkAddress(ToIPAddress(address2), ToIPAddress(subnetMask));
IPAddress network1 = GetNetworkAddress(address1, subnetMask);
IPAddress network2 = GetNetworkAddress(address2, subnetMask);
return network1.Equals(network2);
}
@ -656,13 +577,13 @@ namespace Emby.Server.Implementations.Networking
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
}
return new IPAddress(broadcastAddress);
}
public IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address)
public IPAddress GetLocalIpSubnetMask(IPAddress address)
{
NetworkInterface[] interfaces;
IPAddress ipaddress = ToIPAddress(address);
try
{
@ -674,7 +595,7 @@ namespace Emby.Server.Implementations.Networking
}
catch (Exception ex)
{
Logger.LogError(ex, "Error in GetAllNetworkInterfaces");
_logger.LogError(ex, "Error in GetAllNetworkInterfaces");
return null;
}
@ -684,85 +605,17 @@ namespace Emby.Server.Implementations.Networking
{
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
{
if (ip.Address.Equals(ipaddress) && ip.IPv4Mask != null)
if (ip.Address.Equals(address) && ip.IPv4Mask != null)
{
return ToIpAddressInfo(ip.IPv4Mask);
return ip.IPv4Mask;
}
}
}
}
return null;
}
public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
{
if (endpoint == null)
{
return null;
}
return new IpEndPointInfo(ToIpAddressInfo(endpoint.Address), endpoint.Port);
}
public static IPEndPoint ToIPEndPoint(IpEndPointInfo endpoint)
{
if (endpoint == null)
{
return null;
}
return new IPEndPoint(ToIPAddress(endpoint.IpAddress), endpoint.Port);
}
public static IPAddress ToIPAddress(IpAddressInfo address)
{
if (address.Equals(IpAddressInfo.Any))
{
return IPAddress.Any;
}
if (address.Equals(IpAddressInfo.IPv6Any))
{
return IPAddress.IPv6Any;
}
if (address.Equals(IpAddressInfo.Loopback))
{
return IPAddress.Loopback;
}
if (address.Equals(IpAddressInfo.IPv6Loopback))
{
return IPAddress.IPv6Loopback;
}
return IPAddress.Parse(address.Address);
}
public static IpAddressInfo ToIpAddressInfo(IPAddress address)
{
if (address.Equals(IPAddress.Any))
{
return IpAddressInfo.Any;
}
if (address.Equals(IPAddress.IPv6Any))
{
return IpAddressInfo.IPv6Any;
}
if (address.Equals(IPAddress.Loopback))
{
return IpAddressInfo.Loopback;
}
if (address.Equals(IPAddress.IPv6Loopback))
{
return IpAddressInfo.IPv6Loopback;
}
return new IpAddressInfo(address.ToString(), address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork);
}
public async Task<IpAddressInfo[]> GetHostAddressesAsync(string host)
{
var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
return addresses.Select(ToIpAddressInfo).ToArray();
}
/// <summary>
/// Gets the network shares.
/// </summary>

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -129,7 +130,7 @@ namespace Emby.Server.Implementations.Playlists
{
new Share
{
UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N"),
UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N", CultureInfo.InvariantCulture),
CanEdit = true
}
}
@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0)
{
AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false)
{
EnableImages = true
});
@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Playlists
return new PlaylistCreationResult
{
Id = playlist.Id.ToString("N")
Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture)
};
}
finally

View file

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (_id == null)
{
_id = ScheduledTask.GetType().FullName.GetMD5().ToString("N");
_id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
return _id;

View file

@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName);
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
statement.TryBind("@UserName", info.UserName);
statement.TryBind("@IsActive", true);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName);
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N")));
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
statement.TryBind("@UserName", info.UserName);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Security
if (!query.UserId.Equals(Guid.Empty))
{
statement.TryBind("@UserId", query.UserId.ToString("N"));
statement.TryBind("@UserId", query.UserId.ToString("N", CultureInfo.InvariantCulture));
}
if (!string.IsNullOrEmpty(query.DeviceId))

View file

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
@ -245,7 +246,7 @@ namespace Emby.Server.Implementations.Serialization
return null;
}
return guid.ToString("N");
return guid.ToString("N", CultureInfo.InvariantCulture);
}
/// <summary>

View file

@ -10,8 +10,12 @@ namespace Emby.Server.Implementations
/// </summary>
public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
{
private string _defaultTranscodingTempPath;
private string _transcodingTempPath;
private string _internalMetadataPath;
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
/// Initializes a new instance of the <see cref="ServerApplicationPaths" /> class.
/// </summary>
public ServerApplicationPaths(
string programDataPath,
@ -30,7 +34,7 @@ namespace Emby.Server.Implementations
public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
/// <summary>
/// Gets the path to the base root media directory
/// Gets the path to the base root media directory.
/// </summary>
/// <value>The root folder path.</value>
public string RootFolderPath => Path.Combine(ProgramDataPath, "root");
@ -48,7 +52,7 @@ namespace Emby.Server.Implementations
public string LocalizationPath => Path.Combine(ProgramDataPath, "localization");
/// <summary>
/// Gets the path to the People directory
/// Gets the path to the People directory.
/// </summary>
/// <value>The people path.</value>
public string PeoplePath => Path.Combine(InternalMetadataPath, "People");
@ -56,37 +60,37 @@ namespace Emby.Server.Implementations
public string ArtistsPath => Path.Combine(InternalMetadataPath, "artists");
/// <summary>
/// Gets the path to the Genre directory
/// Gets the path to the Genre directory.
/// </summary>
/// <value>The genre path.</value>
public string GenrePath => Path.Combine(InternalMetadataPath, "Genre");
/// <summary>
/// Gets the path to the Genre directory
/// Gets the path to the Genre directory.
/// </summary>
/// <value>The genre path.</value>
public string MusicGenrePath => Path.Combine(InternalMetadataPath, "MusicGenre");
/// <summary>
/// Gets the path to the Studio directory
/// Gets the path to the Studio directory.
/// </summary>
/// <value>The studio path.</value>
public string StudioPath => Path.Combine(InternalMetadataPath, "Studio");
/// <summary>
/// Gets the path to the Year directory
/// Gets the path to the Year directory.
/// </summary>
/// <value>The year path.</value>
public string YearPath => Path.Combine(InternalMetadataPath, "Year");
/// <summary>
/// Gets the path to the General IBN directory
/// Gets the path to the General IBN directory.
/// </summary>
/// <value>The general path.</value>
public string GeneralPath => Path.Combine(InternalMetadataPath, "general");
/// <summary>
/// Gets the path to the Ratings IBN directory
/// Gets the path to the Ratings IBN directory.
/// </summary>
/// <value>The ratings path.</value>
public string RatingsPath => Path.Combine(InternalMetadataPath, "ratings");
@ -98,15 +102,13 @@ namespace Emby.Server.Implementations
public string MediaInfoImagesPath => Path.Combine(InternalMetadataPath, "mediainfo");
/// <summary>
/// Gets the path to the user configuration directory
/// Gets the path to the user configuration directory.
/// </summary>
/// <value>The user configuration directory path.</value>
public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users");
private string _defaultTranscodingTempPath;
public string DefaultTranscodingTempPath => _defaultTranscodingTempPath ?? (_defaultTranscodingTempPath = Path.Combine(ProgramDataPath, "transcoding-temp"));
private string _transcodingTempPath;
public string TranscodingTempPath
{
get => _transcodingTempPath ?? (_transcodingTempPath = DefaultTranscodingTempPath);
@ -139,7 +141,6 @@ namespace Emby.Server.Implementations
return path;
}
private string _internalMetadataPath;
public string InternalMetadataPath
{
get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata"));

View file

@ -10,8 +10,6 @@ namespace Emby.Server.Implementations.Services
public class HttpResult
: IHttpResult, IAsyncStreamWriter
{
public object Response { get; set; }
public HttpResult(object response, string contentType, HttpStatusCode statusCode)
{
this.Headers = new Dictionary<string, string>();
@ -21,6 +19,8 @@ namespace Emby.Server.Implementations.Services
this.StatusCode = statusCode;
}
public object Response { get; set; }
public string ContentType { get; set; }
public IDictionary<string, string> Headers { get; private set; }
@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Services
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
var response = RequestContext == null ? null : RequestContext.Response;
var response = RequestContext?.Response;
if (this.Response is byte[] bytesResponse)
{
@ -45,13 +45,14 @@ namespace Emby.Server.Implementations.Services
if (response != null)
{
response.OriginalResponse.ContentLength = contentLength;
response.ContentLength = contentLength;
}
if (contentLength > 0)
{
await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
}
return;
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
@ -7,13 +6,14 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using Microsoft.AspNetCore.Http;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.Services
{
public static class ResponseHelper
{
public static Task WriteToResponse(IResponse response, IRequest request, object result, CancellationToken cancellationToken)
public static Task WriteToResponse(HttpResponse response, IRequest request, object result, CancellationToken cancellationToken)
{
if (result == null)
{
@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services
response.StatusCode = (int)HttpStatusCode.NoContent;
}
response.OriginalResponse.ContentLength = 0;
response.ContentLength = 0;
return Task.CompletedTask;
}
@ -41,7 +41,6 @@ namespace Emby.Server.Implementations.Services
httpResult.RequestContext = request;
response.StatusCode = httpResult.Status;
response.StatusDescription = httpResult.StatusCode.ToString();
}
var responseOptions = result as IHasHeaders;
@ -51,11 +50,11 @@ namespace Emby.Server.Implementations.Services
{
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
{
response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
response.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
continue;
}
response.AddHeader(responseHeaders.Key, responseHeaders.Value);
response.Headers.Add(responseHeaders.Key, responseHeaders.Value);
}
}
@ -74,31 +73,31 @@ namespace Emby.Server.Implementations.Services
switch (result)
{
case IAsyncStreamWriter asyncStreamWriter:
return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
return asyncStreamWriter.WriteToAsync(response.Body, cancellationToken);
case IStreamWriter streamWriter:
streamWriter.WriteTo(response.OutputStream);
streamWriter.WriteTo(response.Body);
return Task.CompletedTask;
case FileWriter fileWriter:
return fileWriter.WriteToAsync(response, cancellationToken);
case Stream stream:
return CopyStream(stream, response.OutputStream);
return CopyStream(stream, response.Body);
case byte[] bytes:
response.ContentType = "application/octet-stream";
response.OriginalResponse.ContentLength = bytes.Length;
response.ContentLength = bytes.Length;
if (bytes.Length > 0)
{
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
return response.Body.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
}
return Task.CompletedTask;
case string responseText:
var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
response.OriginalResponse.ContentLength = responseTextAsBytes.Length;
response.ContentLength = responseTextAsBytes.Length;
if (responseTextAsBytes.Length > 0)
{
return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
return response.Body.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
}
return Task.CompletedTask;
@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.Services
}
}
public static async Task WriteObject(IRequest request, object result, IResponse response)
public static async Task WriteObject(IRequest request, object result, HttpResponse response)
{
var contentType = request.ResponseContentType;
var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
@ -127,11 +126,11 @@ namespace Emby.Server.Implementations.Services
ms.Position = 0;
var contentLength = ms.Length;
response.OriginalResponse.ContentLength = contentLength;
response.ContentLength = contentLength;
if (contentLength > 0)
{
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
await ms.CopyToAsync(response.Body).ConfigureAwait(false);
}
}
}

View file

@ -147,7 +147,6 @@ namespace Emby.Server.Implementations.Services
public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
{
req.Dto = requestDto;
var requestType = requestDto.GetType();
req.OperationName = requestType.Name;
@ -161,9 +160,6 @@ namespace Emby.Server.Implementations.Services
serviceRequiresContext.Request = req;
}
if (req.Dto == null) // Don't override existing batched DTO[]
req.Dto = requestDto;
//Executes the service and returns the result
return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
}

View file

@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.Services
foreach (var requestFilter in actionContext.RequestFilters)
{
requestFilter.RequestFilter(request, request.Response, requestDto);
if (request.Response.OriginalResponse.HasStarted)
if (request.Response.HasStarted)
{
Task.FromResult<object>(null);
}

View file

@ -5,20 +5,21 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Services
{
public class ServiceHandler
{
public RestPath RestPath { get; }
private RestPath _restPath;
public string ResponseContentType { get; }
private string _responseContentType;
internal ServiceHandler(RestPath restPath, string responseContentType)
{
RestPath = restPath;
ResponseContentType = responseContentType;
_restPath = restPath;
_responseContentType = responseContentType;
}
protected static Task<object> CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
@ -54,7 +55,7 @@ namespace Emby.Server.Implementations.Services
private static string GetFormatContentType(string format)
{
//built-in formats
// built-in formats
switch (format)
{
case "json": return "application/json";
@ -63,16 +64,16 @@ namespace Emby.Server.Implementations.Services
}
}
public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, IResponse httpRes, ILogger logger, CancellationToken cancellationToken)
public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken)
{
httpReq.Items["__route"] = RestPath;
httpReq.Items["__route"] = _restPath;
if (ResponseContentType != null)
if (_responseContentType != null)
{
httpReq.ResponseContentType = ResponseContentType;
httpReq.ResponseContentType = _responseContentType;
}
var request = httpReq.Dto = await CreateRequest(httpHost, httpReq, RestPath, logger).ConfigureAwait(false);
var request = await CreateRequest(httpHost, httpReq, _restPath, logger).ConfigureAwait(false);
httpHost.ApplyRequestFilters(httpReq, httpRes, request);
@ -94,7 +95,7 @@ namespace Emby.Server.Implementations.Services
if (RequireqRequestStream(requestType))
{
// Used by IRequiresRequestStream
var requestParams = await GetRequestParams(httpReq).ConfigureAwait(false);
var requestParams = GetRequestParams(httpReq.Response.HttpContext.Request);
var request = ServiceHandler.CreateRequest(httpReq, restPath, requestParams, host.CreateInstance(requestType));
var rawReq = (IRequiresRequestStream)request;
@ -103,7 +104,7 @@ namespace Emby.Server.Implementations.Services
}
else
{
var requestParams = await GetFlattenedRequestParams(httpReq).ConfigureAwait(false);
var requestParams = GetFlattenedRequestParams(httpReq.Response.HttpContext.Request);
var requestDto = await CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType).ConfigureAwait(false);
@ -121,7 +122,7 @@ namespace Emby.Server.Implementations.Services
public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
{
var pathInfo = !restPath.IsWildCardPath
? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType)
? GetSanitizedPathInfo(httpReq.PathInfo, out _)
: httpReq.PathInfo;
return restPath.CreateRequest(pathInfo, requestParams, requestDto);
@ -130,56 +131,41 @@ namespace Emby.Server.Implementations.Services
/// <summary>
/// Duplicate Params are given a unique key by appending a #1 suffix
/// </summary>
private static async Task<Dictionary<string, string>> GetRequestParams(IRequest request)
private static Dictionary<string, string> GetRequestParams(HttpRequest request)
{
var map = new Dictionary<string, string>();
foreach (var name in request.QueryString.Keys)
foreach (var pair in request.Query)
{
if (name == null)
{
// thank you ASP.NET
continue;
}
var values = request.QueryString[name];
var values = pair.Value;
if (values.Count == 1)
{
map[name] = values[0];
map[pair.Key] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
{
map[name + (i == 0 ? "" : "#" + i)] = values[i];
map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
}
}
}
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
&& request.HasFormContentType)
{
var formData = await request.GetFormData().ConfigureAwait(false);
if (formData != null)
foreach (var pair in request.Form)
{
foreach (var name in formData.Keys)
var values = pair.Value;
if (values.Count == 1)
{
if (name == null)
map[pair.Key] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
{
// thank you ASP.NET
continue;
}
var values = formData.GetValues(name);
if (values.Count == 1)
{
map[name] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
{
map[name + (i == 0 ? "" : "#" + i)] = values[i];
}
map[pair.Key + (i == 0 ? string.Empty : "#" + i)] = values[i];
}
}
}
@ -189,43 +175,26 @@ namespace Emby.Server.Implementations.Services
}
private static bool IsMethod(string method, string expected)
{
return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
}
=> string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Duplicate params have their values joined together in a comma-delimited string
/// </summary>
private static async Task<Dictionary<string, string>> GetFlattenedRequestParams(IRequest request)
private static Dictionary<string, string> GetFlattenedRequestParams(HttpRequest request)
{
var map = new Dictionary<string, string>();
foreach (var name in request.QueryString.Keys)
foreach (var pair in request.Query)
{
if (name == null)
{
// thank you ASP.NET
continue;
}
map[name] = request.QueryString[name];
map[pair.Key] = pair.Value;
}
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
if ((IsMethod(request.Method, "POST") || IsMethod(request.Method, "PUT"))
&& request.HasFormContentType)
{
var formData = await request.GetFormData().ConfigureAwait(false);
if (formData != null)
foreach (var pair in request.Form)
{
foreach (var name in formData.Keys)
{
if (name == null)
{
// thank you ASP.NET
continue;
}
map[name] = formData[name];
}
map[pair.Key] = pair.Value;
}
}

View file

@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Session
{
var dict = new Dictionary<string, string>();
dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N")).ToArray());
dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
if (command.StartPositionTicks.HasValue)
{

View file

@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Session
{
if (string.IsNullOrEmpty(info.MediaSourceId))
{
info.MediaSourceId = info.ItemId.ToString("N");
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Session
Client = appName,
DeviceId = deviceId,
ApplicationVersion = appVersion,
Id = key.GetMD5().ToString("N"),
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
ServerId = _appHost.SystemId
};
@ -845,7 +845,7 @@ namespace Emby.Server.Implementations.Session
// Normalize
if (string.IsNullOrEmpty(info.MediaSourceId))
{
info.MediaSourceId = info.ItemId.ToString("N");
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@ -1029,7 +1029,7 @@ namespace Emby.Server.Implementations.Session
private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
{
var controllers = session.SessionControllers.ToArray();
var messageId = Guid.NewGuid().ToString("N");
var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
foreach (var controller in controllers)
{
@ -1041,7 +1041,7 @@ namespace Emby.Server.Implementations.Session
{
IEnumerable<Task> GetTasks()
{
var messageId = Guid.NewGuid().ToString("N");
var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
foreach (var session in sessions)
{
var controllers = session.SessionControllers;
@ -1234,7 +1234,7 @@ namespace Emby.Server.Implementations.Session
AssertCanControl(session, controllingSession);
if (!controllingSession.UserId.Equals(Guid.Empty))
{
command.ControllingUserId = controllingSession.UserId.ToString("N");
command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture);
}
}
@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session
DeviceId = deviceId,
DeviceName = deviceName,
UserId = user.Id,
AccessToken = Guid.NewGuid().ToString("N"),
AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
UserName = user.Name
};
@ -1822,6 +1822,7 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
return SendMessageToSessions(sessions, name, data, cancellationToken);
}
@ -1831,6 +1832,7 @@ namespace Emby.Server.Implementations.Session
var sessions = Sessions
.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
return SendMessageToSessions(sessions, name, data, cancellationToken);
}

View file

@ -1,647 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
internal static string GetParameter(ReadOnlySpan<char> header, string attr)
{
int ap = header.IndexOf(attr.AsSpan(), StringComparison.Ordinal);
if (ap == -1)
{
return null;
}
ap += attr.Length;
if (ap >= header.Length)
{
return null;
}
char ending = header[ap];
if (ending != '"')
{
ending = ' ';
}
var slice = header.Slice(ap + 1);
int end = slice.IndexOf(ending);
if (end == -1)
{
return ending == '"' ? null : header.Slice(ap).ToString();
}
return slice.Slice(0, end - ap - 1).ToString();
}
private async Task LoadMultiPart(WebROCollection form)
{
string boundary = GetParameter(ContentType.AsSpan(), "; boundary=");
if (boundary == null)
{
return;
}
using (var requestStream = InputStream)
{
// DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request
// Not ending with \r\n?
var ms = new MemoryStream(32 * 1024);
await requestStream.CopyToAsync(ms).ConfigureAwait(false);
var input = ms;
ms.WriteByte((byte)'\r');
ms.WriteByte((byte)'\n');
input.Position = 0;
// Uncomment to debug
// var content = new StreamReader(ms).ReadToEnd();
// Console.WriteLine(boundary + "::" + content);
// input.Position = 0;
var multi_part = new HttpMultipart(input, boundary, ContentEncoding);
HttpMultipart.Element e;
while ((e = multi_part.ReadNextElement()) != null)
{
if (e.Filename == null)
{
byte[] copy = new byte[e.Length];
input.Position = e.Start;
await input.ReadAsync(copy, 0, (int)e.Length).ConfigureAwait(false);
form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length));
}
else
{
// We use a substream, as in 2.x we will support large uploads streamed to disk,
files[e.Name] = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
}
}
}
}
public async Task<QueryParamCollection> GetFormData()
{
var form = new WebROCollection();
files = new Dictionary<string, HttpPostedFile>();
if (IsContentType("multipart/form-data"))
{
await LoadMultiPart(form).ConfigureAwait(false);
}
else if (IsContentType("application/x-www-form-urlencoded"))
{
await LoadWwwForm(form).ConfigureAwait(false);
}
if (validate_form && !checked_form)
{
checked_form = true;
ValidateNameValueCollection("Form", form);
}
return form;
}
public string Accept => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Accept]) ? null : request.Headers[HeaderNames.Accept].ToString();
public string Authorization => StringValues.IsNullOrEmpty(request.Headers[HeaderNames.Authorization]) ? null : request.Headers[HeaderNames.Authorization].ToString();
protected bool validate_form { get; set; }
protected bool checked_form { get; set; }
private static void ThrowValidationException(string name, string key, string value)
{
string v = "\"" + value + "\"";
if (v.Length > 20)
{
v = v.Substring(0, 16) + "...\"";
}
string msg = string.Format(
CultureInfo.InvariantCulture,
"A potentially dangerous Request.{0} value was detected from the client ({1}={2}).",
name,
key,
v);
throw new Exception(msg);
}
private static void ValidateNameValueCollection(string name, QueryParamCollection coll)
{
if (coll == null)
{
return;
}
foreach (var pair in coll)
{
var key = pair.Name;
var val = pair.Value;
if (val != null && val.Length > 0 && IsInvalidString(val))
{
ThrowValidationException(name, key, val);
}
}
}
internal static bool IsInvalidString(string val)
=> IsInvalidString(val, out var validationFailureIndex);
internal static bool IsInvalidString(string val, out int validationFailureIndex)
{
validationFailureIndex = 0;
int len = val.Length;
if (len < 2)
{
return false;
}
char current = val[0];
for (int idx = 1; idx < len; idx++)
{
char next = val[idx];
// See http://secunia.com/advisories/14325
if (current == '<' || current == '\xff1c')
{
if (next == '!' || next < ' '
|| (next >= 'a' && next <= 'z')
|| (next >= 'A' && next <= 'Z'))
{
validationFailureIndex = idx - 1;
return true;
}
}
else if (current == '&' && next == '#')
{
validationFailureIndex = idx - 1;
return true;
}
current = next;
}
return false;
}
private bool IsContentType(string ct)
{
if (ContentType == null)
{
return false;
}
return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase);
}
private async Task LoadWwwForm(WebROCollection form)
{
using (var input = InputStream)
{
using (var ms = new MemoryStream())
{
await input.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
using (var s = new StreamReader(ms, ContentEncoding))
{
var key = new StringBuilder();
var value = new StringBuilder();
int c;
while ((c = s.Read()) != -1)
{
if (c == '=')
{
value.Length = 0;
while ((c = s.Read()) != -1)
{
if (c == '&')
{
AddRawKeyValue(form, key, value);
break;
}
else
{
value.Append((char)c);
}
}
if (c == -1)
{
AddRawKeyValue(form, key, value);
return;
}
}
else if (c == '&')
{
AddRawKeyValue(form, key, value);
}
else
{
key.Append((char)c);
}
}
if (c == -1)
{
AddRawKeyValue(form, key, value);
}
}
}
}
}
private static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value)
{
form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString()));
key.Length = 0;
value.Length = 0;
}
private Dictionary<string, HttpPostedFile> files;
private class WebROCollection : QueryParamCollection
{
public override string ToString()
{
var result = new StringBuilder();
foreach (var pair in this)
{
if (result.Length > 0)
{
result.Append('&');
}
var key = pair.Name;
if (key != null && key.Length > 0)
{
result.Append(key);
result.Append('=');
}
result.Append(pair.Value);
}
return result.ToString();
}
}
private class HttpMultipart
{
public class Element
{
public string ContentType { get; set; }
public string Name { get; set; }
public string Filename { get; set; }
public Encoding Encoding { get; set; }
public long Start { get; set; }
public long Length { get; set; }
public override string ToString()
{
return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture);
}
}
private const byte LF = (byte)'\n';
private const byte CR = (byte)'\r';
private Stream data;
private string boundary;
private byte[] boundaryBytes;
private byte[] buffer;
private bool atEof;
private Encoding encoding;
private StringBuilder sb;
// See RFC 2046
// In the case of multipart entities, in which one or more different
// sets of data are combined in a single body, a "multipart" media type
// field must appear in the entity's header. The body must then contain
// one or more body parts, each preceded by a boundary delimiter line,
// and the last one followed by a closing boundary delimiter line.
// After its boundary delimiter line, each body part then consists of a
// header area, a blank line, and a body area. Thus a body part is
// similar to an RFC 822 message in syntax, but different in meaning.
public HttpMultipart(Stream data, string b, Encoding encoding)
{
this.data = data;
boundary = b;
boundaryBytes = encoding.GetBytes(b);
buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--'
this.encoding = encoding;
sb = new StringBuilder();
}
public Element ReadNextElement()
{
if (atEof || ReadBoundary())
{
return null;
}
var elem = new Element();
ReadOnlySpan<char> header;
while ((header = ReadLine().AsSpan()).Length != 0)
{
if (header.StartsWith("Content-Disposition:".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
elem.Name = GetContentDispositionAttribute(header, "name");
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
}
else if (header.StartsWith("Content-Type:".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
elem.ContentType = header.Slice("Content-Type:".Length).Trim().ToString();
elem.Encoding = GetEncoding(elem.ContentType);
}
}
long start = data.Position;
elem.Start = start;
long pos = MoveToNextBoundary();
if (pos == -1)
{
return null;
}
elem.Length = pos - start;
return elem;
}
private string ReadLine()
{
// CRLF or LF are ok as line endings.
bool got_cr = false;
int b = 0;
sb.Length = 0;
while (true)
{
b = data.ReadByte();
if (b == -1)
{
return null;
}
if (b == LF)
{
break;
}
got_cr = b == CR;
sb.Append((char)b);
}
if (got_cr)
{
sb.Length--;
}
return sb.ToString();
}
private static string GetContentDispositionAttribute(ReadOnlySpan<char> l, string name)
{
int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal);
if (idx < 0)
{
return null;
}
int begin = idx + name.Length + "=\"".Length;
int end = l.Slice(begin).IndexOf('"');
if (end < 0)
{
return null;
}
if (begin == end)
{
return string.Empty;
}
return l.Slice(begin, end - begin).ToString();
}
private string GetContentDispositionAttributeWithEncoding(ReadOnlySpan<char> l, string name)
{
int idx = l.IndexOf((name + "=\"").AsSpan(), StringComparison.Ordinal);
if (idx < 0)
{
return null;
}
int begin = idx + name.Length + "=\"".Length;
int end = l.Slice(begin).IndexOf('"');
if (end < 0)
{
return null;
}
if (begin == end)
{
return string.Empty;
}
ReadOnlySpan<char> temp = l.Slice(begin, end - begin);
byte[] source = new byte[temp.Length];
for (int i = temp.Length - 1; i >= 0; i--)
{
source[i] = (byte)temp[i];
}
return encoding.GetString(source, 0, source.Length);
}
private bool ReadBoundary()
{
try
{
string line;
do
{
line = ReadLine();
}
while (line.Length == 0);
if (line[0] != '-' || line[1] != '-')
{
return false;
}
if (!line.EndsWith(boundary, StringComparison.Ordinal))
{
return true;
}
}
catch
{
}
return false;
}
private static bool CompareBytes(byte[] orig, byte[] other)
{
for (int i = orig.Length - 1; i >= 0; i--)
{
if (orig[i] != other[i])
{
return false;
}
}
return true;
}
private long MoveToNextBoundary()
{
long retval = 0;
bool got_cr = false;
int state = 0;
int c = data.ReadByte();
while (true)
{
if (c == -1)
{
return -1;
}
if (state == 0 && c == LF)
{
retval = data.Position - 1;
if (got_cr)
{
retval--;
}
state = 1;
c = data.ReadByte();
}
else if (state == 0)
{
got_cr = c == CR;
c = data.ReadByte();
}
else if (state == 1 && c == '-')
{
c = data.ReadByte();
if (c == -1)
{
return -1;
}
if (c != '-')
{
state = 0;
got_cr = false;
continue; // no ReadByte() here
}
int nread = data.Read(buffer, 0, buffer.Length);
int bl = buffer.Length;
if (nread != bl)
{
return -1;
}
if (!CompareBytes(boundaryBytes, buffer))
{
state = 0;
data.Position = retval + 2;
if (got_cr)
{
data.Position++;
got_cr = false;
}
c = data.ReadByte();
continue;
}
if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
{
atEof = true;
}
else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
{
state = 0;
data.Position = retval + 2;
if (got_cr)
{
data.Position++;
got_cr = false;
}
c = data.ReadByte();
continue;
}
data.Position = retval + 2;
if (got_cr)
{
data.Position++;
}
break;
}
else
{
// state == 1
state = 0; // no ReadByte() here
}
}
return retval;
}
private static string StripPath(string path)
{
if (path == null || path.Length == 0)
{
return path;
}
if (path.IndexOf(":\\", StringComparison.Ordinal) != 1
&& !path.StartsWith("\\\\", StringComparison.Ordinal))
{
return path;
}
return path.Substring(path.LastIndexOf('\\') + 1);
}
}
}
}

View file

@ -1,57 +1,56 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Linq;
using System.Text;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
using IResponse = MediaBrowser.Model.Services.IResponse;
namespace Emby.Server.Implementations.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
private readonly HttpRequest request;
public const string FormUrlEncoded = "application/x-www-form-urlencoded";
public const string MultiPartFormData = "multipart/form-data";
public const string Soap11 = "text/xml; charset=utf-8";
public WebSocketSharpRequest(HttpRequest httpContext, HttpResponse response, string operationName, ILogger logger)
private string _remoteIp;
private Dictionary<string, object> _items;
private string _responseContentType;
public WebSocketSharpRequest(HttpRequest httpRequest, HttpResponse httpResponse, string operationName, ILogger logger)
{
this.OperationName = operationName;
this.request = httpContext;
this.Response = new WebSocketSharpResponse(logger, response);
this.Request = httpRequest;
this.Response = httpResponse;
}
public HttpRequest HttpRequest => request;
public string Accept => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Accept]) ? null : Request.Headers[HeaderNames.Accept].ToString();
public IResponse Response { get; }
public string Authorization => StringValues.IsNullOrEmpty(Request.Headers[HeaderNames.Authorization]) ? null : Request.Headers[HeaderNames.Authorization].ToString();
public HttpRequest Request { get; }
public HttpResponse Response { get; }
public string OperationName { get; set; }
public object Dto { get; set; }
public string RawUrl => Request.GetEncodedPathAndQuery();
public string RawUrl => request.GetEncodedPathAndQuery();
public string AbsoluteUri => Request.GetDisplayUrl().TrimEnd('/');
public string AbsoluteUri => request.GetDisplayUrl().TrimEnd('/');
// Header[name] returns "" when undefined
private string GetHeader(string name) => request.Headers[name].ToString();
private string remoteIp;
public string RemoteIp
{
get
{
if (remoteIp != null)
if (_remoteIp != null)
{
return remoteIp;
return _remoteIp;
}
IPAddress ip;
@ -62,14 +61,51 @@ namespace Emby.Server.Implementations.SocketSharp
{
if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
{
ip = request.HttpContext.Connection.RemoteIpAddress;
ip = Request.HttpContext.Connection.RemoteIpAddress;
}
}
return remoteIp = NormalizeIp(ip).ToString();
return _remoteIp = NormalizeIp(ip).ToString();
}
}
public string[] AcceptTypes => Request.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
public Dictionary<string, object> Items => _items ?? (_items = new Dictionary<string, object>());
public string ResponseContentType
{
get =>
_responseContentType
?? (_responseContentType = GetResponseContentType(Request));
set => this._responseContentType = value;
}
public string PathInfo => Request.Path.Value;
public string UserAgent => Request.Headers[HeaderNames.UserAgent];
public IHeaderDictionary Headers => Request.Headers;
public IQueryCollection QueryString => Request.Query;
public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
public string HttpMethod => Request.Method;
public string Verb => HttpMethod;
public string ContentType => Request.ContentType;
public Uri UrlReferrer => Request.GetTypedHeaders().Referer;
public Stream InputStream => Request.Body;
public long ContentLength => Request.ContentLength ?? 0;
private string GetHeader(string name) => Request.Headers[name].ToString();
private static IPAddress NormalizeIp(IPAddress ip)
{
if (ip.IsIPv4MappedToIPv6)
@ -80,22 +116,6 @@ namespace Emby.Server.Implementations.SocketSharp
return ip;
}
public string[] AcceptTypes => request.Headers.GetCommaSeparatedValues(HeaderNames.Accept);
private Dictionary<string, object> items;
public Dictionary<string, object> Items => items ?? (items = new Dictionary<string, object>());
private string responseContentType;
public string ResponseContentType
{
get =>
responseContentType
?? (responseContentType = GetResponseContentType(HttpRequest));
set => this.responseContentType = value;
}
public const string FormUrlEncoded = "application/x-www-form-urlencoded";
public const string MultiPartFormData = "multipart/form-data";
public static string GetResponseContentType(HttpRequest httpReq)
{
var specifiedContentType = GetQueryStringContentType(httpReq);
@ -152,8 +172,6 @@ namespace Emby.Server.Implementations.SocketSharp
return serverDefaultContentType;
}
public const string Soap11 = "text/xml; charset=utf-8";
public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes)
{
if (contentTypes == null || request.ContentType == null)
@ -224,105 +242,5 @@ namespace Emby.Server.Implementations.SocketSharp
var pos = strVal.IndexOf(needle);
return pos == -1 ? strVal : strVal.Slice(0, pos);
}
public string PathInfo => this.request.Path.Value;
public string UserAgent => request.Headers[HeaderNames.UserAgent];
public IHeaderDictionary Headers => request.Headers;
public IQueryCollection QueryString => request.Query;
public bool IsLocal => string.Equals(request.HttpContext.Connection.LocalIpAddress.ToString(), request.HttpContext.Connection.RemoteIpAddress.ToString());
private string httpMethod;
public string HttpMethod =>
httpMethod
?? (httpMethod = request.Method);
public string Verb => HttpMethod;
public string ContentType => request.ContentType;
private Encoding ContentEncoding
{
get
{
// TODO is this necessary?
if (UserAgent != null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP"))
{
string postDataCharset = Headers["x-up-devcap-post-charset"];
if (!string.IsNullOrEmpty(postDataCharset))
{
try
{
return Encoding.GetEncoding(postDataCharset);
}
catch (ArgumentException)
{
}
}
}
return request.GetTypedHeaders().ContentType.Encoding ?? Encoding.UTF8;
}
}
public Uri UrlReferrer => request.GetTypedHeaders().Referer;
public static Encoding GetEncoding(string contentTypeHeader)
{
var param = GetParameter(contentTypeHeader.AsSpan(), "charset=");
if (param == null)
{
return null;
}
try
{
return Encoding.GetEncoding(param);
}
catch (ArgumentException)
{
return null;
}
}
public Stream InputStream => request.Body;
public long ContentLength => request.ContentLength ?? 0;
private IHttpFile[] httpFiles;
public IHttpFile[] Files
{
get
{
if (httpFiles != null)
{
return httpFiles;
}
if (files == null)
{
return httpFiles = Array.Empty<IHttpFile>();
}
var values = files.Values;
httpFiles = new IHttpFile[values.Count];
for (int i = 0; i < values.Count; i++)
{
var reqFile = values.ElementAt(i);
httpFiles[i] = new HttpFile
{
ContentType = reqFile.ContentType,
ContentLength = reqFile.ContentLength,
FileName = reqFile.FileName,
InputStream = reqFile.InputStream,
};
}
return httpFiles;
}
}
}
}

View file

@ -1,98 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using IRequest = MediaBrowser.Model.Services.IRequest;
namespace Emby.Server.Implementations.SocketSharp
{
public class WebSocketSharpResponse : IResponse
{
private readonly ILogger _logger;
public WebSocketSharpResponse(ILogger logger, HttpResponse response)
{
_logger = logger;
OriginalResponse = response;
}
public HttpResponse OriginalResponse { get; }
public int StatusCode
{
get => OriginalResponse.StatusCode;
set => OriginalResponse.StatusCode = value;
}
public string StatusDescription { get; set; }
public string ContentType
{
get => OriginalResponse.ContentType;
set => OriginalResponse.ContentType = value;
}
public void AddHeader(string name, string value)
{
if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
{
ContentType = value;
return;
}
OriginalResponse.Headers.Add(name, value);
}
public void Redirect(string url)
{
OriginalResponse.Redirect(url);
}
public Stream OutputStream => OriginalResponse.Body;
public bool SendChunked { get; set; }
const int StreamCopyToBufferSize = 81920;
public async Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, IFileSystem fileSystem, IStreamHelper streamHelper, CancellationToken cancellationToken)
{
var allowAsync = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
//if (count <= 0)
//{
// allowAsync = true;
//}
var fileOpenOptions = FileOpenOptions.SequentialScan;
if (allowAsync)
{
fileOpenOptions |= FileOpenOptions.Asynchronous;
}
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
using (var fs = fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
{
if (offset > 0)
{
fs.Position = offset;
}
if (count > 0)
{
await streamHelper.CopyToAsync(fs, OutputStream, count, cancellationToken).ConfigureAwait(false);
}
else
{
await fs.CopyToAsync(OutputStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.TV
{
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
.ToArray();
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Udp
private bool _isDisposed;
private readonly List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>();
private readonly List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>();
private readonly IServerApplicationHost _appHost;
private readonly IJsonSerializer _json;
@ -43,9 +44,9 @@ namespace Emby.Server.Implementations.Udp
AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message);
}
private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)
private void AddMessageResponder(string message, bool isSubstring, Func<string, IPEndPoint, Encoding, CancellationToken, Task> responder)
{
_responders.Add(new Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
_responders.Add(new Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
}
/// <summary>
@ -83,7 +84,7 @@ namespace Emby.Server.Implementations.Udp
}
}
private Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
private Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
{
var text = encoding.GetString(buffer, 0, bytesReceived);
var responder = _responders.FirstOrDefault(i =>
@ -99,10 +100,10 @@ namespace Emby.Server.Implementations.Udp
{
return null;
}
return new Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>(text, responder);
return new Tuple<string, Tuple<string, bool, Func<string, IPEndPoint, Encoding, CancellationToken, Task>>>(text, responder);
}
private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding, CancellationToken cancellationToken)
private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, CancellationToken cancellationToken)
{
var parts = messageText.Split('|');
@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Udp
}
}
public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint, CancellationToken cancellationToken)
public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken)
{
if (_isDisposed)
{

View file

@ -18,7 +18,6 @@ using Jellyfin.Drawing.Skia;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -41,12 +40,12 @@ namespace Jellyfin.Server
// For backwards compatibility.
// Modify any input arguments now which start with single-hyphen to POSIX standard
// double-hyphen to allow parsing by CommandLineParser package.
const string pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx
const string substitution = @"-$1"; // Prepend with additional single-hyphen
var regex = new Regex(pattern);
const string Pattern = @"^(-[^-\s]{2})"; // Match -xx, not -x, not --xx, not xx
const string Substitution = @"-$1"; // Prepend with additional single-hyphen
var regex = new Regex(Pattern);
for (var i = 0; i < args.Length; i++)
{
args[i] = regex.Replace(args[i], substitution);
args[i] = regex.Replace(args[i], Substitution);
}
// Parse the command line arguments and either start the app or exit indicating error
@ -134,7 +133,7 @@ namespace Jellyfin.Server
Batteries_V2.Init();
if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
{
Console.WriteLine("WARN: Failed to enable shared cache for SQLite");
_logger.LogWarning("Failed to enable shared cache for SQLite");
}
using (var appHost = new CoreAppHost(
@ -143,7 +142,7 @@ namespace Jellyfin.Server
options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
new NullImageEncoder(),
new NetworkManager(_loggerFactory),
new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
appConfig))
{
await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false);

View file

@ -133,8 +133,21 @@ namespace MediaBrowser.Api.Devices
var album = Request.QueryString["Album"];
var id = Request.QueryString["Id"];
var name = Request.QueryString["Name"];
var req = Request.Response.HttpContext.Request;
if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1)
if (req.HasFormContentType)
{
var file = req.Form.Files.Count == 0 ? null : req.Form.Files[0];
return _deviceManager.AcceptCameraUpload(deviceId, file.OpenReadStream(), new LocalFileInfo
{
MimeType = file.ContentType,
Album = album,
Name = name,
Id = id
});
}
else
{
return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
{
@ -144,18 +157,6 @@ namespace MediaBrowser.Api.Devices
Id = id
});
}
else
{
var file = Request.Files.Length == 0 ? null : Request.Files[0];
return _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
{
MimeType = file.ContentType,
Album = album,
Name = name,
Id = id
});
}
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@ -537,7 +538,7 @@ namespace MediaBrowser.Api.Images
if (item == null)
{
throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N")));
throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N", CultureInfo.InvariantCulture)));
}
}

Some files were not shown because too many files have changed in this diff Show more