aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/EntryPoints
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/EntryPoints')
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs336
-rw-r--r--Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs11
-rw-r--r--Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/SystemEvents.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs3
13 files changed, 360 insertions, 9 deletions
diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
index 561f5ee12..c2cee00c8 100644
--- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
@@ -112,6 +112,7 @@ namespace Emby.Server.Implementations.EntryPoints
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
DisposeTimer();
+ GC.SuppressFinalize(this);
}
private void DisposeTimer()
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
new file mode 100644
index 000000000..9b434d606
--- /dev/null
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -0,0 +1,336 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Threading;
+using Mono.Nat;
+using MediaBrowser.Model.Extensions;
+
+namespace Emby.Server.Implementations.EntryPoints
+{
+ public class ExternalPortForwarding : IServerEntryPoint
+ {
+ private readonly IServerApplicationHost _appHost;
+ private readonly ILogger _logger;
+ private readonly IHttpClient _httpClient;
+ private readonly IServerConfigurationManager _config;
+ private readonly IDeviceDiscovery _deviceDiscovery;
+
+ private ITimer _timer;
+ private bool _isStarted;
+ private readonly ITimerFactory _timerFactory;
+
+ public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory)
+ {
+ _logger = logmanager.GetLogger("PortMapper");
+ _appHost = appHost;
+ _config = config;
+ _deviceDiscovery = deviceDiscovery;
+ _httpClient = httpClient;
+ _timerFactory = timerFactory;
+ }
+
+ private string _lastConfigIdentifier;
+ private string GetConfigIdentifier()
+ {
+ var values = new List<string>();
+ var config = _config.Configuration;
+
+ values.Add(config.EnableUPnP.ToString());
+ values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
+ values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
+ values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
+ values.Add(config.EnableHttps.ToString());
+ values.Add(_appHost.EnableHttps.ToString());
+
+ return string.Join("|", values.ToArray(values.Count));
+ }
+
+ void _config_ConfigurationUpdated(object sender, EventArgs e)
+ {
+ if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
+ {
+ if (_isStarted)
+ {
+ DisposeNat();
+ }
+
+ Run();
+ }
+ }
+
+ public void Run()
+ {
+ NatUtility.Logger = _logger;
+ NatUtility.HttpClient = _httpClient;
+
+ if (_config.Configuration.EnableUPnP)
+ {
+ Start();
+ }
+
+ _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
+ _config.ConfigurationUpdated += _config_ConfigurationUpdated;
+ }
+
+ private void Start()
+ {
+ _logger.Debug("Starting NAT discovery");
+ NatUtility.EnabledProtocols = new List<NatProtocol>
+ {
+ NatProtocol.Pmp
+ };
+ NatUtility.DeviceFound += NatUtility_DeviceFound;
+
+ // Mono.Nat does never rise this event. The event is there however it is useless.
+ // You could remove it with no risk.
+ NatUtility.DeviceLost += NatUtility_DeviceLost;
+
+
+ NatUtility.StartDiscovery();
+
+ _timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
+
+ _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
+
+ _lastConfigIdentifier = GetConfigIdentifier();
+
+ _isStarted = true;
+ }
+
+ private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ var info = e.Argument;
+
+ string usn;
+ if (!info.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
+
+ string nt;
+ if (!info.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
+
+ // Filter device type
+ if (usn.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
+ nt.IndexOf("WANIPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
+ usn.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1 &&
+ nt.IndexOf("WANPPPConnection:", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ return;
+ }
+
+ var identifier = string.IsNullOrWhiteSpace(usn) ? nt : usn;
+
+ if (info.Location == null)
+ {
+ return;
+ }
+
+ lock (_usnsHandled)
+ {
+ if (_usnsHandled.Contains(identifier))
+ {
+ return;
+ }
+ _usnsHandled.Add(identifier);
+ }
+
+ _logger.Debug("Found NAT device: " + identifier);
+
+ IPAddress address;
+ if (IPAddress.TryParse(info.Location.Host, out address))
+ {
+ // The Handle method doesn't need the port
+ var endpoint = new IPEndPoint(address, info.Location.Port);
+
+ IPAddress localAddress = null;
+
+ try
+ {
+ var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
+
+ Uri uri;
+ if (Uri.TryCreate(localAddressString, UriKind.Absolute, out uri))
+ {
+ localAddressString = uri.Host;
+
+ if (!IPAddress.TryParse(localAddressString, out localAddress))
+ {
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ return;
+ }
+
+ if (_disposed)
+ {
+ return;
+ }
+
+ _logger.Debug("Calling Nat.Handle on " + identifier);
+ NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp);
+ }
+ }
+
+ private void ClearCreatedRules(object state)
+ {
+ lock (_createdRules)
+ {
+ _createdRules.Clear();
+ }
+ lock (_usnsHandled)
+ {
+ _usnsHandled.Clear();
+ }
+ }
+
+ void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ try
+ {
+ var device = e.Device;
+ _logger.Debug("NAT device found: {0}", device.LocalAddress.ToString());
+
+ CreateRules(device);
+ }
+ catch
+ {
+ // I think it could be a good idea to log the exception because
+ // you are using permanent portmapping here (never expire) and that means that next time
+ // CreatePortMap is invoked it can fails with a 718-ConflictInMappingEntry or not. That depends
+ // on the router's upnp implementation (specs says it should fail however some routers don't do it)
+ // It also can fail with others like 727-ExternalPortOnlySupportsWildcard, 728-NoPortMapsAvailable
+ // and those errors (upnp errors) could be useful for diagnosting.
+
+ // Commenting out because users are reporting problems out of our control
+ //_logger.ErrorException("Error creating port forwarding rules", ex);
+ }
+ }
+
+ private List<string> _createdRules = new List<string>();
+ private List<string> _usnsHandled = new List<string>();
+ private async void CreateRules(INatDevice device)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException("PortMapper");
+ }
+
+ // On some systems the device discovered event seems to fire repeatedly
+ // This check will help ensure we're not trying to port map the same device over and over
+
+ var address = device.LocalAddress.ToString();
+
+ lock (_createdRules)
+ {
+ if (!_createdRules.Contains(address))
+ {
+ _createdRules.Add(address);
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
+
+ if (success)
+ {
+ await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
+ }
+ }
+
+ private async Task<bool> CreatePortMap(INatDevice device, int privatePort, int publicPort)
+ {
+ _logger.Debug("Creating port map on port {0}", privatePort);
+
+ try
+ {
+ await device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
+ {
+ Description = _appHost.Name
+
+ }).ConfigureAwait(false);
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Error creating port map: " + ex.Message);
+
+ return false;
+ }
+ }
+
+ // As I said before, this method will be never invoked. You can remove it.
+ void NatUtility_DeviceLost(object sender, DeviceEventArgs e)
+ {
+ var device = e.Device;
+ _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
+ }
+
+ private bool _disposed = false;
+ public void Dispose()
+ {
+ _disposed = true;
+ DisposeNat();
+ GC.SuppressFinalize(this);
+ }
+
+ private void DisposeNat()
+ {
+ _logger.Debug("Stopping NAT discovery");
+
+ if (_timer != null)
+ {
+ _timer.Dispose();
+ _timer = null;
+ }
+
+ _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
+
+ try
+ {
+ // This is not a significant improvement
+ NatUtility.StopDiscovery();
+ NatUtility.DeviceFound -= NatUtility_DeviceFound;
+ NatUtility.DeviceLost -= NatUtility_DeviceLost;
+ }
+ // Statements in try-block will no fail because StopDiscovery is a one-line
+ // method that was no chances to fail.
+ // public static void StopDiscovery ()
+ // {
+ // searching.Reset();
+ // }
+ // IMO you could remove the catch-block
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error stopping NAT Discovery", ex);
+ }
+ finally
+ {
+ _isStarted = false;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
index 8ae85e390..221580681 100644
--- a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
+++ b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
@@ -60,6 +60,7 @@ namespace Emby.Server.Implementations.EntryPoints
_timer.Dispose();
_timer = null;
}
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 69a205dda..796d8cf48 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -367,15 +367,15 @@ namespace Emby.Server.Implementations.EntryPoints
return new LibraryUpdateInfo
{
- ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+ ItemsAdded = itemsAdded.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")).Distinct().ToList(),
+ ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
- ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+ ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
- FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+ FoldersAddedTo = foldersAddedTo.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")).Distinct().ToList()
+ FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray()
};
}
@@ -426,6 +426,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs b/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
index 0203b5192..21e075cf5 100644
--- a/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
+++ b/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
@@ -68,6 +68,7 @@ namespace Emby.Server.Implementations.EntryPoints
_timer.Dispose();
_timer = null;
}
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index b674fc39b..f73b40b46 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -72,6 +72,7 @@ namespace Emby.Server.Implementations.EntryPoints
_liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled;
_liveTvManager.TimerCreated -= _liveTvManager_TimerCreated;
_liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated;
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
index 77de849a1..13e14be36 100644
--- a/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using System;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using System.Threading;
@@ -36,6 +37,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public void Dispose()
{
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index 4d640bc95..514321e20 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -174,6 +174,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 424153f22..614c04fd2 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -1,4 +1,5 @@
-using Emby.Server.Implementations.Browser;
+using System;
+using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
@@ -54,6 +55,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public void Dispose()
{
+ GC.SuppressFinalize(this);
}
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
index 4ab6d32f3..aa63dae27 100644
--- a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
+++ b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
@@ -43,6 +43,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
_systemEvents.SystemShutdown -= _systemEvents_SystemShutdown;
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index df5a7c985..d04df0d2b 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -65,6 +65,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
index 9fbe06673..fb9402986 100644
--- a/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Extensions;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -58,7 +59,7 @@ namespace Emby.Server.Implementations.EntryPoints
session.ApplicationVersion
};
- var key = string.Join("_", keys.ToArray()).GetMD5();
+ var key = string.Join("_", keys.ToArray(keys.Count)).GetMD5();
_apps.GetOrAdd(key, guid => GetNewClientInfo(session));
}
@@ -129,6 +130,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 71e31d4d4..4a7182a43 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.EntryPoints
dto.ItemId = i.Id.ToString("N");
return dto;
})
- .ToList();
+ .ToArray();
var info = new UserDataChangeInfo
{
@@ -160,6 +160,7 @@ namespace Emby.Server.Implementations.EntryPoints
}
_userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
+ GC.SuppressFinalize(this);
}
}
}