summaryrefslogtreecommitdiff
path: root/src/reducer/anastasis_api_providers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reducer/anastasis_api_providers.c')
-rw-r--r--src/reducer/anastasis_api_providers.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/reducer/anastasis_api_providers.c b/src/reducer/anastasis_api_providers.c
new file mode 100644
index 0000000..82243f5
--- /dev/null
+++ b/src/reducer/anastasis_api_providers.c
@@ -0,0 +1,300 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file reducer/anastasis_api_providers.c
+ * @brief anastasis provider synchronization logic
+ * @author Christian Grothoff
+ */
+
+#include <platform.h>
+#include <jansson.h>
+#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;
+}