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 - task: CmdLine@2
displayName: Execute ABI compatibility check tool displayName: Execute ABI compatibility check tool
inputs: 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 workingDirectory: $(System.ArtifactsDirectory) # Optional
#failOnStderr: false # 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) - [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic) - [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h) - [joern-h](https://github.com/joern-h)
- [Khinenw](https://github.com/HelloWorld017)
# Emby Contributors # Emby Contributors

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -55,7 +55,7 @@ namespace Emby.Dlna.Eventing
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
{ {
var timeout = ParseTimeout(requestedTimeoutString) ?? 300; 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 // Remove logging for now because some devices are sending this very frequently
// TODO re-enable with dlna debug logging setting // TODO re-enable with dlna debug logging setting

View file

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

View file

@ -1,5 +1,7 @@
using System; using System;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
@ -14,7 +16,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events; using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -141,7 +142,7 @@ namespace Emby.Dlna.PlayTo
return usn; return usn;
} }
return usn.GetMD5().ToString("N"); return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
} }
private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken) private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
@ -156,7 +157,7 @@ namespace Emby.Dlna.PlayTo
} }
else 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); 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); _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
string serverAddress; 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); serverAddress = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
} }

View file

@ -9,7 +9,7 @@ namespace Emby.Dlna.Profiles
{ {
Name = "Dish Hopper-Joey"; 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 Identification = new DeviceIdentification
{ {

View file

@ -28,7 +28,7 @@
<MaxStaticBitrate>140000000</MaxStaticBitrate> <MaxStaticBitrate>140000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" /> <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> <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
<RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
<RequiresPlainFolders>false</RequiresPlainFolders> <RequiresPlainFolders>false</RequiresPlainFolders>

View file

@ -454,14 +454,14 @@ namespace Emby.Drawing
// Optimization // Optimization
if (imageEnhancers.Length == 0) 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 // 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(); var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
cacheKeys.Add(originalImagePath + dateModified.Ticks); 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) private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@ -480,7 +480,7 @@ namespace Emby.Drawing
{ {
try 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"; string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -19,11 +20,44 @@ namespace Emby.Server.Implementations.AppBase
/// </summary> /// </summary>
public abstract class BaseConfigurationManager : IConfigurationManager 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> /// <summary>
/// Gets the type of the configuration. /// The _configuration loaded.
/// </summary> /// </summary>
/// <value>The type of the configuration.</value> private bool _configurationLoaded;
protected abstract Type ConfigurationType { get; }
/// <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> /// <summary>
/// Occurs when [configuration updated]. /// Occurs when [configuration updated].
@ -40,6 +74,12 @@ namespace Emby.Server.Implementations.AppBase
/// </summary> /// </summary>
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated; 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> /// <summary>
/// Gets the logger. /// Gets the logger.
/// </summary> /// </summary>
@ -56,20 +96,7 @@ namespace Emby.Server.Implementations.AppBase
/// </summary> /// </summary>
/// <value>The application paths.</value> /// <value>The application paths.</value>
public IApplicationPaths CommonApplicationPaths { get; private set; } 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> /// <summary>
/// Gets the system configuration /// Gets the system configuration
/// </summary> /// </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) public virtual void AddParts(IEnumerable<IConfigurationFactory> factories)
{ {
_configurationFactories = factories.ToArray(); _configurationFactories = factories.ToArray();
@ -171,6 +178,7 @@ namespace Emby.Server.Implementations.AppBase
private void UpdateCachePath() private void UpdateCachePath()
{ {
string cachePath; string cachePath;
// If the configuration file has no entry (i.e. not set in UI) // If the configuration file has no entry (i.e. not set in UI)
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath)) if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
{ {
@ -207,12 +215,16 @@ namespace Emby.Server.Implementations.AppBase
var newPath = newConfig.CachePath; var newPath = newConfig.CachePath;
if (!string.IsNullOrWhiteSpace(newPath) if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath)) && !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath, StringComparison.Ordinal))
{ {
// Validate // Validate
if (!Directory.Exists(newPath)) 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); EnsureWriteAccess(newPath);
@ -223,11 +235,9 @@ namespace Emby.Server.Implementations.AppBase
{ {
var file = Path.Combine(path, Guid.NewGuid().ToString()); var file = Path.Combine(path, Guid.NewGuid().ToString());
File.WriteAllText(file, string.Empty); 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) private string GetConfigurationFile(string key)
{ {
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml"); return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");

View file

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

View file

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

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Collections
public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids) 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) 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> public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
{ {
{"HttpListenerHost:DefaultRedirectPath", "web/index.html"}, { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
{"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"} { "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }
}; };
} }
} }

View file

@ -8,7 +8,7 @@ using MediaBrowser.Model.Cryptography;
namespace Emby.Server.Implementations.Cryptography namespace Emby.Server.Implementations.Cryptography
{ {
public class CryptographyProvider : ICryptoProvider public class CryptographyProvider : ICryptoProvider, IDisposable
{ {
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>() private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
{ {
@ -28,26 +28,28 @@ namespace Emby.Server.Implementations.Cryptography
"System.Security.Cryptography.SHA512" "System.Security.Cryptography.SHA512"
}; };
public string DefaultHashMethod => "PBKDF2";
private RandomNumberGenerator _randomNumberGenerator; private RandomNumberGenerator _randomNumberGenerator;
private const int _defaultIterations = 1000; private const int _defaultIterations = 1000;
private bool _disposed = false;
public CryptographyProvider() public CryptographyProvider()
{ {
//FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto // 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 // 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 // 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 // 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(); _randomNumberGenerator = RandomNumberGenerator.Create();
} }
public Guid GetMD5(string str) public string DefaultHashMethod => "PBKDF2";
{
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
}
[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) public byte[] ComputeSHA1(byte[] bytes)
{ {
using (var provider = SHA1.Create()) 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) public byte[] ComputeMD5(Stream str)
{ {
using (var provider = MD5.Create()) 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) public byte[] ComputeMD5(byte[] bytes)
{ {
using (var provider = MD5.Create()) using (var provider = MD5.Create())
@ -73,9 +77,7 @@ namespace Emby.Server.Implementations.Cryptography
} }
public IEnumerable<string> GetSupportedHashMethods() public IEnumerable<string> GetSupportedHashMethods()
{ => _supportedHashMethods;
return _supportedHashMethods;
}
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations) 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) public byte[] ComputeHash(string hashMethod, byte[] bytes)
{ => ComputeHash(hashMethod, bytes, Array.Empty<byte>());
return ComputeHash(hashMethod, bytes, Array.Empty<byte>());
}
public byte[] ComputeHashWithDefaultMethod(byte[] bytes) public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
{ => ComputeHash(DefaultHashMethod, bytes);
return ComputeHash(DefaultHashMethod, bytes);
}
public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt) 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) public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
{ => PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
}
public byte[] ComputeHash(PasswordHash hash) public byte[] ComputeHash(PasswordHash hash)
{ {
int iterations = _defaultIterations; int iterations = _defaultIterations;
if (!hash.Parameters.ContainsKey("iterations")) 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 throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}");
{
iterations = int.Parse(hash.Parameters["iterations"]);
}
catch (Exception e)
{
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e);
}
} }
return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations); return PBKDF2(hash.Id, hash.Hash, hash.Salt, iterations);
} }
public byte[] GenerateSalt() public byte[] GenerateSalt()
@ -164,5 +152,29 @@ namespace Emby.Server.Implementations.Cryptography
_randomNumberGenerator.GetBytes(salt); _randomNumberGenerator.GetBytes(salt);
return 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -182,7 +183,7 @@ namespace Emby.Server.Implementations.Data
return new DisplayPreferences 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.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) if (item is IHasProgramAttributes hasProgramAttributes)
{ {
@ -851,7 +851,7 @@ namespace Emby.Server.Implementations.Data
} }
else 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) if (item is Trailer trailer && trailer.TrailerTypes.Length > 0)
@ -3548,12 +3548,12 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ChannelId=@ChannelId"); whereClauses.Add("ChannelId=@ChannelId");
if (statement != null) 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) 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})"); whereClauses.Add($"ChannelId in ({inClause})");
} }
@ -4537,12 +4537,12 @@ namespace Emby.Server.Implementations.Data
} }
if (statement != null) if (statement != null)
{ {
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N")); statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
} }
} }
else if (queryTopParentIds.Length > 1) 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) if (enableItemsByName && includedItemByNameTypes.Count == 1)
{ {
@ -4574,7 +4574,7 @@ namespace Emby.Server.Implementations.Data
} }
if (query.AncestorIds.Length > 1) 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)); whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
} }
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)) if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
@ -4637,7 +4637,7 @@ namespace Emby.Server.Implementations.Data
foreach (var folderId in query.BoxSetLibraryFolders) 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) + ")"); 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]; var ancestorId = ancestorIds[i];
statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob()); statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob());
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N")); statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
} }
statement.Reset(); statement.Reset();
@ -5579,6 +5579,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
counts.TrailerCount = value; counts.TrailerCount = value;
} }
counts.ItemCount += value; counts.ItemCount += value;
} }

View file

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

View file

@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Devices
private string GetDevicePath(string id) 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) public ContentUploadHistory GetCameraUploadHistory(string deviceId)

View file

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

View file

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

View file

@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.EntryPoints
_lastProgressMessageTimes[item.Id] = DateTime.UtcNow; _lastProgressMessageTimes[item.Id] = DateTime.UtcNow;
var dict = new Dictionary<string, string>(); 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); dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture);
try try
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints
foreach (var collectionFolder in collectionFolders) foreach (var collectionFolder in collectionFolders)
{ {
var collectionFolderDict = new Dictionary<string, string>(); 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); collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture);
try try
@ -378,15 +378,15 @@ namespace Emby.Server.Implementations.EntryPoints
return new LibraryUpdateInfo 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() CollectionFolders = GetTopParentIds(newAndRemoved, allUserRootChildren).ToArray()
}; };
@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.EntryPoints
var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren); var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren);
foreach (var folder in 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
@ -134,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="e">The e.</param> /// <param name="e">The e.</param>
void userManager_UserDeleted(object sender, GenericEventArgs<User> e) 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) void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)

View file

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

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -195,7 +196,7 @@ namespace Emby.Server.Implementations.HttpClientManager
} }
var url = options.Url; 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); var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);

View file

@ -1,50 +1,43 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpServer namespace Emby.Server.Implementations.HttpServer
{ {
public class FileWriter : IHttpResult 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 readonly IStreamHelper _streamHelper;
private ILogger Logger { get; set; } private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; 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> /// <summary>
/// The _options /// The _options
/// </summary> /// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); 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) 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; _fileSystem = fileSystem;
Path = path; Path = path;
Logger = logger; _logger = logger;
RangeHeader = rangeHeader; RangeHeader = rangeHeader;
Headers[HeaderNames.ContentType] = contentType; Headers[HeaderNames.ContentType] = contentType;
@ -80,6 +73,88 @@ namespace Emby.Server.Implementations.HttpServer
Cookies = new List<Cookie>(); 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> /// <summary>
/// Sets the range values. /// Sets the range values.
/// </summary> /// </summary>
@ -106,59 +181,10 @@ namespace Emby.Server.Implementations.HttpServer
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}"; var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
Headers[HeaderNames.ContentRange] = rangeString; 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> public async Task WriteToAsync(HttpResponse response, CancellationToken cancellationToken)
/// 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)
{ {
try try
{ {
@ -176,16 +202,16 @@ namespace Emby.Server.Implementations.HttpServer
{ {
var extension = System.IO.Path.GetExtension(path); 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; offset = 0;
count = 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 finally
{ {
@ -193,18 +219,32 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
public string ContentType { get; set; } public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
public IRequest RequestContext { get; set; }
public object Response { get; set; }
public int Status { get; set; }
public HttpStatusCode StatusCode
{ {
get => (HttpStatusCode)Status; var fileOpenOptions = FileOpenOptions.SequentialScan;
set => Status = (int)value;
// 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.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Net;
@ -30,11 +29,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
public class HttpListenerHost : IHttpServer, IDisposable public class HttpListenerHost : IHttpServer, IDisposable
{ {
private string DefaultRedirectPath { get; set; } private readonly ILogger _logger;
public string[] UrlPrefixes { get; private set; }
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
@ -42,18 +37,15 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly IHttpListener _socketListener; private readonly IHttpListener _socketListener;
private readonly Func<Type, Func<string, object>> _funcParseFn; private readonly Func<Type, Func<string, object>> _funcParseFn;
private readonly string _defaultRedirectPath;
public Action<IRequest, IResponse, object>[] ResponseFilters { get; set; }
private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>(); private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
public static HttpListenerHost Instance { get; protected set; }
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
private bool _disposed = false;
public HttpListenerHost( public HttpListenerHost(
IServerApplicationHost applicationHost, IServerApplicationHost applicationHost,
ILoggerFactory loggerFactory, ILogger<HttpListenerHost> logger,
IServerConfigurationManager config, IServerConfigurationManager config,
IConfiguration configuration, IConfiguration configuration,
INetworkManager networkManager, INetworkManager networkManager,
@ -62,9 +54,9 @@ namespace Emby.Server.Implementations.HttpServer
IHttpListener socketListener) IHttpListener socketListener)
{ {
_appHost = applicationHost; _appHost = applicationHost;
Logger = loggerFactory.CreateLogger("HttpServer"); _logger = logger;
_config = config; _config = config;
DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"]; _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_networkManager = networkManager; _networkManager = networkManager;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
@ -74,12 +66,20 @@ namespace Emby.Server.Implementations.HttpServer
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s); _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this; 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; } 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) public object CreateInstance(Type type)
{ {
@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer
/// and no more processing should be done. /// and no more processing should be done.
/// </summary> /// </summary>
/// <returns></returns> /// <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 //Exec all RequestFilter attributes with Priority < 0
var attributes = GetRequestFilterAttributes(requestDto.GetType()); var attributes = GetRequestFilterAttributes(requestDto.GetType());
@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, Logger) var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger)
{ {
OnReceive = ProcessWebSocketMessageReceived, OnReceive = ProcessWebSocketMessageReceived,
Url = e.Url, Url = e.Url,
@ -215,16 +215,16 @@ namespace Emby.Server.Implementations.HttpServer
if (logExceptionStackTrace) if (logExceptionStackTrace)
{ {
Logger.LogError(ex, "Error processing request"); _logger.LogError(ex, "Error processing request");
} }
else if (logExceptionMessage) else if (logExceptionMessage)
{ {
Logger.LogError(ex.Message); _logger.LogError(ex.Message);
} }
var httpRes = httpReq.Response; var httpRes = httpReq.Response;
if (httpRes.OriginalResponse.HasStarted) if (httpRes.HasStarted)
{ {
return; return;
} }
@ -233,11 +233,11 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.StatusCode = statusCode; httpRes.StatusCode = statusCode;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";
await Write(httpRes, NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false); await httpRes.WriteAsync(NormalizeExceptionMessage(ex.Message)).ConfigureAwait(false);
} }
catch (Exception errorEx) 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.StatusCode = 503;
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
await Write(httpRes, "Server shutting down").ConfigureAwait(false); await httpRes.WriteAsync("Server shutting down", cancellationToken).ConfigureAwait(false);
return; return;
} }
@ -439,7 +439,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
httpRes.StatusCode = 400; httpRes.StatusCode = 400;
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
await Write(httpRes, "Invalid host").ConfigureAwait(false); await httpRes.WriteAsync("Invalid host", cancellationToken).ConfigureAwait(false);
return; return;
} }
@ -447,7 +447,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
httpRes.StatusCode = 403; httpRes.StatusCode = 403;
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
await Write(httpRes, "Forbidden").ConfigureAwait(false); await httpRes.WriteAsync("Forbidden", cancellationToken).ConfigureAwait(false);
return; return;
} }
@ -460,28 +460,27 @@ namespace Emby.Server.Implementations.HttpServer
if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
{ {
httpRes.StatusCode = 200; httpRes.StatusCode = 200;
httpRes.AddHeader("Access-Control-Allow-Origin", "*"); httpRes.Headers.Add("Access-Control-Allow-Origin", "*");
httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); httpRes.Headers.Add("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-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
await Write(httpRes, string.Empty).ConfigureAwait(false); await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
return; return;
} }
urlToLog = GetUrlToLog(urlString); 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) || if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
{ {
RedirectToUrl(httpRes, DefaultRedirectPath); httpRes.Redirect(_defaultRedirectPath);
return; return;
} }
if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
{ {
RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); httpRes.Redirect("emby/" + _defaultRedirectPath);
return; return;
} }
@ -494,9 +493,10 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) 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=\"" + "<!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; return;
} }
} }
@ -511,34 +511,35 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) 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=\"" + "<!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; return;
} }
} }
if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
{ {
RedirectToUrl(httpRes, DefaultRedirectPath); httpRes.Redirect(_defaultRedirectPath);
return; return;
} }
if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
{ {
RedirectToUrl(httpRes, "../" + DefaultRedirectPath); httpRes.Redirect("../" + _defaultRedirectPath);
return; return;
} }
if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
{ {
RedirectToUrl(httpRes, DefaultRedirectPath); httpRes.Redirect(_defaultRedirectPath);
return; return;
} }
if (string.IsNullOrEmpty(localPath)) if (string.IsNullOrEmpty(localPath))
{ {
RedirectToUrl(httpRes, "/" + DefaultRedirectPath); httpRes.Redirect("/" + _defaultRedirectPath);
return; return;
} }
@ -546,12 +547,12 @@ namespace Emby.Server.Implementations.HttpServer
{ {
if (localPath.EndsWith("web/dashboard.html", StringComparison.OrdinalIgnoreCase)) 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)) 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.StatusCode = 503;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";
await Write(httpRes, GlobalResponse).ConfigureAwait(false); await httpRes.WriteAsync(GlobalResponse, cancellationToken).ConfigureAwait(false);
return; return;
} }
} }
@ -571,7 +572,7 @@ namespace Emby.Server.Implementations.HttpServer
if (handler != null) if (handler != null)
{ {
await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, cancellationToken).ConfigureAwait(false); await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
@ -598,11 +599,7 @@ namespace Emby.Server.Implementations.HttpServer
var elapsed = stopWatch.Elapsed; var elapsed = stopWatch.Elapsed;
if (elapsed.TotalMilliseconds > 500) if (elapsed.TotalMilliseconds > 500)
{ {
Logger.LogWarning("HTTP Response {StatusCode} to {RemoteIp}. Time (slow): {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);
}
else
{
Logger.LogDebug("HTTP Response {StatusCode} to {RemoteIp}. Time: {Elapsed:g}. {Url}", httpRes.StatusCode, remoteIp, elapsed, urlToLog);
} }
} }
} }
@ -619,18 +616,11 @@ namespace Emby.Server.Implementations.HttpServer
return new ServiceHandler(restPath, contentType); 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; return null;
} }
private static Task Write(IResponse response, string text) private void RedirectToSecureUrl(IHttpRequest httpReq, HttpResponse httpRes, string url)
{
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)
{ {
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
{ {
@ -640,23 +630,11 @@ namespace Emby.Server.Implementations.HttpServer
Scheme = "https" Scheme = "https"
}; };
url = builder.Uri.ToString(); url = builder.Uri.ToString();
RedirectToUrl(httpRes, url);
} }
else
{
RedirectToUrl(httpRes, url);
}
}
public static void RedirectToUrl(IResponse httpRes, string url) httpRes.Redirect(url);
{
httpRes.StatusCode = 302;
httpRes.AddHeader("Location", url);
} }
public ServiceController ServiceController { get; private set; }
/// <summary> /// <summary>
/// Adds the rest handlers. /// Adds the rest handlers.
/// </summary> /// </summary>
@ -672,9 +650,9 @@ namespace Emby.Server.Implementations.HttpServer
var types = services.Select(r => r.GetType()); var types = services.Select(r => r.GetType());
ServiceController.Init(this, types); 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; return "emby/emby/" + path;
} }
private bool _disposed; /// <inheritdoc />
private readonly object _disposeLock = new object(); public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (_disposed) return; if (_disposed) return;
lock (_disposeLock) if (disposing)
{ {
if (_disposed) return; Stop();
_disposed = true;
if (disposing)
{
Stop();
}
} }
_disposed = true;
} }
/// <summary> /// <summary>
@ -803,7 +780,7 @@ namespace Emby.Server.Implementations.HttpServer
return Task.CompletedTask; return Task.CompletedTask;
} }
Logger.LogDebug("Websocket message received: {0}", result.MessageType); _logger.LogDebug("Websocket message received: {0}", result.MessageType);
IEnumerable<Task> GetTasks() IEnumerable<Task> GetTasks()
{ {
@ -815,10 +792,5 @@ namespace Emby.Server.Implementations.HttpServer
return Task.WhenAll(GetTasks()); return Task.WhenAll(GetTasks());
} }
public void Dispose()
{
Dispose(true);
}
} }
} }

View file

@ -2,6 +2,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
@ -9,7 +10,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
public class ResponseFilter 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; private readonly ILogger _logger;
public ResponseFilter(ILogger logger) public ResponseFilter(ILogger logger)
@ -23,12 +24,12 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="req">The req.</param> /// <param name="req">The req.</param>
/// <param name="res">The res.</param> /// <param name="res">The res.</param>
/// <param name="dto">The dto.</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 // 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.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.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
res.AddHeader("Access-Control-Allow-Origin", "*"); res.Headers.Add("Access-Control-Allow-Origin", "*");
if (dto is Exception exception) if (dto is Exception exception)
{ {
@ -39,7 +40,7 @@ namespace Emby.Server.Implementations.HttpServer
var error = exception.Message.Replace(Environment.NewLine, " "); var error = exception.Message.Replace(Environment.NewLine, " ");
error = RemoveControlCharacters(error); 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) if (hasHeaders.Headers.TryGetValue(HeaderNames.ContentLength, out string contentLength)
&& !string.IsNullOrEmpty(contentLength)) && !string.IsNullOrEmpty(contentLength))
{ {
var length = long.Parse(contentLength, UsCulture); var length = long.Parse(contentLength, _usCulture);
if (length > 0) if (length > 0)
{ {
res.OriginalResponse.ContentLength = length; res.ContentLength = length;
res.SendChunked = false;
} }
} }
} }
@ -72,9 +72,12 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public static string RemoveControlCharacters(string inString) 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) foreach (var ch in inString)
{ {
@ -83,6 +86,7 @@ namespace Emby.Server.Implementations.HttpServer
newString.Append(ch); newString.Append(ch);
} }
} }
return newString.ToString(); return newString.ToString();
} }
} }

View file

@ -3,7 +3,6 @@ using System.Linq;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -13,28 +12,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
{ {
public class AuthService : IAuthService public class AuthService : IAuthService
{ {
private readonly IAuthorizationContext _authorizationContext;
private readonly ISessionManager _sessionManager;
private readonly IServerConfigurationManager _config; 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; _config = config;
SessionManager = sessionManager; _sessionManager = sessionManager;
UserManager = userManager; _networkManager = networkManager;
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) public void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues)
{ {
ValidateUser(request, authAttribtues); ValidateUser(request, authAttribtues);
@ -43,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues) private void ValidateUser(IRequest request, IAuthenticationAttributes authAttribtues)
{ {
// This code is executed before the service // This code is executed before the service
var auth = AuthorizationContext.GetAuthorizationInfo(request); var auth = _authorizationContext.GetAuthorizationInfo(request);
if (!IsExemptFromAuthenticationToken(authAttribtues, request)) if (!IsExemptFromAuthenticationToken(authAttribtues, request))
{ {
@ -80,7 +74,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
!string.IsNullOrEmpty(auth.Client) && !string.IsNullOrEmpty(auth.Client) &&
!string.IsNullOrEmpty(auth.Device)) !string.IsNullOrEmpty(auth.Device))
{ {
SessionManager.LogSessionActivity(auth.Client, _sessionManager.LogSessionActivity(auth.Client,
auth.Version, auth.Version,
auth.DeviceId, auth.DeviceId,
auth.Device, 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, IAuthenticationAttributes authAttribtues,
AuthorizationInfo auth) 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.") throw new SecurityException("User account has been disabled.")
{ {
@ -109,11 +105,11 @@ namespace Emby.Server.Implementations.HttpServer.Security
}; };
} }
if (!user.Policy.IsAdministrator && if (!user.Policy.IsAdministrator
!authAttribtues.EscapeParentalControl && && !authAttribtues.EscapeParentalControl
!user.IsParentalScheduleAllowed()) && !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.") 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 (roles.Contains("delete", StringComparer.OrdinalIgnoreCase))
{ {
if (user == null || !user.Policy.EnableContentDeletion) if (user == null || !user.Policy.EnableContentDeletion)
@ -193,6 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
}; };
} }
} }
if (roles.Contains("download", StringComparer.OrdinalIgnoreCase)) if (roles.Contains("download", StringComparer.OrdinalIgnoreCase))
{ {
if (user == null || !user.Policy.EnableContentDownloading) if (user == null || !user.Policy.EnableContentDownloading)

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -555,7 +556,7 @@ namespace Emby.Server.Implementations.IO
throw new ArgumentNullException(nameof(file2)); 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 // Copying over will fail against hidden files
SetHidden(file1, false); SetHidden(file1, false);

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.Images
ImageType imageType, ImageType imageType,
CancellationToken cancellationToken) 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)); Directory.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension));
string outputPath = CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0); 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.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
@ -17,12 +16,10 @@ namespace Emby.Server.Implementations.Library
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private bool _ignoreDotPrefix;
/// <summary> /// <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> /// </summary>
public static readonly string[] IgnoreFolders = private static readonly string[] _ignoreFolders =
{ {
"metadata", "metadata",
"ps3_update", "ps3_update",
@ -43,25 +40,14 @@ namespace Emby.Server.Implementations.Library
"$RECYCLE.BIN", "$RECYCLE.BIN",
"System Volume Information", "System Volume Information",
".grab", ".grab",
// macos
".AppleDouble"
}; };
public CoreResolutionIgnoreRule(ILibraryManager libraryManager) public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
} }
/// <summary> /// <inheritdoc />
/// 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>
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
{ {
// Don't ignore top level folders // Don't ignore top level folders
@ -73,46 +59,17 @@ namespace Emby.Server.Implementations.Library
var filename = fileInfo.Name; var filename = fileInfo.Name;
var path = fileInfo.FullName; var path = fileInfo.FullName;
// Handle mac .DS_Store // Ignore hidden files on UNIX
// https://github.com/MediaBrowser/MediaBrowser/issues/427 if (Environment.OSVersion.Platform != PlatformID.Win32NT
if (_ignoreDotPrefix) && 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) if (fileInfo.IsDirectory)
{ {
// Ignore any folders in our list // Ignore any folders in our list
if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{ {
return true; return true;
} }
@ -120,8 +77,9 @@ namespace Emby.Server.Implementations.Library
if (parent != null) if (parent != null)
{ {
// Ignore trailer folders but allow it at the collection level // Ignore trailer folders but allow it at the collection level
if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)
!(parent is AggregateFolder) && !(parent is UserRootFolder)) && !(parent is AggregateFolder)
&& !(parent is UserRootFolder))
{ {
return true; return true;
} }
@ -142,14 +100,15 @@ namespace Emby.Server.Implementations.Library
if (parent != null) if (parent != null)
{ {
// Don't resolve these into audio files // 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; return true;
} }
} }
// Ignore samples // Ignore samples
Match m = Regex.Match(filename,@"\bsample\b",RegexOptions.IgnoreCase); Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase);
return m.Success; return m.Success;
} }

View file

@ -11,9 +11,9 @@ namespace Emby.Server.Implementations.Library
public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
{ {
private readonly ICryptoProvider _cryptographyProvider; private readonly ICryptoProvider _cryptographyProvider;
public DefaultAuthenticationProvider(ICryptoProvider crypto) public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
{ {
_cryptographyProvider = crypto; _cryptographyProvider = cryptographyProvider;
} }
public string Name => "Default"; public string Name => "Default";
@ -28,17 +28,17 @@ namespace Emby.Server.Implementations.Library
throw new NotImplementedException(); 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) public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{ {
bool success = false; bool success = false;
if (resolvedUser == null) 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 // 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 return Task.FromResult(new ProviderAuthenticationResult
{ {
@ -50,37 +50,24 @@ namespace Emby.Server.Implementations.Library
byte[] passwordbytes = Encoding.UTF8.GetBytes(password); byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
PasswordHash readyHash = new PasswordHash(resolvedUser.Password); PasswordHash readyHash = new PasswordHash(resolvedUser.Password);
byte[] calculatedHash; if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
string calculatedHashString; || _cryptographyProvider.DefaultHashMethod == readyHash.Id)
if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) || _cryptographyProvider.DefaultHashMethod == readyHash.Id)
{ {
if (string.IsNullOrEmpty(readyHash.Salt)) byte[] calculatedHash = _cryptographyProvider.ComputeHash(readyHash.Id, passwordbytes, 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);
}
if (calculatedHashString == readyHash.Hash) if (calculatedHash.SequenceEqual(readyHash.Hash))
{ {
success = true; success = true;
// throw new Exception("Invalid username or password");
} }
} }
else 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) if (!success)
{ {
throw new Exception("Invalid username or password"); throw new AuthenticationException("Invalid username or password");
} }
return Task.FromResult(new ProviderAuthenticationResult return Task.FromResult(new ProviderAuthenticationResult
@ -98,29 +85,22 @@ namespace Emby.Server.Implementations.Library
return; return;
} }
if (!user.Password.Contains("$")) if (user.Password.IndexOf('$') == -1)
{ {
string hash = user.Password; string hash = user.Password;
user.Password = string.Format("$SHA1${0}", hash); 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; string hash = user.EasyPassword;
user.EasyPassword = string.Format("$SHA1${0}", hash); user.EasyPassword = string.Format("$SHA1${0}", hash);
} }
} }
public Task<bool> HasPassword(User user) public bool HasPassword(User user)
{ => !string.IsNullOrEmpty(user.Password);
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 Task ChangePassword(User user, string newPassword) public Task ChangePassword(User user, string newPassword)
{ {
@ -129,30 +109,24 @@ namespace Emby.Server.Implementations.Library
if (string.IsNullOrEmpty(user.Password)) if (string.IsNullOrEmpty(user.Password))
{ {
PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider); PasswordHash newPasswordHash = new PasswordHash(_cryptographyProvider);
newPasswordHash.SaltBytes = _cryptographyProvider.GenerateSalt(); newPasswordHash.Salt = _cryptographyProvider.GenerateSalt();
newPasswordHash.Salt = PasswordHash.ConvertToByteString(newPasswordHash.SaltBytes);
newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod; newPasswordHash.Id = _cryptographyProvider.DefaultHashMethod;
newPasswordHash.Hash = GetHashedStringChangeAuth(newPassword, newPasswordHash); newPasswordHash.Hash = GetHashedChangeAuth(newPassword, newPasswordHash);
user.Password = newPasswordHash.ToString(); user.Password = newPasswordHash.ToString();
return Task.CompletedTask; return Task.CompletedTask;
} }
PasswordHash passwordHash = new PasswordHash(user.Password); 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 = _cryptographyProvider.GenerateSalt();
passwordHash.Salt = PasswordHash.ConvertToByteString(passwordHash.SaltBytes);
passwordHash.Id = _cryptographyProvider.DefaultHashMethod; passwordHash.Id = _cryptographyProvider.DefaultHashMethod;
passwordHash.Hash = GetHashedStringChangeAuth(newPassword, passwordHash); passwordHash.Hash = GetHashedChangeAuth(newPassword, passwordHash);
} }
else if (newPassword != null) else if (newPassword != null)
{ {
passwordHash.Hash = GetHashedString(user, newPassword); passwordHash.Hash = GetHashed(user, newPassword);
}
if (string.IsNullOrWhiteSpace(passwordHash.Hash))
{
throw new ArgumentNullException(nameof(passwordHash.Hash));
} }
user.Password = passwordHash.ToString(); user.Password = passwordHash.ToString();
@ -160,11 +134,6 @@ namespace Emby.Server.Implementations.Library
return Task.CompletedTask; return Task.CompletedTask;
} }
public string GetPasswordHash(User user)
{
return user.Password;
}
public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{ {
ConvertPasswordFormat(user); ConvertPasswordFormat(user);
@ -190,13 +159,13 @@ namespace Emby.Server.Implementations.Library
return string.IsNullOrEmpty(user.EasyPassword) return string.IsNullOrEmpty(user.EasyPassword)
? null ? 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); passwordHash.Hash = Encoding.UTF8.GetBytes(newPassword);
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash)); return _cryptographyProvider.ComputeHash(passwordHash);
} }
/// <summary> /// <summary>
@ -215,10 +184,10 @@ namespace Emby.Server.Implementations.Library
passwordHash = new PasswordHash(user.Password); 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 // 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)); return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash));
} }
else else
@ -227,5 +196,31 @@ namespace Emby.Server.Implementations.Library
return PasswordHash.ConvertToByteString(_cryptographyProvider.ComputeHash(passwordHash.Id, Encoding.UTF8.GetBytes(str))); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
public class DefaultPasswordResetProvider : IPasswordResetProvider public class DefaultPasswordResetProvider : IPasswordResetProvider
{ {
public string Name => "Default Password Reset Provider"; public string Name => "Default Password Reset Provider";
public bool IsEnabled => true; public bool IsEnabled => true;
private readonly string _passwordResetFileBase; private readonly string _passwordResetFileBase;
private readonly string _passwordResetFileBaseDir; private readonly string _passwordResetFileBaseDir;
private readonly string _passwordResetFileBaseName = "passwordreset"; private readonly string _passwordResetFileBaseName = "passwordreset";
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ICryptoProvider _crypto; private readonly ICryptoProvider _crypto;
public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider) public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider)
{ {
_passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath;
_passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName); _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName);
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_userManager = userManager; _userManager = userManager;
_crypto = cryptoProvider; _crypto = cryptoProvider;
} }
public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin) public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{ {
SerializablePasswordReset spr; SerializablePasswordReset spr;
HashSet<string> usersreset = new HashSet<string>(); HashSet<string> usersreset = new HashSet<string>();
foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*")) foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
{ {
using (var str = File.OpenRead(resetfile)) using (var str = File.OpenRead(resetfile))
{ {
spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false); spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
} }
if (spr.ExpirationDate < DateTime.Now) if (spr.ExpirationDate < DateTime.Now)
{ {
File.Delete(resetfile); File.Delete(resetfile);
} }
else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase)) else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase))
{ {
var resetUser = _userManager.GetUserByName(spr.UserName); var resetUser = _userManager.GetUserByName(spr.UserName);
if (resetUser == null) if (resetUser == null)
{ {
throw new Exception($"User with a username of {spr.UserName} not found"); throw new Exception($"User with a username of {spr.UserName} not found");
} }
await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
usersreset.Add(resetUser.Name); usersreset.Add(resetUser.Name);
File.Delete(resetfile); File.Delete(resetfile);
} }
} }
if (usersreset.Count < 1) if (usersreset.Count < 1)
{ {
throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}"); throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
} }
else else
{ {
return new PinRedeemResult return new PinRedeemResult
{ {
Success = true, Success = true,
UsersReset = usersreset.ToArray() UsersReset = usersreset.ToArray()
}; };
} }
} }
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork) public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
{ {
string pin = string.Empty; string pin = string.Empty;
using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create()) using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create())
{ {
byte[] bytes = new byte[4]; byte[] bytes = new byte[4];
cryptoRandom.GetBytes(bytes); cryptoRandom.GetBytes(bytes);
pin = BitConverter.ToString(bytes); pin = BitConverter.ToString(bytes);
} }
DateTime expireTime = DateTime.Now.AddMinutes(30); DateTime expireTime = DateTime.Now.AddMinutes(30);
string filePath = _passwordResetFileBase + user.InternalId + ".json"; string filePath = _passwordResetFileBase + user.InternalId + ".json";
SerializablePasswordReset spr = new SerializablePasswordReset SerializablePasswordReset spr = new SerializablePasswordReset
{ {
ExpirationDate = expireTime, ExpirationDate = expireTime,
Pin = pin, Pin = pin,
PinFile = filePath, PinFile = filePath,
UserName = user.Name UserName = user.Name
}; };
try using (FileStream fileStream = File.OpenWrite(filePath))
{ {
using (FileStream fileStream = File.OpenWrite(filePath)) _jsonSerializer.SerializeToStream(spr, fileStream);
{ await fileStream.FlushAsync().ConfigureAwait(false);
_jsonSerializer.SerializeToStream(spr, fileStream); }
await fileStream.FlushAsync().ConfigureAwait(false);
} return new ForgotPasswordResult
} {
catch (Exception e) Action = ForgotPasswordAction.PinCode,
{ PinExpirationDate = expireTime,
throw new Exception($"Error serializing or writing password reset for {user.Name} to location: {filePath}", e); PinFile = filePath
} };
}
return new ForgotPasswordResult
{ private class SerializablePasswordReset : PasswordPinCreationResult
Action = ForgotPasswordAction.PinCode, {
PinExpirationDate = expireTime, public string Pin { get; set; }
PinFile = filePath
}; public string UserName { get; set; }
} }
}
private class SerializablePasswordReset : PasswordPinCreationResult }
{
public string Pin { get; set; }
public string UserName { get; set; }
}
}
}

View file

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

View file

@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -16,12 +13,12 @@ namespace Emby.Server.Implementations.Library
public Task<ProviderAuthenticationResult> Authenticate(string username, string password) 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) 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) public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
{ {
// Nothing here // Nothing here
} }
public string GetPasswordHash(User user) public string GetPasswordHash(User user)

View file

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

View file

@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
MediaInfo mediaInfo = null; 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)) if (!string.IsNullOrEmpty(cacheKey))
{ {

View file

@ -269,7 +269,7 @@ namespace Emby.Server.Implementations.Library
private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) 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)) if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{ {
@ -626,7 +626,7 @@ namespace Emby.Server.Implementations.Library
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
MediaInfo mediaInfo = null; 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)) if (!string.IsNullOrEmpty(cacheKey))
{ {
@ -854,7 +854,7 @@ namespace Emby.Server.Implementations.Library
var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2); 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 splitIndex = key.IndexOf(LiveStreamIdDelimeter);
var keyId = key.Substring(splitIndex + 1); var keyId = key.Substring(splitIndex + 1);

View file

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

View file

@ -152,7 +152,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetCacheKey(long internalUserId, Guid itemId) 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) public UserItemData GetUserData(User user, BaseItem item)

View file

@ -266,6 +266,7 @@ namespace Emby.Server.Implementations.Library
builder.Append(c); builder.Append(c);
} }
} }
return builder.ToString(); return builder.ToString();
} }
@ -286,17 +287,17 @@ namespace Emby.Server.Implementations.Library
if (user != null) if (user != null)
{ {
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false); var authResult = await AuthenticateLocalUser(username, password, hashedPassword, user, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.Item1; authenticationProvider = authResult.authenticationProvider;
updatedUsername = authResult.Item2; updatedUsername = authResult.username;
success = authResult.Item3; success = authResult.success;
} }
else else
{ {
// user is null // user is null
var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false); var authResult = await AuthenticateLocalUser(username, password, hashedPassword, null, remoteEndPoint).ConfigureAwait(false);
authenticationProvider = authResult.Item1; authenticationProvider = authResult.authenticationProvider;
updatedUsername = authResult.Item2; updatedUsername = authResult.username;
success = authResult.Item3; success = authResult.success;
if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) if (success && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider))
{ {
@ -331,22 +332,25 @@ namespace Emby.Server.Implementations.Library
if (user == null) if (user == null)
{ {
throw new SecurityException("Invalid username or password entered."); throw new AuthenticationException("Invalid username or password entered.");
} }
if (user.Policy.IsDisabled) 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)) if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
{ {
throw new SecurityException("Forbidden."); throw new AuthenticationException("Forbidden.");
} }
if (!user.IsParentalScheduleAllowed()) 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 // Update LastActivityDate and LastLoginDate, then save
@ -357,6 +361,7 @@ namespace Emby.Server.Implementations.Library
user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow; user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
UpdateUser(user); UpdateUser(user);
} }
UpdateInvalidLoginAttemptCount(user, 0); UpdateInvalidLoginAttemptCount(user, 0);
} }
else else
@ -429,7 +434,7 @@ namespace Emby.Server.Implementations.Library
return providers; 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 try
{ {
@ -444,23 +449,23 @@ namespace Emby.Server.Implementations.Library
authenticationResult = await provider.Authenticate(username, password).ConfigureAwait(false); 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); _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username);
username = 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; string updatedUsername = null;
bool success = false; bool success = false;
@ -475,15 +480,15 @@ namespace Emby.Server.Implementations.Library
if (password == null) if (password == null)
{ {
// legacy // 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 else
{ {
foreach (var provider in GetAuthenticationProviders(user)) foreach (var provider in GetAuthenticationProviders(user))
{ {
var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); var providerAuthResult = await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
updatedUsername = providerAuthResult.Item1; updatedUsername = providerAuthResult.username;
success = providerAuthResult.Item2; success = providerAuthResult.success;
if (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) private void UpdateInvalidLoginAttemptCount(User user, int newValue)
@ -593,7 +598,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(user)); 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 hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?

View file

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

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Validators
continue; 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 _libraryManager.DeleteItem(item, new DeleteOptions
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities) 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 _libraryManager.DeleteItem(item, new DeleteOptions
{ {

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -76,7 +77,7 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities) 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 _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; LiveTvProgram programInfo = null;
@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken) 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 // populate info.seriesID
var program = GetProgramInfoFromCache(info.ProgramId); var program = GetProgramInfoFromCache(info.ProgramId);
@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var json = _jsonSerializer.SerializeToString(mediaSource); var json = _jsonSerializer.SerializeToString(mediaSource);
mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json); 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) //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
//{ //{
@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var timer = new TimerInfo var timer = new TimerInfo
{ {
ChannelId = channelId, ChannelId = channelId,
Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N"), Id = (seriesTimer.Id + parent.ExternalId).GetMD5().ToString("N", CultureInfo.InvariantCulture),
StartDate = parent.StartDate, StartDate = parent.StartDate,
EndDate = parent.EndDate.Value, EndDate = parent.EndDate.Value,
ProgramId = parent.ExternalId, ProgramId = parent.ExternalId,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -11,7 +13,6 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -20,7 +21,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 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)) using (var manager = new HdHomerunManager(_socketFactory, Logger))
{ {
// Legacy HdHomeruns are IPv4 only // 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) for (int i = 0; i < model.TunerCount; ++i)
{ {
@ -461,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{ {
id = "native"; 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 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? // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try 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]; var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
var response = await udpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false); 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 // 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) 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 uint? _lockkey = null;
private int _activeTuner = -1; private int _activeTuner = -1;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private IpAddressInfo _remoteIp; private IPAddress _remoteIp;
private ILogger _logger; private ILogger _logger;
private ISocket _currentTcpSocket; 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)) 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"); var lockkeyMsg = CreateGetMessage(tuner, "lockkey");
await socket.SendToAsync(lockkeyMsg, 0, lockkeyMsg.Length, ipEndPoint, cancellationToken); 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); 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; _remoteIp = remoteIp;
@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var lockKeyValue = _lockkey.Value; var lockKeyValue = _lockkey.Value;
var ipEndPoint = new IpEndPointInfo(_remoteIp, HdHomeRunPort); var ipEndPoint = new IPEndPoint(_remoteIp, HdHomeRunPort);
for (int i = 0; i < numTuners; ++i) 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) foreach (Tuple<string, string> command in commandList)
{ {
var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey); 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); var response = await tcpClient.ReceiveAsync(receiveBuffer, 0, receiveBuffer.Length, cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked // parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out string returnVal)) 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); _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); var releaseTarget = CreateSetMessage(_activeTuner, "target", "none", lockKeyValue);
await tcpClient.SendToAsync(releaseTarget, 0, releaseTarget.Length, ipEndPoint, CancellationToken.None).ConfigureAwait(false); 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 int _numTuners;
private readonly INetworkManager _networkManager; 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) : base(mediaSource, tunerHostInfo, fileSystem, logger, appPaths)
{ {
_appHost = appHost; _appHost = appHost;
@ -58,7 +70,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host); Logger.LogInformation("Opening HDHR UDP Live stream from {host}", uri.Host);
var remoteAddress = IPAddress.Parse(uri.Host); var remoteAddress = IPAddress.Parse(uri.Host);
var embyRemoteAddress = _networkManager.ParseIpAddress(uri.Host);
IPAddress localAddress = null; IPAddress localAddress = null;
using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) using (var tcpSocket = CreateSocket(remoteAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{ {
@ -81,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try try
{ {
// send url to start streaming // 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) catch (Exception ex)
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,8 +21,8 @@
"HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Shows Favoritos", "HeaderFavoriteShows": "Séries Favoritas",
"HeaderFavoriteSongs": "Musicas Favoritas", "HeaderFavoriteSongs": "Músicas Favoritas",
"HeaderLiveTV": "TV ao Vivo", "HeaderLiveTV": "TV ao Vivo",
"HeaderNextUp": "Próximos", "HeaderNextUp": "Próximos",
"HeaderRecordingGroups": "Grupos de Gravação", "HeaderRecordingGroups": "Grupos de Gravação",
@ -32,19 +32,19 @@
"ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca",
"LabelIpAddressValue": "Endereço IP: {0}", "LabelIpAddressValue": "Endereço IP: {0}",
"LabelRunningTimeValue": "Tempo de execução: {0}", "LabelRunningTimeValue": "Tempo de execução: {0}",
"Latest": "Recente", "Latest": "Recentes",
"MessageApplicationUpdated": "O servidor Jellyfin foi atualizado", "MessageApplicationUpdated": "Servidor Jellyfin atualizado",
"MessageApplicationUpdatedTo": "O Servidor Jellyfin foi atualizado para {0}", "MessageApplicationUpdatedTo": "Servidor Jellyfin atualizado para {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada", "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada",
"MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada", "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
"MixedContent": "Conteúdo misto", "MixedContent": "Conteúdo misto",
"Movies": "Filmes", "Movies": "Filmes",
"Music": "Música", "Music": "Música",
"MusicVideos": "Vídeos musicais", "MusicVideos": "Clipes",
"NameInstallFailed": "A instalação de {0} falhou", "NameInstallFailed": "A instalação de {0} falhou",
"NameSeasonNumber": "Temporada {0}", "NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconhecida", "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", "NotificationOptionApplicationUpdateAvailable": "Atualização de aplicativo disponível",
"NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada", "NotificationOptionApplicationUpdateInstalled": "Atualização de aplicativo instalada",
"NotificationOptionAudioPlayback": "Reprodução de áudio iniciada", "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 // 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. // 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) if (remotePort < 0)
{ {
throw new ArgumentException("remotePort cannot be less than zero.", nameof(remotePort)); 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.InterNetwork
: AddressFamily.InterNetworkV6; : AddressFamily.InterNetworkV6;
@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Net
try try
{ {
return new UdpSocket(retVal, new IpEndPointInfo(remoteAddress, remotePort)); return new UdpSocket(retVal, new IPEndPoint(remoteAddress, remotePort));
} }
catch 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. /// 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> /// </summary>
/// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns> /// <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) if (localPort < 0)
{ {
@ -115,10 +115,8 @@ namespace Emby.Server.Implementations.Net
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); 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"), localIpAddress));
return new UdpSocket(retVal, localPort, localIpAddress);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp));
return new UdpSocket(retVal, localPort, localIp);
} }
catch catch
{ {

View file

@ -3,7 +3,6 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.Networking;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Net namespace Emby.Server.Implementations.Net
@ -19,7 +18,7 @@ namespace Emby.Server.Implementations.Net
public Socket Socket => _socket; public Socket Socket => _socket;
public IpAddressInfo LocalIPAddress { get; } public IPAddress LocalIPAddress { get; }
private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
{ {
@ -40,7 +39,7 @@ namespace Emby.Server.Implementations.Net
_socket = socket; _socket = socket;
_localPort = localPort; _localPort = localPort;
LocalIPAddress = NetworkManager.ToIpAddressInfo(ip); LocalIPAddress = ip;
_socket.Bind(new IPEndPoint(ip, _localPort)); _socket.Bind(new IPEndPoint(ip, _localPort));
@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Net
{ {
Buffer = e.Buffer, Buffer = e.Buffer,
ReceivedBytes = e.BytesTransferred, ReceivedBytes = e.BytesTransferred,
RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), RemoteEndPoint = e.RemoteEndPoint as IPEndPoint,
LocalIPAddress = LocalIPAddress 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)); if (socket == null) throw new ArgumentNullException(nameof(socket));
_socket = socket; _socket = socket;
_socket.Connect(NetworkManager.ToIPEndPoint(endPoint)); _socket.Connect(endPoint);
InitReceiveSocketAsyncEventArgs(); InitReceiveSocketAsyncEventArgs();
} }
@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.Net
return new SocketReceiveResult return new SocketReceiveResult
{ {
ReceivedBytes = receivedBytes, ReceivedBytes = receivedBytes,
RemoteEndPoint = ToIpEndPointInfo((IPEndPoint)remoteEndPoint), RemoteEndPoint = (IPEndPoint)remoteEndPoint,
Buffer = buffer, Buffer = buffer,
LocalIPAddress = LocalIPAddress LocalIPAddress = LocalIPAddress
}; };
@ -191,7 +190,7 @@ namespace Emby.Server.Implementations.Net
return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken); 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(); ThrowIfDisposed();
@ -227,13 +226,11 @@ namespace Emby.Server.Implementations.Net
return taskCompletion.Task; 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(); ThrowIfDisposed();
var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, endPoint, callback, state);
return _socket.BeginSendTo(buffer, offset, size, SocketFlags.None, ipEndPoint, callback, state);
} }
public int EndSendTo(IAsyncResult result) public int EndSendTo(IAsyncResult result)
@ -268,15 +265,5 @@ namespace Emby.Server.Implementations.Net
_disposed = true; _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.Common.Net;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations.Networking namespace Emby.Server.Implementations.Networking
{ {
public class NetworkManager : INetworkManager public class NetworkManager : INetworkManager
{ {
protected ILogger Logger { get; private set; } private readonly ILogger _logger;
public event EventHandler NetworkChanged; private IPAddress[] _localIpAddresses;
public Func<string[]> LocalSubnetsFn { get; set; } 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 NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;
if (OperatingSystem.Id != OperatingSystemId.BSD) NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
{
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");
}
}
} }
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(); OnNetworkChanged();
} }
private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) private void OnNetworkAddressChanged(object sender, EventArgs e)
{ {
Logger.LogDebug("NetworkAddressChanged"); _logger.LogDebug("NetworkAddressChanged");
OnNetworkChanged(); OnNetworkChanged();
} }
@ -68,39 +51,35 @@ namespace Emby.Server.Implementations.Networking
_localIpAddresses = null; _localIpAddresses = null;
_macAddresses = null; _macAddresses = null;
} }
if (NetworkChanged != null) if (NetworkChanged != null)
{ {
NetworkChanged(this, EventArgs.Empty); NetworkChanged(this, EventArgs.Empty);
} }
} }
private IpAddressInfo[] _localIpAddresses; public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
private readonly object _localIpAddressSyncLock = new object();
public IpAddressInfo[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
{ {
lock (_localIpAddressSyncLock) lock (_localIpAddressSyncLock)
{ {
if (_localIpAddresses == null) if (_localIpAddresses == null)
{ {
var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).Result.Select(ToIpAddressInfo).ToArray(); var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray();
_localIpAddresses = addresses; _localIpAddresses = addresses;
return addresses;
} }
return _localIpAddresses; return _localIpAddresses;
} }
} }
private async Task<List<IPAddress>> GetLocalIpAddressesInternal(bool ignoreVirtualInterface) private List<IPAddress> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
{ {
var list = GetIPsDefault(ignoreVirtualInterface) var list = GetIPsDefault(ignoreVirtualInterface).ToList();
.ToList();
if (list.Count == 0) if (list.Count == 0)
{ {
list.AddRange(await GetLocalIpAddressesFallback().ConfigureAwait(false)); list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
} }
var listClone = list.ToList(); var listClone = list.ToList();
@ -116,9 +95,8 @@ namespace Emby.Server.Implementations.Networking
private static bool FilterIpAddress(IPAddress address) private static bool FilterIpAddress(IPAddress address)
{ {
var addressString = address.ToString(); if (address.IsIPv6LinkLocal
|| address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase))
if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{ {
return false; return false;
} }
@ -279,7 +257,7 @@ namespace Emby.Server.Implementations.Networking
if (normalizedSubnet.IndexOf('/') != -1) if (normalizedSubnet.IndexOf('/') != -1)
{ {
var ipnetwork = IPNetwork.IPNetwork.Parse(normalizedSubnet); var ipnetwork = IPNetwork.Parse(normalizedSubnet);
if (ipnetwork.Contains(address)) if (ipnetwork.Contains(address))
{ {
return true; return true;
@ -351,13 +329,13 @@ namespace Emby.Server.Implementations.Networking
try try
{ {
var host = uri.DnsSafeHost; var host = uri.DnsSafeHost;
Logger.LogDebug("Resolving host {0}", host); _logger.LogDebug("Resolving host {0}", host);
address = GetIpAddresses(host).Result.FirstOrDefault(); address = GetIpAddresses(host).Result.FirstOrDefault();
if (address != null) if (address != null)
{ {
Logger.LogDebug("{0} resolved to {1}", host, address); _logger.LogDebug("{0} resolved to {1}", host, address);
return IsInLocalNetworkInternal(address.ToString(), false); return IsInLocalNetworkInternal(address.ToString(), false);
} }
@ -368,7 +346,7 @@ namespace Emby.Server.Implementations.Networking
} }
catch (Exception ex) 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); return Dns.GetHostAddressesAsync(hostName);
} }
private List<IPAddress> GetIPsDefault(bool ignoreVirtualInterface) private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
{ {
NetworkInterface[] interfaces; IEnumerable<NetworkInterface> interfaces;
try try
{ {
var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
interfaces = NetworkInterface.GetAllNetworkInterfaces() interfaces = NetworkInterface.GetAllNetworkInterfaces()
.Where(i => validStatuses.Contains(i.OperationalStatus)) .Where(x => x.OperationalStatus == OperationalStatus.Up
.ToArray(); || x.OperationalStatus == OperationalStatus.Unknown);
} }
catch (Exception ex) catch (NetworkInformationException ex)
{ {
Logger.LogError(ex, "Error in GetAllNetworkInterfaces"); _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
return new List<IPAddress>(); return Enumerable.Empty<IPAddress>();
} }
return interfaces.SelectMany(network => 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 return Enumerable.Empty<IPAddress>();
//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 ipProperties.UnicastAddresses
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork || i.AddressFamily == AddressFamily.InterNetworkV6);
}).GroupBy(i => i.ToString()) }).GroupBy(i => i.ToString())
.Select(x => x.First()) .Select(x => x.First());
.ToList();
} }
private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback() private static async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
@ -462,47 +425,27 @@ namespace Emby.Server.Implementations.Networking
var localEndPoint = new IPEndPoint(IPAddress.Any, 0); var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
using (var udpClient = new UdpClient(localEndPoint)) using (var udpClient = new UdpClient(localEndPoint))
{ {
var port = ((IPEndPoint)(udpClient.Client.LocalEndPoint)).Port; var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
return port; return port;
} }
} }
private List<string> _macAddresses; private List<PhysicalAddress> _macAddresses;
public List<string> GetMacAddresses() public List<PhysicalAddress> GetMacAddresses()
{ {
if (_macAddresses == null) if (_macAddresses == null)
{ {
_macAddresses = GetMacAddressesInternal(); _macAddresses = GetMacAddressesInternal().ToList();
} }
return _macAddresses; return _macAddresses;
} }
private static List<string> GetMacAddressesInternal() private static IEnumerable<PhysicalAddress> GetMacAddressesInternal()
{ => NetworkInterface.GetAllNetworkInterfaces()
return NetworkInterface.GetAllNetworkInterfaces()
.Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback) .Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(i => .Select(x => x.GetPhysicalAddress())
{ .Where(x => x != null && x != PhysicalAddress.None);
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();
}
/// <summary> /// <summary>
/// Parses the specified endpointstring. /// Parses the specified endpointstring.
@ -612,32 +555,10 @@ namespace Emby.Server.Implementations.Networking
return hosts[0]; return hosts[0];
} }
public IpAddressInfo ParseIpAddress(string ipAddress) public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
{ {
if (TryParseIpAddress(ipAddress, out var info)) IPAddress network1 = GetNetworkAddress(address1, subnetMask);
{ IPAddress network2 = GetNetworkAddress(address2, subnetMask);
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));
return network1.Equals(network2); return network1.Equals(network2);
} }
@ -656,13 +577,13 @@ namespace Emby.Server.Implementations.Networking
{ {
broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i])); broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
} }
return new IPAddress(broadcastAddress); return new IPAddress(broadcastAddress);
} }
public IpAddressInfo GetLocalIpSubnetMask(IpAddressInfo address) public IPAddress GetLocalIpSubnetMask(IPAddress address)
{ {
NetworkInterface[] interfaces; NetworkInterface[] interfaces;
IPAddress ipaddress = ToIPAddress(address);
try try
{ {
@ -674,7 +595,7 @@ namespace Emby.Server.Implementations.Networking
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error in GetAllNetworkInterfaces"); _logger.LogError(ex, "Error in GetAllNetworkInterfaces");
return null; return null;
} }
@ -684,85 +605,17 @@ namespace Emby.Server.Implementations.Networking
{ {
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses) 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; 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> /// <summary>
/// Gets the network shares. /// Gets the network shares.
/// </summary> /// </summary>

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -129,7 +130,7 @@ namespace Emby.Server.Implementations.Playlists
{ {
new Share 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 CanEdit = true
} }
} }
@ -144,7 +145,7 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0) 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 EnableImages = true
}); });
@ -152,7 +153,7 @@ namespace Emby.Server.Implementations.Playlists
return new PlaylistCreationResult return new PlaylistCreationResult
{ {
Id = playlist.Id.ToString("N") Id = playlist.Id.ToString("N", CultureInfo.InvariantCulture)
}; };
} }
finally finally

View file

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

View file

@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName); statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion); statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName); 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("@UserName", info.UserName);
statement.TryBind("@IsActive", true); statement.TryBind("@IsActive", true);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName); statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion); statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName); 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("@UserName", info.UserName);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue()); statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue()); statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Security
if (!query.UserId.Equals(Guid.Empty)) 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)) if (!string.IsNullOrEmpty(query.DeviceId))

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
@ -7,13 +6,14 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer;
using Microsoft.AspNetCore.Http;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.Services namespace Emby.Server.Implementations.Services
{ {
public static class ResponseHelper 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) if (result == null)
{ {
@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Services
response.StatusCode = (int)HttpStatusCode.NoContent; response.StatusCode = (int)HttpStatusCode.NoContent;
} }
response.OriginalResponse.ContentLength = 0; response.ContentLength = 0;
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -41,7 +41,6 @@ namespace Emby.Server.Implementations.Services
httpResult.RequestContext = request; httpResult.RequestContext = request;
response.StatusCode = httpResult.Status; response.StatusCode = httpResult.Status;
response.StatusDescription = httpResult.StatusCode.ToString();
} }
var responseOptions = result as IHasHeaders; var responseOptions = result as IHasHeaders;
@ -51,11 +50,11 @@ namespace Emby.Server.Implementations.Services
{ {
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase)) 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; 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) switch (result)
{ {
case IAsyncStreamWriter asyncStreamWriter: case IAsyncStreamWriter asyncStreamWriter:
return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken); return asyncStreamWriter.WriteToAsync(response.Body, cancellationToken);
case IStreamWriter streamWriter: case IStreamWriter streamWriter:
streamWriter.WriteTo(response.OutputStream); streamWriter.WriteTo(response.Body);
return Task.CompletedTask; return Task.CompletedTask;
case FileWriter fileWriter: case FileWriter fileWriter:
return fileWriter.WriteToAsync(response, cancellationToken); return fileWriter.WriteToAsync(response, cancellationToken);
case Stream stream: case Stream stream:
return CopyStream(stream, response.OutputStream); return CopyStream(stream, response.Body);
case byte[] bytes: case byte[] bytes:
response.ContentType = "application/octet-stream"; response.ContentType = "application/octet-stream";
response.OriginalResponse.ContentLength = bytes.Length; response.ContentLength = bytes.Length;
if (bytes.Length > 0) 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; return Task.CompletedTask;
case string responseText: case string responseText:
var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText); var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
response.OriginalResponse.ContentLength = responseTextAsBytes.Length; response.ContentLength = responseTextAsBytes.Length;
if (responseTextAsBytes.Length > 0) 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; 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 contentType = request.ResponseContentType;
var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
@ -127,11 +126,11 @@ namespace Emby.Server.Implementations.Services
ms.Position = 0; ms.Position = 0;
var contentLength = ms.Length; var contentLength = ms.Length;
response.OriginalResponse.ContentLength = contentLength; response.ContentLength = contentLength;
if (contentLength > 0) 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) public Task<object> Execute(HttpListenerHost httpHost, object requestDto, IRequest req)
{ {
req.Dto = requestDto;
var requestType = requestDto.GetType(); var requestType = requestDto.GetType();
req.OperationName = requestType.Name; req.OperationName = requestType.Name;
@ -161,9 +160,6 @@ namespace Emby.Server.Implementations.Services
serviceRequiresContext.Request = req; serviceRequiresContext.Request = req;
} }
if (req.Dto == null) // Don't override existing batched DTO[]
req.Dto = requestDto;
//Executes the service and returns the result //Executes the service and returns the result
return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()); 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) foreach (var requestFilter in actionContext.RequestFilters)
{ {
requestFilter.RequestFilter(request, request.Response, requestDto); requestFilter.RequestFilter(request, request.Response, requestDto);
if (request.Response.OriginalResponse.HasStarted) if (request.Response.HasStarted)
{ {
Task.FromResult<object>(null); Task.FromResult<object>(null);
} }

View file

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

View file

@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Session
{ {
var dict = new Dictionary<string, string>(); 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) if (command.StartPositionTicks.HasValue)
{ {

View file

@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Session
{ {
if (string.IsNullOrEmpty(info.MediaSourceId)) 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) if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Session
Client = appName, Client = appName,
DeviceId = deviceId, DeviceId = deviceId,
ApplicationVersion = appVersion, ApplicationVersion = appVersion,
Id = key.GetMD5().ToString("N"), Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
ServerId = _appHost.SystemId ServerId = _appHost.SystemId
}; };
@ -845,7 +845,7 @@ namespace Emby.Server.Implementations.Session
// Normalize // Normalize
if (string.IsNullOrEmpty(info.MediaSourceId)) 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) 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) private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
{ {
var controllers = session.SessionControllers.ToArray(); var controllers = session.SessionControllers.ToArray();
var messageId = Guid.NewGuid().ToString("N"); var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
foreach (var controller in controllers) foreach (var controller in controllers)
{ {
@ -1041,7 +1041,7 @@ namespace Emby.Server.Implementations.Session
{ {
IEnumerable<Task> GetTasks() IEnumerable<Task> GetTasks()
{ {
var messageId = Guid.NewGuid().ToString("N"); var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
foreach (var session in sessions) foreach (var session in sessions)
{ {
var controllers = session.SessionControllers; var controllers = session.SessionControllers;
@ -1234,7 +1234,7 @@ namespace Emby.Server.Implementations.Session
AssertCanControl(session, controllingSession); AssertCanControl(session, controllingSession);
if (!controllingSession.UserId.Equals(Guid.Empty)) 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, DeviceId = deviceId,
DeviceName = deviceName, DeviceName = deviceName,
UserId = user.Id, UserId = user.Id,
AccessToken = Guid.NewGuid().ToString("N"), AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
UserName = user.Name UserName = user.Name
}; };
@ -1822,6 +1822,7 @@ namespace Emby.Server.Implementations.Session
CheckDisposed(); CheckDisposed();
var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
return SendMessageToSessions(sessions, name, data, cancellationToken); return SendMessageToSessions(sessions, name, data, cancellationToken);
} }
@ -1831,6 +1832,7 @@ namespace Emby.Server.Implementations.Session
var sessions = Sessions var sessions = Sessions
.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
return SendMessageToSessions(sessions, name, data, cancellationToken); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Linq; using System.Linq;
using System.Text;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
using IResponse = MediaBrowser.Model.Services.IResponse;
namespace Emby.Server.Implementations.SocketSharp namespace Emby.Server.Implementations.SocketSharp
{ {
public partial class WebSocketSharpRequest : IHttpRequest 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.OperationName = operationName;
this.request = httpContext; this.Request = httpRequest;
this.Response = new WebSocketSharpResponse(logger, response); 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 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 public string RemoteIp
{ {
get get
{ {
if (remoteIp != null) if (_remoteIp != null)
{ {
return remoteIp; return _remoteIp;
} }
IPAddress ip; IPAddress ip;
@ -62,14 +61,51 @@ namespace Emby.Server.Implementations.SocketSharp
{ {
if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip)) 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) private static IPAddress NormalizeIp(IPAddress ip)
{ {
if (ip.IsIPv4MappedToIPv6) if (ip.IsIPv4MappedToIPv6)
@ -80,22 +116,6 @@ namespace Emby.Server.Implementations.SocketSharp
return ip; 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) public static string GetResponseContentType(HttpRequest httpReq)
{ {
var specifiedContentType = GetQueryStringContentType(httpReq); var specifiedContentType = GetQueryStringContentType(httpReq);
@ -152,8 +172,6 @@ namespace Emby.Server.Implementations.SocketSharp
return serverDefaultContentType; return serverDefaultContentType;
} }
public const string Soap11 = "text/xml; charset=utf-8";
public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes) public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes)
{ {
if (contentTypes == null || request.ContentType == null) if (contentTypes == null || request.ContentType == null)
@ -224,105 +242,5 @@ namespace Emby.Server.Implementations.SocketSharp
var pos = strVal.IndexOf(needle); var pos = strVal.IndexOf(needle);
return pos == -1 ? strVal : strVal.Slice(0, pos); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.TV
{ {
parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder) .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(); .ToArray();
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Udp
private bool _isDisposed; 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 IServerApplicationHost _appHost;
private readonly IJsonSerializer _json; private readonly IJsonSerializer _json;
@ -43,9 +44,9 @@ namespace Emby.Server.Implementations.Udp
AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message); 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> /// <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 text = encoding.GetString(buffer, 0, bytesReceived);
var responder = _responders.FirstOrDefault(i => var responder = _responders.FirstOrDefault(i =>
@ -99,10 +100,10 @@ namespace Emby.Server.Implementations.Udp
{ {
return null; 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('|'); 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) if (_isDisposed)
{ {

View file

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

View file

@ -133,8 +133,21 @@ namespace MediaBrowser.Api.Devices
var album = Request.QueryString["Album"]; var album = Request.QueryString["Album"];
var id = Request.QueryString["Id"]; var id = Request.QueryString["Id"];
var name = Request.QueryString["Name"]; 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 return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
{ {
@ -144,18 +157,6 @@ namespace MediaBrowser.Api.Devices
Id = id 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -537,7 +538,7 @@ namespace MediaBrowser.Api.Images
if (item == null) 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