merchant_api_post-management-instances.c (14732B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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-management-instances-new.c 21 * @brief Implementation of the POST /management/instances 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-management-instances.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 /management/instances operation. 39 */ 40 struct TALER_MERCHANT_PostManagementInstancesHandle 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_PostManagementInstancesCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_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 * Instance identifier. 79 */ 80 char *instance_id; 81 82 /** 83 * Human-readable name. 84 */ 85 char *name; 86 87 /** 88 * Address (JSON). 89 */ 90 json_t *address; 91 92 /** 93 * Jurisdiction (JSON). 94 */ 95 json_t *jurisdiction; 96 97 /** 98 * Whether to use the STEFAN curve. 99 */ 100 bool use_stefan; 101 102 /** 103 * Default wire transfer delay (optional, zero if not set). 104 */ 105 struct GNUNET_TIME_Relative default_wire_transfer_delay; 106 107 /** 108 * Default payment deadline (optional, zero if not set). 109 */ 110 struct GNUNET_TIME_Relative default_pay_delay; 111 112 /** 113 * Default refund deadline (optional, zero if not set). 114 */ 115 struct GNUNET_TIME_Relative default_refund_delay; 116 117 /** 118 * Default wire transfer rounding interval (optional, zero if not set). 119 */ 120 enum GNUNET_TIME_RounderInterval default_wire_transfer_rounding_interval; 121 122 /** 123 * Authentication password (or NULL). 124 */ 125 char *auth_password; 126 127 /** 128 * Optional email address. 129 */ 130 char *email; 131 132 /** 133 * Optional phone number. 134 */ 135 char *phone_number; 136 137 /** 138 * Optional website URL. 139 */ 140 char *website; 141 142 /** 143 * Optional logo. 144 */ 145 char *logo; 146 147 /** 148 * True if default_pay_delay was explicitly set via options. 149 */ 150 bool have_default_pay_delay; 151 152 /** 153 * True if default_refund_delay was explicitly set via options. 154 */ 155 bool have_default_refund_delay; 156 157 /** 158 * True if default_wire_transfer_delay was explicitly set via options. 159 */ 160 bool have_default_wire_transfer_delay; 161 162 /** 163 * True if default_wire_transfer_rounding_interval was explicitly set via options. 164 */ 165 bool have_default_wire_transfer_rounding_interval; 166 }; 167 168 169 /** 170 * Function called when we're done processing the 171 * HTTP POST /management/instances request. 172 * 173 * @param cls the `struct TALER_MERCHANT_PostManagementInstancesHandle` 174 * @param response_code HTTP response code, 0 on error 175 * @param response response body, NULL if not in JSON 176 */ 177 static void 178 handle_post_management_instances_finished (void *cls, 179 long response_code, 180 const void *response) 181 { 182 struct TALER_MERCHANT_PostManagementInstancesHandle *pmih = cls; 183 const json_t *json = response; 184 struct TALER_MERCHANT_PostManagementInstancesResponse mir = { 185 .hr.http_status = (unsigned int) response_code, 186 .hr.reply = json 187 }; 188 189 pmih->job = NULL; 190 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 191 "POST /management/instances completed with response code %u\n", 192 (unsigned int) response_code); 193 switch (response_code) 194 { 195 case 0: 196 mir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 197 break; 198 case MHD_HTTP_NO_CONTENT: 199 break; 200 case MHD_HTTP_ACCEPTED: 201 if (GNUNET_OK != 202 TALER_MERCHANT_parse_mfa_challenge_response_ ( 203 json, 204 &mir.details.accepted)) 205 { 206 GNUNET_break_op (0); 207 mir.hr.http_status = 0; 208 mir.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 209 } 210 break; 211 case MHD_HTTP_BAD_REQUEST: 212 mir.hr.ec = TALER_JSON_get_error_code (json); 213 mir.hr.hint = TALER_JSON_get_error_hint (json); 214 break; 215 case MHD_HTTP_UNAUTHORIZED: 216 mir.hr.ec = TALER_JSON_get_error_code (json); 217 mir.hr.hint = TALER_JSON_get_error_hint (json); 218 break; 219 case MHD_HTTP_FORBIDDEN: 220 mir.hr.ec = TALER_JSON_get_error_code (json); 221 mir.hr.hint = TALER_JSON_get_error_hint (json); 222 break; 223 case MHD_HTTP_NOT_FOUND: 224 mir.hr.ec = TALER_JSON_get_error_code (json); 225 mir.hr.hint = TALER_JSON_get_error_hint (json); 226 break; 227 case MHD_HTTP_CONFLICT: 228 mir.hr.ec = TALER_JSON_get_error_code (json); 229 mir.hr.hint = TALER_JSON_get_error_hint (json); 230 break; 231 case MHD_HTTP_INTERNAL_SERVER_ERROR: 232 mir.hr.ec = TALER_JSON_get_error_code (json); 233 mir.hr.hint = TALER_JSON_get_error_hint (json); 234 break; 235 default: 236 TALER_MERCHANT_parse_error_details_ (json, 237 response_code, 238 &mir.hr); 239 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 240 "Unexpected response code %u/%d\n", 241 (unsigned int) response_code, 242 (int) mir.hr.ec); 243 GNUNET_break_op (0); 244 break; 245 } 246 pmih->cb (pmih->cb_cls, 247 &mir); 248 if (MHD_HTTP_ACCEPTED == response_code) 249 TALER_MERCHANT_mfa_challenge_response_free ( 250 &mir.details.accepted); 251 TALER_MERCHANT_post_management_instances_cancel (pmih); 252 } 253 254 255 struct TALER_MERCHANT_PostManagementInstancesHandle * 256 TALER_MERCHANT_post_management_instances_create ( 257 struct GNUNET_CURL_Context *ctx, 258 const char *url, 259 const char *instance_id, 260 const char *name, 261 const json_t *address, 262 const json_t *jurisdiction, 263 bool use_stefan) 264 { 265 struct TALER_MERCHANT_PostManagementInstancesHandle *pmih; 266 267 pmih = GNUNET_new (struct TALER_MERCHANT_PostManagementInstancesHandle); 268 pmih->ctx = ctx; 269 pmih->base_url = GNUNET_strdup (url); 270 pmih->instance_id = GNUNET_strdup (instance_id); 271 pmih->name = GNUNET_strdup (name); 272 pmih->address = json_incref ((json_t *) address); 273 pmih->jurisdiction = json_incref ((json_t *) jurisdiction); 274 pmih->use_stefan = use_stefan; 275 return pmih; 276 } 277 278 279 enum GNUNET_GenericReturnValue 280 TALER_MERCHANT_post_management_instances_set_options_ ( 281 struct TALER_MERCHANT_PostManagementInstancesHandle *pmih, 282 unsigned int num_options, 283 const struct TALER_MERCHANT_PostManagementInstancesOptionValue *options) 284 { 285 for (unsigned int i = 0; i < num_options; i++) 286 { 287 switch (options[i].option) 288 { 289 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_END: 290 return GNUNET_OK; 291 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_PAY_DELAY: 292 pmih->default_pay_delay = options[i].details.default_pay_delay; 293 pmih->have_default_pay_delay = true; 294 break; 295 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_REFUND_DELAY: 296 pmih->default_refund_delay = options[i].details.default_refund_delay; 297 pmih->have_default_refund_delay = true; 298 break; 299 case 300 TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_WIRE_TRANSFER_DELAY 301 : 302 pmih->default_wire_transfer_delay 303 = options[i].details.default_wire_transfer_delay; 304 pmih->have_default_wire_transfer_delay = true; 305 break; 306 case 307 TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL 308 : 309 pmih->default_wire_transfer_rounding_interval 310 = options[i].details.default_wire_transfer_rounding_interval; 311 pmih->have_default_wire_transfer_rounding_interval = true; 312 break; 313 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_EMAIL: 314 GNUNET_free (pmih->email); 315 if (NULL != options[i].details.email) 316 pmih->email = GNUNET_strdup (options[i].details.email); 317 break; 318 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_PHONE_NUMBER: 319 GNUNET_free (pmih->phone_number); 320 if (NULL != options[i].details.phone_number) 321 pmih->phone_number = GNUNET_strdup (options[i].details.phone_number); 322 break; 323 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_WEBSITE: 324 GNUNET_free (pmih->website); 325 if (NULL != options[i].details.website) 326 pmih->website = GNUNET_strdup (options[i].details.website); 327 break; 328 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_LOGO: 329 GNUNET_free (pmih->logo); 330 if (NULL != options[i].details.logo) 331 pmih->logo = GNUNET_strdup (options[i].details.logo); 332 break; 333 case TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_OPTION_AUTH_PASSWORD: 334 GNUNET_free (pmih->auth_password); 335 if (NULL != options[i].details.auth_password) 336 pmih->auth_password = GNUNET_strdup ( 337 options[i].details.auth_password); 338 break; 339 default: 340 GNUNET_break (0); 341 return GNUNET_SYSERR; 342 } 343 } 344 return GNUNET_OK; 345 } 346 347 348 enum TALER_ErrorCode 349 TALER_MERCHANT_post_management_instances_start ( 350 struct TALER_MERCHANT_PostManagementInstancesHandle *pmih, 351 TALER_MERCHANT_PostManagementInstancesCallback cb, 352 TALER_MERCHANT_POST_MANAGEMENT_INSTANCES_RESULT_CLOSURE *cb_cls) 353 { 354 json_t *req_obj; 355 json_t *auth_obj; 356 CURL *eh; 357 358 pmih->cb = cb; 359 pmih->cb_cls = cb_cls; 360 pmih->url = TALER_url_join (pmih->base_url, 361 "management/instances", 362 NULL); 363 if (NULL == pmih->url) 364 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 365 if (NULL != pmih->auth_password) 366 { 367 auth_obj = GNUNET_JSON_PACK ( 368 GNUNET_JSON_pack_string ("method", 369 "token"), 370 GNUNET_JSON_pack_string ("password", 371 pmih->auth_password)); 372 } 373 else 374 { 375 auth_obj = GNUNET_JSON_PACK ( 376 GNUNET_JSON_pack_string ("method", 377 "external")); 378 } 379 if (NULL == auth_obj) 380 { 381 GNUNET_break (0); 382 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 383 } 384 req_obj = GNUNET_JSON_PACK ( 385 GNUNET_JSON_pack_string ("id", 386 pmih->instance_id), 387 GNUNET_JSON_pack_string ("name", 388 pmih->name), 389 GNUNET_JSON_pack_object_incref ("address", 390 pmih->address), 391 GNUNET_JSON_pack_object_incref ("jurisdiction", 392 pmih->jurisdiction), 393 GNUNET_JSON_pack_bool ("use_stefan", 394 pmih->use_stefan), 395 GNUNET_JSON_pack_allow_null ( 396 pmih->have_default_wire_transfer_delay 397 ? GNUNET_JSON_pack_time_rel ("default_wire_transfer_delay", 398 pmih->default_wire_transfer_delay) 399 : GNUNET_JSON_pack_string ("default_wire_transfer_delay", 400 NULL)), 401 GNUNET_JSON_pack_allow_null ( 402 pmih->have_default_pay_delay 403 ? GNUNET_JSON_pack_time_rel ("default_pay_delay", 404 pmih->default_pay_delay) 405 : GNUNET_JSON_pack_string ("default_pay_delay", 406 NULL)), 407 GNUNET_JSON_pack_allow_null ( 408 pmih->have_default_refund_delay 409 ? GNUNET_JSON_pack_time_rel ("default_refund_delay", 410 pmih->default_refund_delay) 411 : GNUNET_JSON_pack_string ("default_refund_delay", 412 NULL)), 413 GNUNET_JSON_pack_allow_null ( 414 pmih->have_default_wire_transfer_rounding_interval 415 ? GNUNET_JSON_pack_time_rounder_interval ( 416 "default_wire_transfer_rounding_interval", 417 pmih->default_wire_transfer_rounding_interval) 418 : GNUNET_JSON_pack_string ( 419 "default_wire_transfer_rounding_interval", 420 NULL)), 421 GNUNET_JSON_pack_allow_null ( 422 GNUNET_JSON_pack_string ("email", 423 pmih->email)), 424 GNUNET_JSON_pack_allow_null ( 425 GNUNET_JSON_pack_string ("phone_number", 426 pmih->phone_number)), 427 GNUNET_JSON_pack_allow_null ( 428 GNUNET_JSON_pack_string ("website", 429 pmih->website)), 430 GNUNET_JSON_pack_allow_null ( 431 GNUNET_JSON_pack_string ("logo", 432 pmih->logo)), 433 GNUNET_JSON_pack_object_steal ("auth", 434 auth_obj)); 435 eh = TALER_MERCHANT_curl_easy_get_ (pmih->url); 436 if ( (NULL == eh) || 437 (GNUNET_OK != 438 TALER_curl_easy_post (&pmih->post_ctx, 439 eh, 440 req_obj)) ) 441 { 442 GNUNET_break (0); 443 json_decref (req_obj); 444 if (NULL != eh) 445 curl_easy_cleanup (eh); 446 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 447 } 448 json_decref (req_obj); 449 pmih->job = GNUNET_CURL_job_add2 (pmih->ctx, 450 eh, 451 pmih->post_ctx.headers, 452 &handle_post_management_instances_finished, 453 pmih); 454 if (NULL == pmih->job) 455 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 456 return TALER_EC_NONE; 457 } 458 459 460 void 461 TALER_MERCHANT_post_management_instances_cancel ( 462 struct TALER_MERCHANT_PostManagementInstancesHandle *pmih) 463 { 464 if (NULL != pmih->job) 465 { 466 GNUNET_CURL_job_cancel (pmih->job); 467 pmih->job = NULL; 468 } 469 TALER_curl_easy_post_finished (&pmih->post_ctx); 470 json_decref (pmih->address); 471 json_decref (pmih->jurisdiction); 472 GNUNET_free (pmih->instance_id); 473 GNUNET_free (pmih->name); 474 GNUNET_free (pmih->auth_password); 475 GNUNET_free (pmih->email); 476 GNUNET_free (pmih->phone_number); 477 GNUNET_free (pmih->website); 478 GNUNET_free (pmih->logo); 479 GNUNET_free (pmih->url); 480 GNUNET_free (pmih->base_url); 481 GNUNET_free (pmih); 482 } 483 484 485 /* end of merchant_api_post-management-instances-new.c */