anastasis_api_providers.c (7855B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021, 2022 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file reducer/anastasis_api_providers.c 18 * @brief anastasis provider synchronization logic 19 * @author Christian Grothoff 20 */ 21 22 #include <platform.h> 23 #include <jansson.h> 24 #include "anastasis_redux.h" 25 #include "anastasis_error_codes.h" 26 #include "anastasis_api_redux.h" 27 #include "anastasis_api_redux_state.h" 28 29 30 /** 31 * Main data structure for sync_providers(). 32 */ 33 struct MasterSync; 34 35 36 /** 37 * Data structure for one provider we are syncing /config with. 38 */ 39 struct SyncEntry 40 { 41 /** 42 * Kept in a DLL. 43 */ 44 struct SyncEntry *next; 45 46 /** 47 * Kept in a DLL. 48 */ 49 struct SyncEntry *prev; 50 51 /** 52 * Sync operation we are part of. 53 */ 54 struct MasterSync *ms; 55 56 /** 57 * Redux action for this provider. 58 */ 59 struct ANASTASIS_ReduxAction *ra; 60 }; 61 62 63 /** 64 * Main data structure for sync_providers(). 65 */ 66 struct MasterSync 67 { 68 /** 69 * Our own sync action we expose externally. 70 */ 71 struct ANASTASIS_ReduxAction ra; 72 /** 73 * Head of DLL with entries per provider. 74 */ 75 struct SyncEntry *se_head; 76 /** 77 * Tail of DLL with entries per provider. 78 */ 79 struct SyncEntry *se_tail; 80 81 /** 82 * Function to call with the result. 83 */ 84 ANASTASIS_ActionCallback cb; 85 86 /** 87 * Closure for @e cb. 88 */ 89 void *cb_cls; 90 91 /** 92 * State all our entries share. We own it until the first entry 93 * reports back, at which point it is handed to @e cb. 94 */ 95 struct ANASTASIS_ReduxState *rs; 96 97 }; 98 99 100 /** 101 * Free @a cls data structure. 102 * 103 * @param[in] cls data structure to free, must be a `struct MasterSync *` 104 */ 105 static void 106 clean_sync (void *cls) 107 { 108 struct MasterSync *ms = cls; 109 struct SyncEntry *se; 110 111 while (NULL != (se = ms->se_head)) 112 { 113 GNUNET_CONTAINER_DLL_remove (ms->se_head, 114 ms->se_tail, 115 se); 116 se->ra->cleanup (se->ra->cleanup_cls); 117 GNUNET_free (se); 118 } 119 ANASTASIS_REDUX_state_free_ (ms->rs); 120 GNUNET_free (ms); 121 } 122 123 124 /** 125 * Function called when we have made progress on any of the 126 * providers we are trying to sync with. The first one to report wins: 127 * we return its result and drop the rest. 128 * 129 * @param cls closure 130 * @param error error code, #TALER_EC_NONE if the state is the new successful state 131 * @param[in] rs the state, updated for the provider that reported 132 */ 133 static void 134 sync_progress (void *cls, 135 enum TALER_ErrorCode error, 136 struct ANASTASIS_ReduxState *rs) 137 { 138 struct SyncEntry *se = cls; 139 struct MasterSync *ms = se->ms; 140 ANASTASIS_ActionCallback cb = ms->cb; 141 void *cb_cls = ms->cb_cls; 142 143 GNUNET_assert (rs == ms->rs); 144 GNUNET_CONTAINER_DLL_remove (ms->se_head, 145 ms->se_tail, 146 se); 147 GNUNET_free (se); 148 /* The remaining entries are cancelled below; none of them touches the 149 state once it has been unlinked, so handing it over here is safe. */ 150 ms->rs = NULL; 151 clean_sync (ms); 152 ANASTASIS_REDUX_return_ (rs, 153 cb, 154 cb_cls, 155 error); 156 } 157 158 159 struct ANASTASIS_ReduxAction * 160 ANASTASIS_REDUX_sync_providers_ (struct ANASTASIS_ReduxState *rs, 161 const json_t *arguments, 162 ANASTASIS_ActionCallback cb, 163 void *cb_cls) 164 { 165 const struct ANASTASIS_RecoveryInformation *ri; 166 struct MasterSync *ms; 167 168 if (NULL == rs->details.recovery.r) 169 { 170 GNUNET_break (0); 171 ANASTASIS_REDUX_fail_ (rs, 172 cb, 173 cb_cls, 174 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 175 "'recovery_document' missing"); 176 return NULL; 177 } 178 ri = ANASTASIS_recovery_get_info (rs->details.recovery.r); 179 ms = GNUNET_new (struct MasterSync); 180 ms->cb = cb; 181 ms->cb_cls = cb_cls; 182 ms->rs = rs; 183 for (unsigned int i = 0; i < ri->cs_len; i++) 184 { 185 const struct ANASTASIS_ChallengeDetails *cd; 186 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 187 struct SyncEntry *se; 188 189 cd = ANASTASIS_challenge_get_details (ri->cs[i]); 190 if (GNUNET_OK == 191 ANASTASIS_REDUX_lookup_salt_ (&rs->common, 192 cd->provider_url, 193 &provider_salt)) 194 continue; /* provider already ready */ 195 se = GNUNET_new (struct SyncEntry); 196 se->ms = ms; 197 GNUNET_CONTAINER_DLL_insert (ms->se_head, 198 ms->se_tail, 199 se); 200 se->ra = ANASTASIS_REDUX_add_provider_to_state_ (cd->provider_url, 201 rs, 202 &sync_progress, 203 se); 204 } 205 if (NULL == ms->se_head) 206 { 207 /* everything already synced */ 208 ms->rs = NULL; 209 clean_sync (ms); 210 ANASTASIS_REDUX_fail_ (rs, 211 cb, 212 cb_cls, 213 TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED, 214 "already in sync"); 215 return NULL; 216 } 217 ms->ra.cleanup = &clean_sync; 218 ms->ra.cleanup_cls = ms; 219 return &ms->ra; 220 } 221 222 223 struct ANASTASIS_ReduxAction * 224 ANASTASIS_REDUX_poll_providers_ (struct ANASTASIS_ReduxState *rs, 225 const json_t *arguments, 226 ANASTASIS_ActionCallback cb, 227 void *cb_cls) 228 { 229 struct MasterSync *ms; 230 231 if (! rs->common.have_providers) 232 { 233 GNUNET_break (0); 234 ANASTASIS_REDUX_fail_ (rs, 235 cb, 236 cb_cls, 237 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 238 "'authentication_providers' missing"); 239 return NULL; 240 } 241 ms = GNUNET_new (struct MasterSync); 242 ms->cb = cb; 243 ms->cb_cls = cb_cls; 244 ms->rs = rs; 245 for (unsigned int i = 0; i < rs->common.providers_len; i++) 246 { 247 const char *url = rs->common.providers[i].url.url; 248 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 249 struct SyncEntry *se; 250 251 if (GNUNET_OK == 252 ANASTASIS_REDUX_lookup_salt_ (&rs->common, 253 url, 254 &provider_salt)) 255 continue; 256 se = GNUNET_new (struct SyncEntry); 257 se->ms = ms; 258 GNUNET_CONTAINER_DLL_insert (ms->se_head, 259 ms->se_tail, 260 se); 261 se->ra = ANASTASIS_REDUX_add_provider_to_state_ (url, 262 rs, 263 &sync_progress, 264 se); 265 } 266 if (NULL == ms->se_head) 267 { 268 /* everything already synced */ 269 ms->rs = NULL; 270 clean_sync (ms); 271 ANASTASIS_REDUX_fail_ (rs, 272 cb, 273 cb_cls, 274 TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, 275 "already in sync"); 276 return NULL; 277 } 278 ms->ra.cleanup = &clean_sync; 279 ms->ra.cleanup_cls = ms; 280 return &ms->ra; 281 }