merchant_api_patch-private-templates-TEMPLATE_ID.c (9198B)
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 src/lib/merchant_api_patch-private-templates-TEMPLATE_ID.c 21 * @brief Implementation of the PATCH /private/templates/$TEMPLATE_ID request 22 * @author Christian Grothoff 23 */ 24 #include "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/merchant/patch-private-templates-TEMPLATE_ID.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/templates/$TEMPLATE_ID operation. 39 */ 40 struct TALER_MERCHANT_PatchPrivateTemplateHandle 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_PatchPrivateTemplateCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_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 * Identifier of the template to update. 79 */ 80 char *template_id; 81 82 /** 83 * New human-readable description. 84 */ 85 char *template_description; 86 87 /** 88 * New OTP device ID, or NULL to remove association. 89 */ 90 char *otp_id; 91 92 /** 93 * New template contract (JSON). 94 */ 95 json_t *template_contract; 96 97 /** 98 * Optional editable defaults (JSON object). 99 */ 100 json_t *editable_defaults; 101 }; 102 103 104 /** 105 * Function called when we're done processing the 106 * HTTP PATCH /private/templates/$TEMPLATE_ID request. 107 * 108 * @param cls the `struct TALER_MERCHANT_PatchPrivateTemplateHandle` 109 * @param response_code HTTP response code, 0 on error 110 * @param response response body, NULL if not in JSON 111 */ 112 static void 113 handle_patch_template_finished (void *cls, 114 long response_code, 115 const void *response) 116 { 117 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph = cls; 118 const json_t *json = response; 119 struct TALER_MERCHANT_PatchPrivateTemplateResponse tpr = { 120 .hr.http_status = (unsigned int) response_code, 121 .hr.reply = json 122 }; 123 124 tph->job = NULL; 125 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 126 "PATCH /private/templates/$TEMPLATE_ID completed with response code %u\n", 127 (unsigned int) response_code); 128 switch (response_code) 129 { 130 case 0: 131 tpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 132 break; 133 case MHD_HTTP_NO_CONTENT: 134 break; 135 case MHD_HTTP_BAD_REQUEST: 136 tpr.hr.ec = TALER_JSON_get_error_code (json); 137 tpr.hr.hint = TALER_JSON_get_error_hint (json); 138 GNUNET_break_op (0); 139 /* This should never happen, either us 140 * or the merchant is buggy (or API version conflict); 141 * just pass JSON reply to the application */ 142 break; 143 case MHD_HTTP_UNAUTHORIZED: 144 tpr.hr.ec = TALER_JSON_get_error_code (json); 145 tpr.hr.hint = TALER_JSON_get_error_hint (json); 146 /* Nothing really to verify, merchant says we need to authenticate. */ 147 break; 148 case MHD_HTTP_FORBIDDEN: 149 tpr.hr.ec = TALER_JSON_get_error_code (json); 150 tpr.hr.hint = TALER_JSON_get_error_hint (json); 151 break; 152 case MHD_HTTP_NOT_FOUND: 153 tpr.hr.ec = TALER_JSON_get_error_code (json); 154 tpr.hr.hint = TALER_JSON_get_error_hint (json); 155 break; 156 case MHD_HTTP_INTERNAL_SERVER_ERROR: 157 tpr.hr.ec = TALER_JSON_get_error_code (json); 158 tpr.hr.hint = TALER_JSON_get_error_hint (json); 159 /* Server had an internal issue; we should retry, 160 but this API leaves this to the application */ 161 break; 162 default: 163 TALER_MERCHANT_parse_error_details_ (json, 164 response_code, 165 &tpr.hr); 166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 167 "Unexpected response code %u/%d\n", 168 (unsigned int) response_code, 169 (int) tpr.hr.ec); 170 GNUNET_break_op (0); 171 break; 172 } 173 tph->cb (tph->cb_cls, 174 &tpr); 175 TALER_MERCHANT_patch_private_template_cancel (tph); 176 } 177 178 179 struct TALER_MERCHANT_PatchPrivateTemplateHandle * 180 TALER_MERCHANT_patch_private_template_create ( 181 struct GNUNET_CURL_Context *ctx, 182 const char *url, 183 const char *template_id, 184 const char *template_description, 185 const char *otp_id, 186 json_t *template_contract) 187 { 188 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph; 189 190 tph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateTemplateHandle); 191 tph->ctx = ctx; 192 tph->base_url = GNUNET_strdup (url); 193 tph->template_id = GNUNET_strdup (template_id); 194 tph->template_description = GNUNET_strdup (template_description); 195 if (NULL != otp_id) 196 tph->otp_id = GNUNET_strdup (otp_id); 197 tph->template_contract = json_incref (template_contract); 198 return tph; 199 } 200 201 202 enum GNUNET_GenericReturnValue 203 TALER_MERCHANT_patch_private_template_set_options_ ( 204 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph, 205 unsigned int num_options, 206 const struct TALER_MERCHANT_PatchPrivateTemplateOptionValue *options) 207 { 208 for (unsigned int i = 0; i < num_options; i++) 209 { 210 switch (options[i].option) 211 { 212 case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_END: 213 return GNUNET_OK; 214 case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_EDITABLE_DEFAULTS: 215 tph->editable_defaults 216 = json_incref ((json_t *) options[i].details.editable_defaults); 217 break; 218 default: 219 GNUNET_break (0); 220 return GNUNET_SYSERR; 221 } 222 } 223 return GNUNET_OK; 224 } 225 226 227 enum TALER_ErrorCode 228 TALER_MERCHANT_patch_private_template_start ( 229 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph, 230 TALER_MERCHANT_PatchPrivateTemplateCallback cb, 231 TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls) 232 { 233 json_t *req_obj; 234 CURL *eh; 235 236 tph->cb = cb; 237 tph->cb_cls = cb_cls; 238 { 239 char *path; 240 241 GNUNET_asprintf (&path, 242 "private/templates/%s", 243 tph->template_id); 244 tph->url = TALER_url_join (tph->base_url, 245 path, 246 NULL); 247 GNUNET_free (path); 248 } 249 if (NULL == tph->url) 250 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 251 req_obj = GNUNET_JSON_PACK ( 252 GNUNET_JSON_pack_string ("template_description", 253 tph->template_description), 254 GNUNET_JSON_pack_allow_null ( 255 GNUNET_JSON_pack_string ("otp_id", 256 tph->otp_id)), 257 GNUNET_JSON_pack_object_incref ("template_contract", 258 (json_t *) tph->template_contract), 259 GNUNET_JSON_pack_allow_null ( 260 GNUNET_JSON_pack_object_incref ("editable_defaults", 261 tph->editable_defaults))); 262 eh = TALER_MERCHANT_curl_easy_get_ (tph->url); 263 if ( (NULL == eh) || 264 (GNUNET_OK != 265 TALER_curl_easy_post (&tph->post_ctx, 266 eh, 267 req_obj)) ) 268 { 269 GNUNET_break (0); 270 json_decref (req_obj); 271 if (NULL != eh) 272 curl_easy_cleanup (eh); 273 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 274 } 275 json_decref (req_obj); 276 GNUNET_assert (CURLE_OK == 277 curl_easy_setopt (eh, 278 CURLOPT_CUSTOMREQUEST, 279 MHD_HTTP_METHOD_PATCH)); 280 tph->job = GNUNET_CURL_job_add2 (tph->ctx, 281 eh, 282 tph->post_ctx.headers, 283 &handle_patch_template_finished, 284 tph); 285 if (NULL == tph->job) 286 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 287 return TALER_EC_NONE; 288 } 289 290 291 void 292 TALER_MERCHANT_patch_private_template_cancel ( 293 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph) 294 { 295 if (NULL != tph->job) 296 { 297 GNUNET_CURL_job_cancel (tph->job); 298 tph->job = NULL; 299 } 300 TALER_curl_easy_post_finished (&tph->post_ctx); 301 json_decref (tph->template_contract); 302 GNUNET_free (tph->url); 303 GNUNET_free (tph->base_url); 304 GNUNET_free (tph->template_id); 305 GNUNET_free (tph->template_description); 306 GNUNET_free (tph->otp_id); 307 json_decref (tph->editable_defaults); 308 GNUNET_free (tph); 309 } 310 311 312 /* end of merchant_api_patch-private-templates-TEMPLATE_ID-new.c */