merchant_api_patch-private-accounts-H_WIRE.c (9382B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022-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_patch-private-accounts-H_WIRE.c 21 * @brief Implementation of the PATCH /private/accounts/$H_WIRE 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/patch-private-accounts-H_WIRE.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 PATCH /private/accounts/$H_WIRE operation. 39 */ 40 struct TALER_MERCHANT_PatchPrivateAccountHandle 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_PatchPrivateAccountCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_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 * Hash of the wire details identifying the account. 79 */ 80 struct TALER_MerchantWireHashP h_wire; 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 PATCH /private/accounts/$H_WIRE request. 102 * 103 * @param cls the `struct TALER_MERCHANT_PatchPrivateAccountHandle` 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_patch_account_finished (void *cls, 109 long response_code, 110 const void *response) 111 { 112 struct TALER_MERCHANT_PatchPrivateAccountHandle *pah = cls; 113 const json_t *json = response; 114 struct TALER_MERCHANT_PatchPrivateAccountResponse par = { 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 "PATCH /private/accounts/$H_WIRE completed with response code %u\n", 122 (unsigned int) response_code); 123 switch (response_code) 124 { 125 case 0: 126 par.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 127 break; 128 case MHD_HTTP_NO_CONTENT: 129 break; 130 case MHD_HTTP_BAD_REQUEST: 131 par.hr.ec = TALER_JSON_get_error_code (json); 132 par.hr.hint = TALER_JSON_get_error_hint (json); 133 GNUNET_break_op (0); 134 break; 135 case MHD_HTTP_UNAUTHORIZED: 136 par.hr.ec = TALER_JSON_get_error_code (json); 137 par.hr.hint = TALER_JSON_get_error_hint (json); 138 break; 139 case MHD_HTTP_FORBIDDEN: 140 par.hr.ec = TALER_JSON_get_error_code (json); 141 par.hr.hint = TALER_JSON_get_error_hint (json); 142 break; 143 case MHD_HTTP_NOT_FOUND: 144 par.hr.ec = TALER_JSON_get_error_code (json); 145 par.hr.hint = TALER_JSON_get_error_hint (json); 146 break; 147 case MHD_HTTP_CONFLICT: 148 par.hr.ec = TALER_JSON_get_error_code (json); 149 par.hr.hint = TALER_JSON_get_error_hint (json); 150 break; 151 case MHD_HTTP_INTERNAL_SERVER_ERROR: 152 par.hr.ec = TALER_JSON_get_error_code (json); 153 par.hr.hint = TALER_JSON_get_error_hint (json); 154 break; 155 default: 156 TALER_MERCHANT_parse_error_details_ (json, 157 response_code, 158 &par.hr); 159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 160 "Unexpected response code %u/%d\n", 161 (unsigned int) response_code, 162 (int) par.hr.ec); 163 GNUNET_break_op (0); 164 break; 165 } 166 pah->cb (pah->cb_cls, 167 &par); 168 TALER_MERCHANT_patch_private_account_cancel (pah); 169 } 170 171 172 struct TALER_MERCHANT_PatchPrivateAccountHandle * 173 TALER_MERCHANT_patch_private_account_create ( 174 struct GNUNET_CURL_Context *ctx, 175 const char *url, 176 const struct TALER_MerchantWireHashP *h_wire) 177 { 178 struct TALER_MERCHANT_PatchPrivateAccountHandle *pah; 179 180 pah = GNUNET_new (struct TALER_MERCHANT_PatchPrivateAccountHandle); 181 pah->ctx = ctx; 182 pah->base_url = GNUNET_strdup (url); 183 pah->h_wire = *h_wire; 184 return pah; 185 } 186 187 188 enum GNUNET_GenericReturnValue 189 TALER_MERCHANT_patch_private_account_set_options_ ( 190 struct TALER_MERCHANT_PatchPrivateAccountHandle *pah, 191 unsigned int num_options, 192 const struct TALER_MERCHANT_PatchPrivateAccountOptionValue *options) 193 { 194 for (unsigned int i = 0; i < num_options; i++) 195 { 196 switch (options[i].option) 197 { 198 case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_END: 199 return GNUNET_OK; 200 case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_CREDIT_FACADE_URL: 201 GNUNET_free (pah->credit_facade_url); 202 pah->credit_facade_url = GNUNET_strdup (options[i].details.credit_facade_url); 203 break; 204 case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_CREDIT_FACADE_CREDENTIALS: 205 json_decref (pah->credit_facade_credentials); 206 pah->credit_facade_credentials 207 = json_incref ((json_t *) options[i].details.credit_facade_credentials); 208 break; 209 case TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_OPTION_EXTRA_WIRE_SUBJECT_METADATA: 210 GNUNET_free (pah->extra_wire_subject_metadata); 211 pah->extra_wire_subject_metadata 212 = GNUNET_strdup (options[i].details.extra_wire_subject_metadata); 213 break; 214 default: 215 GNUNET_break (0); 216 return GNUNET_SYSERR; 217 } 218 } 219 return GNUNET_OK; 220 } 221 222 223 enum TALER_ErrorCode 224 TALER_MERCHANT_patch_private_account_start ( 225 struct TALER_MERCHANT_PatchPrivateAccountHandle *pah, 226 TALER_MERCHANT_PatchPrivateAccountCallback cb, 227 TALER_MERCHANT_PATCH_PRIVATE_ACCOUNT_RESULT_CLOSURE *cb_cls) 228 { 229 json_t *req_obj; 230 CURL *eh; 231 232 pah->cb = cb; 233 pah->cb_cls = cb_cls; 234 { 235 char w_str[sizeof (pah->h_wire) * 2]; 236 char *path; 237 char *end; 238 239 end = GNUNET_STRINGS_data_to_string (&pah->h_wire, 240 sizeof (pah->h_wire), 241 w_str, 242 sizeof (w_str)); 243 *end = '\0'; 244 GNUNET_asprintf (&path, 245 "private/accounts/%s", 246 w_str); 247 pah->url = TALER_url_join (pah->base_url, 248 path, 249 NULL); 250 GNUNET_free (path); 251 } 252 if (NULL == pah->url) 253 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 254 req_obj = GNUNET_JSON_PACK ( 255 GNUNET_JSON_pack_allow_null ( 256 GNUNET_JSON_pack_string ("credit_facade_url", 257 pah->credit_facade_url)), 258 GNUNET_JSON_pack_allow_null ( 259 GNUNET_JSON_pack_object_incref ("credit_facade_credentials", 260 pah->credit_facade_credentials)), 261 GNUNET_JSON_pack_allow_null ( 262 GNUNET_JSON_pack_string ("extra_wire_subject_metadata", 263 pah->extra_wire_subject_metadata) 264 )); 265 eh = TALER_MERCHANT_curl_easy_get_ (pah->url); 266 if ( (NULL == eh) || 267 (GNUNET_OK != 268 TALER_curl_easy_post (&pah->post_ctx, 269 eh, 270 req_obj)) ) 271 { 272 GNUNET_break (0); 273 json_decref (req_obj); 274 if (NULL != eh) 275 curl_easy_cleanup (eh); 276 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 277 } 278 json_decref (req_obj); 279 GNUNET_assert (CURLE_OK == 280 curl_easy_setopt (eh, 281 CURLOPT_CUSTOMREQUEST, 282 MHD_HTTP_METHOD_PATCH)); 283 pah->job = GNUNET_CURL_job_add2 (pah->ctx, 284 eh, 285 pah->post_ctx.headers, 286 &handle_patch_account_finished, 287 pah); 288 if (NULL == pah->job) 289 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 290 return TALER_EC_NONE; 291 } 292 293 294 void 295 TALER_MERCHANT_patch_private_account_cancel ( 296 struct TALER_MERCHANT_PatchPrivateAccountHandle *pah) 297 { 298 if (NULL != pah->job) 299 { 300 GNUNET_CURL_job_cancel (pah->job); 301 pah->job = NULL; 302 } 303 TALER_curl_easy_post_finished (&pah->post_ctx); 304 GNUNET_free (pah->url); 305 GNUNET_free (pah->credit_facade_url); 306 json_decref (pah->credit_facade_credentials); 307 GNUNET_free (pah->extra_wire_subject_metadata); 308 GNUNET_free (pah->base_url); 309 GNUNET_free (pah); 310 } 311 312 313 /* end of merchant_api_patch-private-accounts-H_WIRE.c */