/*
This file is part of Anastasis
Copyright (C) 2020, 2021, 2022 Anastasis SARL
Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
Anastasis; see the file COPYING.GPL. If not, see
*/
/**
* @file reducer/anastasis_api_providers.c
* @brief anastasis provider synchronization logic
* @author Christian Grothoff
*/
#include
#include
#include "anastasis_redux.h"
#include "anastasis_error_codes.h"
#include "anastasis_api_redux.h"
/**
* Main data structure for sync_providers().
*/
struct MasterSync;
/**
* Data structure for one provider we are syncing /config with.
*/
struct SyncEntry
{
/**
* Kept in a DLL.
*/
struct SyncEntry *next;
/**
* Kept in a DLL.
*/
struct SyncEntry *prev;
/**
* Sync operation we are part of.
*/
struct MasterSync *ms;
/**
* Redux action for this provider.
*/
struct ANASTASIS_ReduxAction *ra;
};
/**
* Main data structure for sync_providers().
*/
struct MasterSync
{
/**
* Our own sync action we expose externally.
*/
struct ANASTASIS_ReduxAction ra;
/**
* Head of DLL with entries per provider.
*/
struct SyncEntry *se_head;
/**
* Tail of DLL with entries per provider.
*/
struct SyncEntry *se_tail;
/**
* Function to call with the result.
*/
ANASTASIS_ActionCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
};
/**
* Free @a cls data structure.
*
* @param[in] cls data structure to free, must be a `struct MasterSync *`
*/
static void
clean_sync (void *cls)
{
struct MasterSync *ms = cls;
struct SyncEntry *se;
while (NULL != (se = ms->se_head))
{
GNUNET_CONTAINER_DLL_remove (ms->se_head,
ms->se_tail,
se);
se->ra->cleanup (se->ra->cleanup_cls);
GNUNET_free (se);
}
GNUNET_free (ms);
}
/**
* Function called when we have made progress on any of the
* providers we are trying to sync with.
*
* @param cls closure
* @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state
* @param new_state the new state of the operation (client should json_incref() to keep an alias)
*/
static void
sync_progress (void *cls,
enum TALER_ErrorCode error,
json_t *new_state)
{
struct SyncEntry *se = cls;
struct MasterSync *ms = se->ms;
GNUNET_CONTAINER_DLL_remove (ms->se_head,
ms->se_tail,
se);
GNUNET_free (se);
ms->cb (ms->cb_cls,
error,
new_state);
clean_sync (ms);
}
struct ANASTASIS_ReduxAction *
ANASTASIS_REDUX_sync_providers_ (json_t *state,
const json_t *arguments,
ANASTASIS_ActionCallback cb,
void *cb_cls)
{
json_t *rd;
json_t *cs_arr;
struct MasterSync *ms;
rd = json_object_get (state,
"recovery_document");
if (NULL == rd)
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'recovery_document' missing");
return NULL;
}
cs_arr = json_object_get (rd,
"challenges");
if (! json_is_array (cs_arr))
{
GNUNET_break_op (0);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'recovery_document' must be an array");
return NULL;
}
ms = GNUNET_new (struct MasterSync);
ms->cb = cb;
ms->cb_cls = cb_cls;
{
json_t *cs;
unsigned int n_index;
json_array_foreach (cs_arr, n_index, cs)
{
const char *provider_url;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("url",
&provider_url),
GNUNET_JSON_spec_end ()
};
struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
struct SyncEntry *se;
if (GNUNET_OK !=
GNUNET_JSON_parse (cs,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'recovery_document' missing");
clean_sync (ms);
return NULL;
}
if (GNUNET_OK ==
ANASTASIS_reducer_lookup_salt (state,
provider_url,
&provider_salt))
continue; /* provider already ready */
se = GNUNET_new (struct SyncEntry);
se->ms = ms;
GNUNET_CONTAINER_DLL_insert (ms->se_head,
ms->se_tail,
se);
se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url,
state,
&sync_progress,
se);
}
}
if (NULL == ms->se_head)
{
/* everything already synced */
clean_sync (ms);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED,
"already in sync");
return NULL;
}
ms->ra.cleanup = &clean_sync;
ms->ra.cleanup_cls = ms;
return &ms->ra;
}
struct ANASTASIS_ReduxAction *
ANASTASIS_REDUX_poll_providers_ (json_t *state,
const json_t *arguments,
ANASTASIS_ActionCallback cb,
void *cb_cls)
{
json_t *ap;
const char *url;
json_t *obj;
struct MasterSync *ms;
ap = json_object_get (state,
"authentication_providers");
if (NULL == ap)
{
GNUNET_break (0);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_STATE_INVALID,
"'authentication_providers' missing");
return NULL;
}
ms = GNUNET_new (struct MasterSync);
ms->cb = cb;
ms->cb_cls = cb_cls;
json_object_foreach (ap, url, obj)
{
struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt;
struct SyncEntry *se;
struct ANASTASIS_ReduxAction *ra;
if (GNUNET_OK ==
ANASTASIS_reducer_lookup_salt (state,
url,
&provider_salt))
continue;
se = GNUNET_new (struct SyncEntry);
se->ms = ms;
GNUNET_CONTAINER_DLL_insert (ms->se_head,
ms->se_tail,
se);
ra = ANASTASIS_REDUX_add_provider_to_state_ (url,
state,
&sync_progress,
se);
if (NULL == ra)
return NULL; /* sync_progress already called! */
se->ra = ra;
}
if (NULL == ms->se_head)
{
/* everything already synced */
clean_sync (ms);
ANASTASIS_redux_fail_ (cb,
cb_cls,
TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID,
"already in sync");
return NULL;
}
ms->ra.cleanup = &clean_sync;
ms->ra.cleanup_cls = ms;
return &ms->ra;
}