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(CancellationToken cancellationToken = default) { if (IsLoggedIn) { _logger.LogWarning("The client is already logged in!"); return; } var userName = _config.UserName; if (string.IsNullOrWhiteSpace(userName)) { throw new PiwigoException("username cannot be null or whitespace."); } var password = _config.Password; if (string.IsNullOrWhiteSpace(password)) { throw new PiwigoException("password cannot be null or whitespace."); } _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), cancellationToken); 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(CancellationToken cancellationToken = default) { 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", cancellationToken); IsLoggedIn = false; _cookies.Clear(); } public Task PostAsync(ILogger logger, string method, CancellationToken cancellationToken = default) where T : PiwigoResponse { return PostInternalAsync(logger, method, null, cancellationToken); } public Task PostAsync(ILogger logger, string method, IDictionary formParams, CancellationToken cancellationToken = default) where T : PiwigoResponse { return PostInternalAsync(logger, method, formParams, cancellationToken); } 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, CancellationToken cancellationToken) where T : PiwigoResponse { await EnsureLoggedInAsync(cancellationToken); logger.LogInformation("executing {Method} using post", method); var response = await ConfigureRequest(logger).PostMultipartAsync(c => { c.AddMethod(method); if (formParams != null) { AddFormParams(c, formParams); } }, cancellationToken); 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(CancellationToken cancellationToken) { if (IsLoggedIn) { return; } await LoginAsync(cancellationToken); } 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); } }