anastasis_api_providers.c (8089B)
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 28 29 /** 30 * Main data structure for sync_providers(). 31 */ 32 struct MasterSync; 33 34 35 /** 36 * Data structure for one provider we are syncing /config with. 37 */ 38 struct SyncEntry 39 { 40 /** 41 * Kept in a DLL. 42 */ 43 struct SyncEntry *next; 44 45 /** 46 * Kept in a DLL. 47 */ 48 struct SyncEntry *prev; 49 50 /** 51 * Sync operation we are part of. 52 */ 53 struct MasterSync *ms; 54 55 /** 56 * Redux action for this provider. 57 */ 58 struct ANASTASIS_ReduxAction *ra; 59 }; 60 61 62 /** 63 * Main data structure for sync_providers(). 64 */ 65 struct MasterSync 66 { 67 /** 68 * Our own sync action we expose externally. 69 */ 70 struct ANASTASIS_ReduxAction ra; 71 /** 72 * Head of DLL with entries per provider. 73 */ 74 struct SyncEntry *se_head; 75 /** 76 * Tail of DLL with entries per provider. 77 */ 78 struct SyncEntry *se_tail; 79 80 /** 81 * Function to call with the result. 82 */ 83 ANASTASIS_ActionCallback cb; 84 85 /** 86 * Closure for @e cb. 87 */ 88 void *cb_cls; 89 90 }; 91 92 93 /** 94 * Free @a cls data structure. 95 * 96 * @param[in] cls data structure to free, must be a `struct MasterSync *` 97 */ 98 static void 99 clean_sync (void *cls) 100 { 101 struct MasterSync *ms = cls; 102 struct SyncEntry *se; 103 104 while (NULL != (se = ms->se_head)) 105 { 106 GNUNET_CONTAINER_DLL_remove (ms->se_head, 107 ms->se_tail, 108 se); 109 se->ra->cleanup (se->ra->cleanup_cls); 110 GNUNET_free (se); 111 } 112 GNUNET_free (ms); 113 } 114 115 116 /** 117 * Function called when we have made progress on any of the 118 * providers we are trying to sync with. 119 * 120 * @param cls closure 121 * @param error error code, #TALER_EC_NONE if @a new_bs is the new successful state 122 * @param new_state the new state of the operation (client should json_incref() to keep an alias) 123 */ 124 static void 125 sync_progress (void *cls, 126 enum TALER_ErrorCode error, 127 json_t *new_state) 128 { 129 struct SyncEntry *se = cls; 130 struct MasterSync *ms = se->ms; 131 132 GNUNET_CONTAINER_DLL_remove (ms->se_head, 133 ms->se_tail, 134 se); 135 GNUNET_free (se); 136 ms->cb (ms->cb_cls, 137 error, 138 new_state); 139 clean_sync (ms); 140 } 141 142 143 struct ANASTASIS_ReduxAction * 144 ANASTASIS_REDUX_sync_providers_ (json_t *state, 145 const json_t *arguments, 146 ANASTASIS_ActionCallback cb, 147 void *cb_cls) 148 { 149 json_t *rd; 150 json_t *cs_arr; 151 struct MasterSync *ms; 152 153 rd = json_object_get (state, 154 "recovery_document"); 155 if (NULL == rd) 156 { 157 GNUNET_break (0); 158 ANASTASIS_redux_fail_ (cb, 159 cb_cls, 160 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 161 "'recovery_document' missing"); 162 return NULL; 163 } 164 cs_arr = json_object_get (rd, 165 "challenges"); 166 if (! json_is_array (cs_arr)) 167 { 168 GNUNET_break_op (0); 169 ANASTASIS_redux_fail_ (cb, 170 cb_cls, 171 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 172 "'recovery_document' must be an array"); 173 return NULL; 174 } 175 ms = GNUNET_new (struct MasterSync); 176 ms->cb = cb; 177 ms->cb_cls = cb_cls; 178 { 179 json_t *cs; 180 unsigned int n_index; 181 182 json_array_foreach (cs_arr, n_index, cs) 183 { 184 const char *provider_url; 185 struct GNUNET_JSON_Specification spec[] = { 186 GNUNET_JSON_spec_string ("url", 187 &provider_url), 188 GNUNET_JSON_spec_end () 189 }; 190 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 191 struct SyncEntry *se; 192 193 if (GNUNET_OK != 194 GNUNET_JSON_parse (cs, 195 spec, 196 NULL, NULL)) 197 { 198 GNUNET_break_op (0); 199 ANASTASIS_redux_fail_ (cb, 200 cb_cls, 201 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 202 "'recovery_document' missing"); 203 clean_sync (ms); 204 return NULL; 205 } 206 if (GNUNET_OK == 207 ANASTASIS_reducer_lookup_salt (state, 208 provider_url, 209 &provider_salt)) 210 continue; /* provider already ready */ 211 se = GNUNET_new (struct SyncEntry); 212 se->ms = ms; 213 GNUNET_CONTAINER_DLL_insert (ms->se_head, 214 ms->se_tail, 215 se); 216 se->ra = ANASTASIS_REDUX_add_provider_to_state_ (provider_url, 217 state, 218 &sync_progress, 219 se); 220 } 221 } 222 if (NULL == ms->se_head) 223 { 224 /* everything already synced */ 225 clean_sync (ms); 226 ANASTASIS_redux_fail_ (cb, 227 cb_cls, 228 TALER_EC_ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED, 229 "already in sync"); 230 return NULL; 231 } 232 ms->ra.cleanup = &clean_sync; 233 ms->ra.cleanup_cls = ms; 234 return &ms->ra; 235 } 236 237 238 struct ANASTASIS_ReduxAction * 239 ANASTASIS_REDUX_poll_providers_ (json_t *state, 240 const json_t *arguments, 241 ANASTASIS_ActionCallback cb, 242 void *cb_cls) 243 { 244 json_t *ap; 245 const char *url; 246 json_t *obj; 247 struct MasterSync *ms; 248 249 ap = json_object_get (state, 250 "authentication_providers"); 251 if (NULL == ap) 252 { 253 GNUNET_break (0); 254 ANASTASIS_redux_fail_ (cb, 255 cb_cls, 256 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 257 "'authentication_providers' missing"); 258 return NULL; 259 } 260 ms = GNUNET_new (struct MasterSync); 261 ms->cb = cb; 262 ms->cb_cls = cb_cls; 263 json_object_foreach (ap, url, obj) 264 { 265 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 266 struct SyncEntry *se; 267 struct ANASTASIS_ReduxAction *ra; 268 269 if (GNUNET_OK == 270 ANASTASIS_reducer_lookup_salt (state, 271 url, 272 &provider_salt)) 273 continue; 274 se = GNUNET_new (struct SyncEntry); 275 se->ms = ms; 276 GNUNET_CONTAINER_DLL_insert (ms->se_head, 277 ms->se_tail, 278 se); 279 ra = ANASTASIS_REDUX_add_provider_to_state_ (url, 280 state, 281 &sync_progress, 282 se); 283 if (NULL == ra) 284 return NULL; /* sync_progress already called! */ 285 se->ra = ra; 286 } 287 if (NULL == ms->se_head) 288 { 289 /* everything already synced */ 290 clean_sync (ms); 291 ANASTASIS_redux_fail_ (cb, 292 cb_cls, 293 TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID, 294 "already in sync"); 295 return NULL; 296 } 297 ms->ra.cleanup = &clean_sync; 298 ms->ra.cleanup_cls = ms; 299 return &ms->ra; 300 }