merchant_api_post-private-accounts.c (9604B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General 16 Public License along with TALER; see the file COPYING.LGPL. 17 If not, see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file merchant_api_post-private-accounts.c 21 * @brief Implementation of the POST /private/accounts request 22 * @author Christian Grothoff 23 */ 24 #include "taler/platform.h" 25 #include <curl/curl.h> 26 #include <jansson.h> 27 #include <microhttpd.h> /* just for HTTP status codes */ 28 #include <gnunet/gnunet_util_lib.h> 29 #include <gnunet/gnunet_curl_lib.h> 30 #include <taler/taler-merchant/post-private-accounts.h> 31 #include "merchant_api_curl_defaults.h" 32 #include "merchant_api_common.h" 33 #include <taler/taler_json_lib.h> 34 #include <taler/taler_curl_lib.h> 35 36 37 /** 38 * Handle for a POST /private/accounts operation. 39 */ 40 struct TALER_MERCHANT_PostPrivateAccountsHandle 41 { 42 /** 43 * Base URL of the merchant backend. 44 */ 45 char *base_url; 46 47 /** 48 * The full URL for this request. 49 */ 50 char *url; 51 52 /** 53 * Handle for the request. 54 */ 55 struct GNUNET_CURL_Job *job; 56 57 /** 58 * Function to call with the result. 59 */ 60 TALER_MERCHANT_PostPrivateAccountsCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls; 66 67 /** 68 * Reference to the execution context. 69 */ 70 struct GNUNET_CURL_Context *ctx; 71 72 /** 73 * Minor context that holds body and headers. 74 */ 75 struct TALER_CURL_PostContext post_ctx; 76 77 /** 78 * Payto URI to add. 79 */ 80 struct TALER_FullPayto payto_uri; 81 82 /** 83 * Optional credit facade URL. 84 */ 85 char *credit_facade_url; 86 87 /** 88 * Optional credit facade credentials (JSON). 89 */ 90 json_t *credit_facade_credentials; 91 92 /** 93 * Optional extra wire subject metadata (JSON). 94 */ 95 char *extra_wire_subject_metadata; 96 }; 97 98 99 /** 100 * Function called when we're done processing the 101 * HTTP POST /private/accounts request. 102 * 103 * @param cls the `struct TALER_MERCHANT_PostPrivateAccountsHandle` 104 * @param response_code HTTP response code, 0 on error 105 * @param response response body, NULL if not in JSON 106 */ 107 static void 108 handle_post_account_finished (void *cls, 109 long response_code, 110 const void *response) 111 { 112 struct TALER_MERCHANT_PostPrivateAccountsHandle *pah = cls; 113 const json_t *json = response; 114 struct TALER_MERCHANT_PostPrivateAccountsResponse apr = { 115 .hr.http_status = (unsigned int) response_code, 116 .hr.reply = json 117 }; 118 119 pah->job = NULL; 120 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 121 "POST /private/accounts completed with response code %u\n", 122 (unsigned int) response_code); 123 switch (response_code) 124 { 125 case 0: 126 apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 127 break; 128 case MHD_HTTP_OK: 129 { 130 struct GNUNET_JSON_Specification spec[] = { 131 GNUNET_JSON_spec_fixed_auto ("h_wire", 132 &apr.details.ok.h_wire), 133 GNUNET_JSON_spec_fixed_auto ("salt", 134 &apr.details.ok.salt), 135 GNUNET_JSON_spec_end () 136 }; 137 138 if (GNUNET_OK != 139 GNUNET_JSON_parse (json, 140 spec, 141 NULL, NULL)) 142 { 143 GNUNET_break_op (0); 144 apr.hr.http_status = 0; 145 apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 146 break; 147 } 148 } 149 break; 150 case MHD_HTTP_ACCEPTED: 151 if (GNUNET_OK != 152 TALER_MERCHANT_parse_mfa_challenge_response_ ( 153 json, 154 &apr.details.accepted)) 155 { 156 GNUNET_break_op (0); 157 apr.hr.http_status = 0; 158 apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 159 } 160 break; 161 case MHD_HTTP_BAD_REQUEST: 162 GNUNET_break_op (0); 163 apr.hr.ec = TALER_JSON_get_error_code (json); 164 apr.hr.hint = TALER_JSON_get_error_hint (json); 165 break; 166 case MHD_HTTP_FORBIDDEN: 167 apr.hr.ec = TALER_JSON_get_error_code (json); 168 apr.hr.hint = TALER_JSON_get_error_hint (json); 169 break; 170 case MHD_HTTP_NOT_FOUND: 171 apr.hr.ec = TALER_JSON_get_error_code (json); 172 apr.hr.hint = TALER_JSON_get_error_hint (json); 173 break; 174 case MHD_HTTP_CONFLICT: 175 apr.hr.ec = TALER_JSON_get_error_code (json); 176 apr.hr.hint = TALER_JSON_get_error_hint (json); 177 break; 178 case MHD_HTTP_INTERNAL_SERVER_ERROR: 179 apr.hr.ec = TALER_JSON_get_error_code (json); 180 apr.hr.hint = TALER_JSON_get_error_hint (json); 181 break; 182 default: 183 TALER_MERCHANT_parse_error_details_ (json, 184 response_code, 185 &apr.hr); 186 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 187 "Unexpected response code %u/%d\n", 188 (unsigned int) response_code, 189 (int) apr.hr.ec); 190 GNUNET_break_op (0); 191 break; 192 } 193 pah->cb (pah->cb_cls, 194 &apr); 195 if (MHD_HTTP_ACCEPTED == response_code) 196 TALER_MERCHANT_mfa_challenge_response_free ( 197 &apr.details.accepted); 198 TALER_MERCHANT_post_private_accounts_cancel (pah); 199 } 200 201 202 struct TALER_MERCHANT_PostPrivateAccountsHandle * 203 TALER_MERCHANT_post_private_accounts_create ( 204 struct GNUNET_CURL_Context *ctx, 205 const char *url, 206 struct TALER_FullPayto payto_uri) 207 { 208 struct TALER_MERCHANT_PostPrivateAccountsHandle *pah; 209 210 pah = GNUNET_new (struct TALER_MERCHANT_PostPrivateAccountsHandle); 211 pah->ctx = ctx; 212 pah->base_url = GNUNET_strdup (url); 213 pah->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto); 214 return pah; 215 } 216 217 218 enum GNUNET_GenericReturnValue 219 TALER_MERCHANT_post_private_accounts_set_options_ ( 220 struct TALER_MERCHANT_PostPrivateAccountsHandle *pah, 221 unsigned int num_options, 222 const struct TALER_MERCHANT_PostPrivateAccountsOptionValue *options) 223 { 224 for (unsigned int i = 0; i < num_options; i++) 225 { 226 switch (options[i].option) 227 { 228 case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_END: 229 return GNUNET_OK; 230 case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_URL: 231 GNUNET_free (pah->credit_facade_url); 232 pah->credit_facade_url = GNUNET_strdup (options[i].details.credit_facade_url); 233 break; 234 case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_CREDIT_FACADE_CREDENTIALS: 235 json_decref (pah->credit_facade_credentials); 236 pah->credit_facade_credentials 237 = json_incref ((json_t *) options[i].details.credit_facade_credentials); 238 break; 239 case TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_OPTION_EXTRA_WIRE_SUBJECT_METADATA: 240 GNUNET_free (pah->extra_wire_subject_metadata); 241 pah->extra_wire_subject_metadata 242 = GNUNET_strdup (options[i].details.extra_wire_subject_metadata); 243 break; 244 default: 245 GNUNET_break (0); 246 return GNUNET_SYSERR; 247 } 248 } 249 return GNUNET_OK; 250 } 251 252 253 enum TALER_ErrorCode 254 TALER_MERCHANT_post_private_accounts_start ( 255 struct TALER_MERCHANT_PostPrivateAccountsHandle *pah, 256 TALER_MERCHANT_PostPrivateAccountsCallback cb, 257 TALER_MERCHANT_POST_PRIVATE_ACCOUNTS_RESULT_CLOSURE *cb_cls) 258 { 259 json_t *req_obj; 260 CURL *eh; 261 262 pah->cb = cb; 263 pah->cb_cls = cb_cls; 264 pah->url = TALER_url_join (pah->base_url, 265 "private/accounts", 266 NULL); 267 if (NULL == pah->url) 268 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 269 req_obj = GNUNET_JSON_PACK ( 270 TALER_JSON_pack_full_payto ( 271 "payto_uri", 272 pah->payto_uri), 273 GNUNET_JSON_pack_allow_null ( 274 GNUNET_JSON_pack_string ( 275 "credit_facade_url", 276 pah->credit_facade_url)), 277 GNUNET_JSON_pack_allow_null ( 278 GNUNET_JSON_pack_object_incref ( 279 "credit_facade_credentials", 280 pah->credit_facade_credentials)), 281 GNUNET_JSON_pack_allow_null ( 282 GNUNET_JSON_pack_string ( 283 "extra_wire_subject_metadata", 284 pah->extra_wire_subject_metadata))); 285 eh = TALER_MERCHANT_curl_easy_get_ (pah->url); 286 if ( (NULL == eh) || 287 (GNUNET_OK != 288 TALER_curl_easy_post (&pah->post_ctx, 289 eh, 290 req_obj)) ) 291 { 292 GNUNET_break (0); 293 json_decref (req_obj); 294 if (NULL != eh) 295 curl_easy_cleanup (eh); 296 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 297 } 298 json_decref (req_obj); 299 pah->job = GNUNET_CURL_job_add2 (pah->ctx, 300 eh, 301 pah->post_ctx.headers, 302 &handle_post_account_finished, 303 pah); 304 if (NULL == pah->job) 305 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 306 return TALER_EC_NONE; 307 } 308 309 310 void 311 TALER_MERCHANT_post_private_accounts_cancel ( 312 struct TALER_MERCHANT_PostPrivateAccountsHandle *pah) 313 { 314 if (NULL != pah->job) 315 { 316 GNUNET_CURL_job_cancel (pah->job); 317 pah->job = NULL; 318 } 319 TALER_curl_easy_post_finished (&pah->post_ctx); 320 GNUNET_free (pah->payto_uri.full_payto); 321 GNUNET_free (pah->url); 322 GNUNET_free (pah->credit_facade_url); 323 json_decref (pah->credit_facade_credentials); 324 GNUNET_free (pah->extra_wire_subject_metadata); 325 GNUNET_free (pah->base_url); 326 GNUNET_free (pah); 327 } 328 329 330 /* end of merchant_api_post-private-accounts.c */