PageRenderTime 49ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NzbDrone.Core/Download/Clients/DownloadStation/Proxies/DiskStationProxyBase.cs

https://github.com/NzbDrone/NzbDrone
C# | 265 lines | 224 code | 41 blank | 0 comment | 26 complexity | 2699f794176b15a095802aa1db5b8ea7 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using NLog;
  6. using NzbDrone.Common.Cache;
  7. using NzbDrone.Common.Http;
  8. using NzbDrone.Common.Serializer;
  9. using NzbDrone.Core.Download.Clients.DownloadStation.Responses;
  10. namespace NzbDrone.Core.Download.Clients.DownloadStation.Proxies
  11. {
  12. public interface IDiskStationProxy
  13. {
  14. DiskStationApiInfo GetApiInfo(DownloadStationSettings settings);
  15. }
  16. public abstract class DiskStationProxyBase : IDiskStationProxy
  17. {
  18. protected readonly Logger _logger;
  19. private readonly IHttpClient _httpClient;
  20. private readonly ICached<DiskStationApiInfo> _infoCache;
  21. private readonly ICached<string> _sessionCache;
  22. private readonly DiskStationApi _apiType;
  23. private readonly string _apiName;
  24. private static readonly DiskStationApiInfo _apiInfo;
  25. static DiskStationProxyBase()
  26. {
  27. _apiInfo = new DiskStationApiInfo()
  28. {
  29. Type = DiskStationApi.Info,
  30. Name = "SYNO.API.Info",
  31. Path = "query.cgi",
  32. MaxVersion = 1,
  33. MinVersion = 1,
  34. NeedsAuthentication = false
  35. };
  36. }
  37. public DiskStationProxyBase(DiskStationApi apiType,
  38. string apiName,
  39. IHttpClient httpClient,
  40. ICacheManager cacheManager,
  41. Logger logger)
  42. {
  43. _httpClient = httpClient;
  44. _logger = logger;
  45. _infoCache = cacheManager.GetCache<DiskStationApiInfo>(typeof(DiskStationProxyBase), "apiInfo");
  46. _sessionCache = cacheManager.GetCache<string>(typeof(DiskStationProxyBase), "sessions");
  47. _apiType = apiType;
  48. _apiName = apiName;
  49. }
  50. private string GenerateSessionCacheKey(DownloadStationSettings settings)
  51. {
  52. return $"{settings.Username}@{settings.Host}:{settings.Port}";
  53. }
  54. protected DiskStationResponse<T> ProcessRequest<T>(HttpRequestBuilder requestBuilder,
  55. string operation,
  56. DownloadStationSettings settings) where T : new()
  57. {
  58. return ProcessRequest<T>(requestBuilder, operation, _apiType, settings);
  59. }
  60. private DiskStationResponse<T> ProcessRequest<T>(HttpRequestBuilder requestBuilder,
  61. string operation,
  62. DiskStationApi api,
  63. DownloadStationSettings settings) where T : new()
  64. {
  65. var request = requestBuilder.Build();
  66. HttpResponse response;
  67. try
  68. {
  69. response = _httpClient.Execute(request);
  70. }
  71. catch (HttpException ex)
  72. {
  73. throw new DownloadClientException("Unable to connect to Diskstation, please check your settings", ex);
  74. }
  75. catch (WebException ex)
  76. {
  77. if (ex.Status == WebExceptionStatus.TrustFailure)
  78. {
  79. throw new DownloadClientUnavailableException("Unable to connect to Diskstation, certificate validation failed.", ex);
  80. }
  81. throw new DownloadClientUnavailableException("Unable to connect to Diskstation, please check your settings", ex);
  82. }
  83. _logger.Debug("Trying to {0}", operation);
  84. if (response.StatusCode == HttpStatusCode.OK)
  85. {
  86. var responseContent = Json.Deserialize<DiskStationResponse<T>>(response.Content);
  87. if (responseContent.Success)
  88. {
  89. return responseContent;
  90. }
  91. else
  92. {
  93. var msg = $"Failed to {operation}. Reason: {responseContent.Error.GetMessage(api)}";
  94. _logger.Error(msg);
  95. if (responseContent.Error.SessionError)
  96. {
  97. _sessionCache.Remove(GenerateSessionCacheKey(settings));
  98. if (responseContent.Error.Code == 105)
  99. {
  100. throw new DownloadClientAuthenticationException(msg);
  101. }
  102. }
  103. throw new DownloadClientException(msg);
  104. }
  105. }
  106. else
  107. {
  108. throw new HttpException(request, response);
  109. }
  110. }
  111. private string AuthenticateClient(DownloadStationSettings settings)
  112. {
  113. var authInfo = GetApiInfo(DiskStationApi.Auth, settings);
  114. var requestBuilder = BuildRequest(settings, authInfo, "login", authInfo.MaxVersion >= 7 ? 6 : 2);
  115. requestBuilder.AddQueryParam("account", settings.Username);
  116. requestBuilder.AddQueryParam("passwd", settings.Password);
  117. requestBuilder.AddQueryParam("format", "sid");
  118. requestBuilder.AddQueryParam("session", "DownloadStation");
  119. var authResponse = ProcessRequest<DiskStationAuthResponse>(requestBuilder, "login", DiskStationApi.Auth, settings);
  120. return authResponse.Data.SId;
  121. }
  122. protected HttpRequestBuilder BuildRequest(DownloadStationSettings settings, string methodName, int apiVersion, HttpMethod httpVerb = HttpMethod.GET)
  123. {
  124. var info = GetApiInfo(_apiType, settings);
  125. return BuildRequest(settings, info, methodName, apiVersion, httpVerb);
  126. }
  127. private HttpRequestBuilder BuildRequest(DownloadStationSettings settings, DiskStationApiInfo apiInfo, string methodName, int apiVersion, HttpMethod httpVerb = HttpMethod.GET)
  128. {
  129. var requestBuilder = new HttpRequestBuilder(settings.UseSsl, settings.Host, settings.Port).Resource($"webapi/{apiInfo.Path}");
  130. requestBuilder.Method = httpVerb;
  131. requestBuilder.LogResponseContent = true;
  132. requestBuilder.SuppressHttpError = true;
  133. requestBuilder.AllowAutoRedirect = false;
  134. requestBuilder.Headers.ContentType = "application/json";
  135. if (apiVersion < apiInfo.MinVersion || apiVersion > apiInfo.MaxVersion)
  136. {
  137. throw new ArgumentOutOfRangeException(nameof(apiVersion));
  138. }
  139. if (httpVerb == HttpMethod.POST)
  140. {
  141. if (apiInfo.NeedsAuthentication)
  142. {
  143. if (_apiType == DiskStationApi.DownloadStation2Task)
  144. {
  145. requestBuilder.AddQueryParam("_sid", _sessionCache.Get(GenerateSessionCacheKey(settings), () => AuthenticateClient(settings), TimeSpan.FromHours(6)));
  146. }
  147. else
  148. {
  149. requestBuilder.AddFormParameter("_sid", _sessionCache.Get(GenerateSessionCacheKey(settings), () => AuthenticateClient(settings), TimeSpan.FromHours(6)));
  150. }
  151. }
  152. requestBuilder.AddFormParameter("api", apiInfo.Name);
  153. requestBuilder.AddFormParameter("version", apiVersion);
  154. requestBuilder.AddFormParameter("method", methodName);
  155. }
  156. else
  157. {
  158. if (apiInfo.NeedsAuthentication)
  159. {
  160. requestBuilder.AddQueryParam("_sid", _sessionCache.Get(GenerateSessionCacheKey(settings), () => AuthenticateClient(settings), TimeSpan.FromHours(6)));
  161. }
  162. requestBuilder.AddQueryParam("api", apiInfo.Name);
  163. requestBuilder.AddQueryParam("version", apiVersion);
  164. requestBuilder.AddQueryParam("method", methodName);
  165. }
  166. return requestBuilder;
  167. }
  168. private string GenerateInfoCacheKey(DownloadStationSettings settings, DiskStationApi api)
  169. {
  170. return $"{settings.Host}:{settings.Port}->{api}";
  171. }
  172. private void UpdateApiInfo(DownloadStationSettings settings)
  173. {
  174. var apis = new Dictionary<string, DiskStationApi>()
  175. {
  176. { "SYNO.API.Auth", DiskStationApi.Auth },
  177. { _apiName, _apiType }
  178. };
  179. var requestBuilder = BuildRequest(settings, _apiInfo, "query", _apiInfo.MinVersion);
  180. requestBuilder.AddQueryParam("query", string.Join(",", apis.Keys));
  181. var infoResponse = ProcessRequest<DiskStationApiInfoResponse>(requestBuilder, "get api info", _apiInfo.Type, settings);
  182. foreach (var data in infoResponse.Data)
  183. {
  184. if (apis.ContainsKey(data.Key))
  185. {
  186. data.Value.Name = data.Key;
  187. data.Value.Type = apis[data.Key];
  188. data.Value.NeedsAuthentication = apis[data.Key] != DiskStationApi.Auth;
  189. _infoCache.Set(GenerateInfoCacheKey(settings, apis[data.Key]), data.Value, TimeSpan.FromHours(1));
  190. }
  191. }
  192. }
  193. private DiskStationApiInfo GetApiInfo(DiskStationApi api, DownloadStationSettings settings)
  194. {
  195. if (api == DiskStationApi.Info)
  196. {
  197. return _apiInfo;
  198. }
  199. var key = GenerateInfoCacheKey(settings, api);
  200. var info = _infoCache.Find(key);
  201. if (info == null)
  202. {
  203. UpdateApiInfo(settings);
  204. info = _infoCache.Find(key);
  205. if (info == null)
  206. {
  207. if (api == DiskStationApi.DownloadStation2Task)
  208. {
  209. _logger.Warn("Info of {0} not found on {1}:{2}", api, settings.Host, settings.Port);
  210. }
  211. else
  212. {
  213. throw new DownloadClientException("Info of {0} not found on {1}:{2}", api, settings.Host, settings.Port);
  214. }
  215. }
  216. }
  217. return info;
  218. }
  219. public DiskStationApiInfo GetApiInfo(DownloadStationSettings settings)
  220. {
  221. return GetApiInfo(_apiType, settings);
  222. }
  223. }
  224. }