using System.Net; using Flurl.Http; using Flurl.Http.Content; using Microsoft.Extensions.Logging; using Piwigo.Client.Contract; namespace Piwigo.Client; public class PiwigoContext : IPiwigoContext { private readonly IPiwigoConfiguration _config; private readonly CookieJar _cookies = new(); private readonly ILogger _logger; public PiwigoContext(IPiwigoConfiguration configuration, ILogger logger) { _config = configuration ?? throw new ArgumentNullException(nameof(configuration)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public bool IsLoggedIn { get; private set; } public async Task LoginAsync() { var userName = _config.UserName; if (string.IsNullOrWhiteSpace(userName)) { throw new ArgumentException("Value cannot be null or whitespace.", nameof(userName)); } var password = _config.Password; if (string.IsNullOrWhiteSpace(password)) { throw new ArgumentException("Value cannot be null or whitespace.", nameof(password)); } if (IsLoggedIn) { throw new PiwigoException("The client is already logged in. Create a new instance or log out first!"); } _logger.LogInformation("Logging into {PiwigoBaseUri} using username {Username}", _config.BaseUri, userName); var response = await ConfigureRequest(_logger).PostMultipartAsync(c => c.AddMethod("pwg.session.login").AddString("username", userName).AddString("password", password)); if (response.StatusCode != (int)HttpStatusCode.OK) { throw new PiwigoException($"Could not log in to {_config.BaseUri} using username {userName}"); } _logger.LogInformation("Logging in succeeded"); IsLoggedIn = true; } public async Task LogoutAsync() { if (!IsLoggedIn) { _logger.LogWarning("Tried to log out from {Uri} but was not logged in!", _config.BaseUri); return; } _logger.LogInformation("Logging out from {Uri}", _config.BaseUri); await PostAsync(_logger, "pwg.session.logout"); IsLoggedIn = false; _cookies.Clear(); } public Task PostAsync(ILogger logger, string method) where T : PiwigoResponse { return PostInternalAsync(logger, method, null); } public Task PostAsync(ILogger logger, string method, IDictionary formParams) where T : PiwigoResponse { return PostInternalAsync(logger, method, formParams); } private static void AddFormParams(CapturedMultipartContent c, IDictionary formParams) { foreach (var formParam in formParams) { c.AddString(formParam.Key, formParam.Value); } } private async Task PostInternalAsync(ILogger logger, string method, IDictionary? formParams) where T : PiwigoResponse { await EnsureLoggedInAsync(); logger.LogInformation("executing {Method} using post", method); var response = await ConfigureRequest(logger).PostMultipartAsync(c => { c.AddMethod(method); if (formParams != null) { AddFormParams(c, formParams); } }); if (response.StatusCode != (int)HttpStatusCode.OK) { throw new PiwigoException($"failed to call {method} on {_config.BaseUri}: {response.StatusCode}"); } var typedResponse = await response.GetJsonAsync(); if (!typedResponse.IsOk) { throw new PiwigoException($"failed to call {method} on {_config.BaseUri}: {typedResponse.Status} - Error: {typedResponse.Error} - Message:{typedResponse.Message}"); } return typedResponse; } private async ValueTask EnsureLoggedInAsync() { if (IsLoggedIn) { return; } await LoginAsync(); } private IFlurlRequest ConfigureRequest(ILogger logger) { if (logger == null) { throw new ArgumentNullException(nameof(logger)); } return _config.BaseUri.WithCookies(_cookies).ConfigureRequest(r => r.AfterCallAsync = call => LogResponse(call, logger)); } private static async Task LogResponse(FlurlCall call, ILogger logger) { var responseString = await call.Response.GetStringAsync(); logger.LogDebug("Response: {Response}", responseString); } }