diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-10-13 16:14:53 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-10-13 16:14:53 -0400 |
| commit | ab3c26c5647a56e95d0f5229f07b1a1b45812ff8 (patch) | |
| tree | bbcaf5db256364f19cdb4f7c18eb82171dc4183b /MediaBrowser.Server.Implementations/Connect/ConnectManager.cs | |
| parent | 8a15ee6ab4149cf59f8ff2724e3e415d34557da1 (diff) | |
update connect
Diffstat (limited to 'MediaBrowser.Server.Implementations/Connect/ConnectManager.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/Connect/ConnectManager.cs | 359 |
1 files changed, 300 insertions, 59 deletions
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index a4535a57c..4e3bbbcb2 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -5,8 +5,10 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Connect; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; @@ -24,7 +26,7 @@ namespace MediaBrowser.Server.Implementations.Connect { public class ConnectManager : IConnectManager { - private SemaphoreSlim _operationLock = new SemaphoreSlim(1,1); + private readonly SemaphoreSlim _operationLock = new SemaphoreSlim(1, 1); private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -34,6 +36,7 @@ namespace MediaBrowser.Server.Implementations.Connect private readonly IServerApplicationHost _appHost; private readonly IServerConfigurationManager _config; private readonly IUserManager _userManager; + private readonly IProviderManager _providerManager; private ConnectData _data = new ConnectData(); @@ -90,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.Connect IEncryptionManager encryption, IHttpClient httpClient, IServerApplicationHost appHost, - IServerConfigurationManager config, IUserManager userManager) + IServerConfigurationManager config, IUserManager userManager, IProviderManager providerManager) { _logger = logger; _appPaths = appPaths; @@ -100,6 +103,7 @@ namespace MediaBrowser.Server.Implementations.Connect _appHost = appHost; _config = config; _userManager = userManager; + _providerManager = providerManager; LoadCachedData(); } @@ -165,13 +169,15 @@ namespace MediaBrowser.Server.Implementations.Connect } await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false); + + await RefreshUserInfosInternal(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error registering with Connect", ex); } } - + private async Task CreateServerRegistration(string wanApiAddress) { var url = "Servers"; @@ -181,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.Connect { {"name", _appHost.FriendlyName}, {"url", wanApiAddress}, - {"systemid", _appHost.SystemId} + {"systemId", _appHost.SystemId} }; using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false)) @@ -211,7 +217,7 @@ namespace MediaBrowser.Server.Implementations.Connect { {"name", _appHost.FriendlyName}, {"url", wanApiAddress}, - {"systemid", _appHost.SystemId} + {"systemId", _appHost.SystemId} }); SetServerAccessToken(options); @@ -222,6 +228,7 @@ namespace MediaBrowser.Server.Implementations.Connect } } + private readonly object _dataFileLock = new object(); private string CacheFilePath { get { return Path.Combine(_appPaths.DataPath, "connect.txt"); } @@ -239,7 +246,10 @@ namespace MediaBrowser.Server.Implementations.Connect var encrypted = _encryption.EncryptString(json); - File.WriteAllText(path, encrypted, Encoding.UTF8); + lock (_dataFileLock) + { + File.WriteAllText(path, encrypted, Encoding.UTF8); + } } catch (Exception ex) { @@ -253,11 +263,14 @@ namespace MediaBrowser.Server.Implementations.Connect try { - var encrypted = File.ReadAllText(path, Encoding.UTF8); + lock (_dataFileLock) + { + var encrypted = File.ReadAllText(path, Encoding.UTF8); - var json = _encryption.DecryptString(encrypted); + var json = _encryption.DecryptString(encrypted); - _data = _json.DeserializeFromString<ConnectData>(json); + _data = _json.DeserializeFromString<ConnectData>(json); + } } catch (IOException) { @@ -288,6 +301,20 @@ namespace MediaBrowser.Server.Implementations.Connect public async Task<UserLinkResult> LinkUser(string userId, string connectUsername) { + await _operationLock.WaitAsync().ConfigureAwait(false); + + try + { + return await LinkUserInternal(userId, connectUsername).ConfigureAwait(false); + } + finally + { + _operationLock.Release(); + } + } + + private async Task<UserLinkResult> LinkUserInternal(string userId, string connectUsername) + { if (string.IsNullOrWhiteSpace(connectUsername)) { throw new ArgumentNullException("connectUsername"); @@ -350,56 +377,98 @@ namespace MediaBrowser.Server.Implementations.Connect await user.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + user.Configuration.SyncConnectImage = user.ConnectLinkType == UserLinkType.Guest; + user.Configuration.SyncConnectName = user.ConnectLinkType == UserLinkType.Guest; + _userManager.UpdateConfiguration(user, user.Configuration); + + await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false); + return result; } - - public Task RemoveLink(string userId) + + public async Task<UserLinkResult> InviteUser(string sendingUserId, string connectUsername) { - var user = GetUser(userId); + await _operationLock.WaitAsync().ConfigureAwait(false); - return RemoveLink(user, user.ConnectUserId); + try + { + return await InviteUserInternal(sendingUserId, connectUsername).ConfigureAwait(false); + } + finally + { + _operationLock.Release(); + } } - private async Task RemoveLink(User user, string connectUserId) + private async Task<UserLinkResult> InviteUserInternal(string sendingUserId, string connectUsername) { - if (!string.IsNullOrWhiteSpace(connectUserId)) + if (string.IsNullOrWhiteSpace(connectUsername)) { - var url = GetConnectUrl("ServerAuthorizations"); + throw new ArgumentNullException("connectUsername"); + } - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None - }; + var connectUser = await GetConnectUser(new ConnectUserQuery + { + Name = connectUsername - var postData = new Dictionary<string, string> - { - {"serverId", ConnectServerId}, - {"userId", connectUserId} - }; + }, CancellationToken.None).ConfigureAwait(false); + + if (!connectUser.IsActive) + { + throw new ArgumentException("The Media Browser account has been disabled."); + } - options.SetPostData(postData); + var url = GetConnectUrl("ServerAuthorizations"); - SetServerAccessToken(options); + var options = new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None + }; - try - { - // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content) - { - } - } - catch (HttpException ex) - { - // If connect says the auth doesn't exist, we can handle that gracefully since this is a remove operation + var accessToken = Guid.NewGuid().ToString("N"); + var sendingUser = GetUser(sendingUserId); - if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound) - { - throw; - } + var postData = new Dictionary<string, string> + { + {"serverId", ConnectServerId}, + {"userId", connectUser.Id}, + {"userType", "Guest"}, + {"accessToken", accessToken}, + {"requesterUserName", sendingUser.ConnectUserName} + }; - _logger.Debug("Connect returned a 404 when removing a user auth link. Handling it."); - } + options.SetPostData(postData); + + SetServerAccessToken(options); + + var result = new UserLinkResult(); + + // No need to examine the response + using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content) + { + var response = _json.DeserializeFromStream<ServerUserAuthorizationResponse>(stream); + + result.IsPending = string.Equals(response.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase); + } + + await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false); + + return result; + } + + public Task RemoveLink(string userId) + { + var user = GetUser(userId); + + return RemoveLink(user, user.ConnectUserId); + } + + private async Task RemoveLink(User user, string connectUserId) + { + if (!string.IsNullOrWhiteSpace(connectUserId)) + { + await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false); } user.ConnectAccessKey = null; @@ -472,24 +541,19 @@ namespace MediaBrowser.Server.Implementations.Connect { var url = GetConnectUrl("ServerAuthorizations"); + url += "?serverId=" + ConnectServerId; + var options = new HttpRequestOptions { Url = url, CancellationToken = cancellationToken }; - var postData = new Dictionary<string, string> - { - {"serverId", ConnectServerId} - }; - - options.SetPostData(postData); - SetServerAccessToken(options); try { - using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) + using (var stream = (await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)).Content) { var list = _json.DeserializeFromStream<List<ServerUserAuthorizationResponse>>(stream); @@ -521,10 +585,11 @@ namespace MediaBrowser.Server.Implementations.Connect user.ConnectUserName = null; await _userManager.UpdateUser(user).ConfigureAwait(false); - + if (user.ConnectLinkType == UserLinkType.Guest) { - await _userManager.DeleteUser(user).ConfigureAwait(false); + _logger.Debug("Deleting guest user {0}", user.Name); + //await _userManager.DeleteUser(user).ConfigureAwait(false); } } else @@ -544,19 +609,195 @@ namespace MediaBrowser.Server.Implementations.Connect users = _userManager.Users.ToList(); + var pending = new List<ConnectAuthorization>(); + // TODO: Handle newly added guests that we don't know about foreach (var connectEntry in list) { - if (string.Equals(connectEntry.UserType, "guest", StringComparison.OrdinalIgnoreCase) && - string.Equals(connectEntry.AcceptStatus, "accepted", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(connectEntry.UserType, "guest", StringComparison.OrdinalIgnoreCase)) + { + if (string.Equals(connectEntry.AcceptStatus, "accepted", StringComparison.OrdinalIgnoreCase)) + { + var user = users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectEntry.UserId, StringComparison.OrdinalIgnoreCase)); + + if (user == null) + { + // Add user + } + } + else if (string.Equals(connectEntry.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase)) + { + pending.Add(new ConnectAuthorization + { + ConnectUserId = connectEntry.UserId, + ImageUrl = connectEntry.ImageUrl, + UserName = connectEntry.UserName, + Id = connectEntry.Id + }); + } + } + } + + _data.PendingAuthorizations = pending; + CacheData(); + } + + public async Task RefreshUserInfos(CancellationToken cancellationToken) + { + await _operationLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + await RefreshUserInfosInternal(cancellationToken).ConfigureAwait(false); + } + finally + { + _operationLock.Release(); + } + } + + private readonly SemaphoreSlim _connectImageSemaphore = new SemaphoreSlim(5, 5); + + private async Task RefreshUserInfosInternal(CancellationToken cancellationToken) + { + var users = _userManager.Users + .Where(i => !string.IsNullOrEmpty(i.ConnectUserId) && + (i.Configuration.SyncConnectImage || i.Configuration.SyncConnectName)) + .ToList(); + + foreach (var user in users) + { + cancellationToken.ThrowIfCancellationRequested(); + + var connectUser = await GetConnectUser(new ConnectUserQuery { - var user = users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectEntry.UserId, StringComparison.OrdinalIgnoreCase)); + Id = user.ConnectUserId - if (user == null) + }, cancellationToken).ConfigureAwait(false); + + if (user.Configuration.SyncConnectName) + { + var changed = !string.Equals(connectUser.Name, user.Name, StringComparison.OrdinalIgnoreCase); + + if (changed) { - // Add user + await user.Rename(connectUser.Name).ConfigureAwait(false); } } + + if (user.Configuration.SyncConnectImage) + { + var imageUrl = connectUser.ImageUrl; + + if (!string.IsNullOrWhiteSpace(imageUrl)) + { + var changed = false; + + if (!user.HasImage(ImageType.Primary)) + { + changed = true; + } + else + { + using (var response = await _httpClient.SendAsync(new HttpRequestOptions + { + Url = imageUrl, + CancellationToken = cancellationToken, + BufferContent = false + + }, "HEAD").ConfigureAwait(false)) + { + var length = response.ContentLength; + + if (length != new FileInfo(user.GetImageInfo(ImageType.Primary, 0).Path).Length) + { + changed = true; + } + } + } + + if (changed) + { + await _providerManager.SaveImage(user, imageUrl, _connectImageSemaphore, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + + await user.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = true, + + }, cancellationToken).ConfigureAwait(false); + } + } + } + } + } + + public async Task<List<ConnectAuthorization>> GetPendingGuests() + { + return _data.PendingAuthorizations.ToList(); + } + + public async Task CancelAuthorization(string id) + { + await _operationLock.WaitAsync().ConfigureAwait(false); + + try + { + await CancelAuthorizationInternal(id).ConfigureAwait(false); + } + finally + { + _operationLock.Release(); + } + } + + private async Task CancelAuthorizationInternal(string id) + { + var connectUserId = _data.PendingAuthorizations + .First(i => string.Equals(i.Id, id, StringComparison.Ordinal)) + .ConnectUserId; + + await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false); + + await RefreshAuthorizationsInternal(CancellationToken.None).ConfigureAwait(false); + } + + private async Task CancelAuthorizationByConnectUserId(string connectUserId) + { + var url = GetConnectUrl("ServerAuthorizations"); + + var options = new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None + }; + + var postData = new Dictionary<string, string> + { + {"serverId", ConnectServerId}, + {"userId", connectUserId} + }; + + options.SetPostData(postData); + + SetServerAccessToken(options); + + try + { + // No need to examine the response + using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content) + { + } + } + catch (HttpException ex) + { + // If connect says the auth doesn't exist, we can handle that gracefully since this is a remove operation + + if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound) + { + throw; + } + + _logger.Debug("Connect returned a 404 when removing a user auth link. Handling it."); } } } |
