merchant_api_patch-private-templates-TEMPLATE_ID.c (9345B)
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-templates-TEMPLATE_ID-new.c 21 * @brief Implementation of the PATCH /private/templates/$TEMPLATE_ID 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-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_CONFLICT: 157 tpr.hr.ec = TALER_JSON_get_error_code (json); 158 tpr.hr.hint = TALER_JSON_get_error_hint (json); 159 break; 160 case MHD_HTTP_INTERNAL_SERVER_ERROR: 161 tpr.hr.ec = TALER_JSON_get_error_code (json); 162 tpr.hr.hint = TALER_JSON_get_error_hint (json); 163 /* Server had an internal issue; we should retry, 164 but this API leaves this to the application */ 165 break; 166 default: 167 TALER_MERCHANT_parse_error_details_ (json, 168 response_code, 169 &tpr.hr); 170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 171 "Unexpected response code %u/%d\n", 172 (unsigned int) response_code, 173 (int) tpr.hr.ec); 174 GNUNET_break_op (0); 175 break; 176 } 177 tph->cb (tph->cb_cls, 178 &tpr); 179 TALER_MERCHANT_patch_private_template_cancel (tph); 180 } 181 182 183 struct TALER_MERCHANT_PatchPrivateTemplateHandle * 184 TALER_MERCHANT_patch_private_template_create ( 185 struct GNUNET_CURL_Context *ctx, 186 const char *url, 187 const char *template_id, 188 const char *template_description, 189 const char *otp_id, 190 json_t *template_contract) 191 { 192 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph; 193 194 tph = GNUNET_new (struct TALER_MERCHANT_PatchPrivateTemplateHandle); 195 tph->ctx = ctx; 196 tph->base_url = GNUNET_strdup (url); 197 tph->template_id = GNUNET_strdup (template_id); 198 tph->template_description = GNUNET_strdup (template_description); 199 if (NULL != otp_id) 200 tph->otp_id = GNUNET_strdup (otp_id); 201 tph->template_contract = json_incref (template_contract); 202 return tph; 203 } 204 205 206 enum GNUNET_GenericReturnValue 207 TALER_MERCHANT_patch_private_template_set_options_ ( 208 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph, 209 unsigned int num_options, 210 const struct TALER_MERCHANT_PatchPrivateTemplateOptionValue *options) 211 { 212 for (unsigned int i = 0; i < num_options; i++) 213 { 214 switch (options[i].option) 215 { 216 case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_END: 217 return GNUNET_OK; 218 case TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_OPTION_EDITABLE_DEFAULTS: 219 tph->editable_defaults 220 = json_incref ((json_t *) options[i].details.editable_defaults); 221 break; 222 default: 223 GNUNET_break (0); 224 return GNUNET_SYSERR; 225 } 226 } 227 return GNUNET_OK; 228 } 229 230 231 enum TALER_ErrorCode 232 TALER_MERCHANT_patch_private_template_start ( 233 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph, 234 TALER_MERCHANT_PatchPrivateTemplateCallback cb, 235 TALER_MERCHANT_PATCH_PRIVATE_TEMPLATE_RESULT_CLOSURE *cb_cls) 236 { 237 json_t *req_obj; 238 CURL *eh; 239 240 tph->cb = cb; 241 tph->cb_cls = cb_cls; 242 { 243 char *path; 244 245 GNUNET_asprintf (&path, 246 "private/templates/%s", 247 tph->template_id); 248 tph->url = TALER_url_join (tph->base_url, 249 path, 250 NULL); 251 GNUNET_free (path); 252 } 253 if (NULL == tph->url) 254 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 255 req_obj = GNUNET_JSON_PACK ( 256 GNUNET_JSON_pack_string ("template_description", 257 tph->template_description), 258 GNUNET_JSON_pack_allow_null ( 259 GNUNET_JSON_pack_string ("otp_id", 260 tph->otp_id)), 261 GNUNET_JSON_pack_object_incref ("template_contract", 262 (json_t *) tph->template_contract), 263 GNUNET_JSON_pack_allow_null ( 264 GNUNET_JSON_pack_object_incref ("editable_defaults", 265 tph->editable_defaults))); 266 eh = TALER_MERCHANT_curl_easy_get_ (tph->url); 267 if ( (NULL == eh) || 268 (GNUNET_OK != 269 TALER_curl_easy_post (&tph->post_ctx, 270 eh, 271 req_obj)) ) 272 { 273 GNUNET_break (0); 274 json_decref (req_obj); 275 if (NULL != eh) 276 curl_easy_cleanup (eh); 277 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 278 } 279 json_decref (req_obj); 280 GNUNET_assert (CURLE_OK == 281 curl_easy_setopt (eh, 282 CURLOPT_CUSTOMREQUEST, 283 MHD_HTTP_METHOD_PATCH)); 284 tph->job = GNUNET_CURL_job_add2 (tph->ctx, 285 eh, 286 tph->post_ctx.headers, 287 &handle_patch_template_finished, 288 tph); 289 if (NULL == tph->job) 290 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 291 return TALER_EC_NONE; 292 } 293 294 295 void 296 TALER_MERCHANT_patch_private_template_cancel ( 297 struct TALER_MERCHANT_PatchPrivateTemplateHandle *tph) 298 { 299 if (NULL != tph->job) 300 { 301 GNUNET_CURL_job_cancel (tph->job); 302 tph->job = NULL; 303 } 304 TALER_curl_easy_post_finished (&tph->post_ctx); 305 json_decref (tph->template_contract); 306 GNUNET_free (tph->url); 307 GNUNET_free (tph->base_url); 308 GNUNET_free (tph->template_id); 309 GNUNET_free (tph->template_description); 310 GNUNET_free (tph->otp_id); 311 json_decref (tph->editable_defaults); 312 GNUNET_free (tph); 313 } 314 315 316 /* end of merchant_api_patch-private-templates-TEMPLATE_ID-new.c */