merchant_api_post-private-categories.c (8031B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025-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-categories-new.c 21 * @brief Implementation of the POST /private/categories 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-categories.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/categories operation. 39 */ 40 struct TALER_MERCHANT_PostPrivateCategoriesHandle 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_PostPrivateCategoriesCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_PRIVATE_CATEGORIES_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 * Human-readable name of the category. 79 */ 80 char *name; 81 82 /** 83 * Optional internationalized names (JSON). 84 */ 85 const json_t *name_i18n; 86 }; 87 88 89 /** 90 * Function called when we're done processing the 91 * HTTP POST /private/categories request. 92 * 93 * @param cls the `struct TALER_MERCHANT_PostPrivateCategoriesHandle` 94 * @param response_code HTTP response code, 0 on error 95 * @param response response body, NULL if not in JSON 96 */ 97 static void 98 handle_post_categories_finished (void *cls, 99 long response_code, 100 const void *response) 101 { 102 struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch = cls; 103 const json_t *json = response; 104 struct TALER_MERCHANT_PostPrivateCategoriesResponse cpr = { 105 .hr.http_status = (unsigned int) response_code, 106 .hr.reply = json 107 }; 108 109 ppch->job = NULL; 110 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 111 "POST /private/categories completed with response code %u\n", 112 (unsigned int) response_code); 113 switch (response_code) 114 { 115 case 0: 116 cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 117 break; 118 case MHD_HTTP_OK: 119 { 120 struct GNUNET_JSON_Specification spec[] = { 121 GNUNET_JSON_spec_uint64 ("category_id", 122 &cpr.details.ok.category_id), 123 GNUNET_JSON_spec_end () 124 }; 125 126 if (GNUNET_OK != 127 GNUNET_JSON_parse (json, 128 spec, 129 NULL, NULL)) 130 { 131 GNUNET_break_op (0); 132 cpr.hr.http_status = 0; 133 cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 134 break; 135 } 136 } 137 break; 138 case MHD_HTTP_BAD_REQUEST: 139 cpr.hr.ec = TALER_JSON_get_error_code (json); 140 cpr.hr.hint = TALER_JSON_get_error_hint (json); 141 break; 142 case MHD_HTTP_UNAUTHORIZED: 143 cpr.hr.ec = TALER_JSON_get_error_code (json); 144 cpr.hr.hint = TALER_JSON_get_error_hint (json); 145 break; 146 case MHD_HTTP_FORBIDDEN: 147 cpr.hr.ec = TALER_JSON_get_error_code (json); 148 cpr.hr.hint = TALER_JSON_get_error_hint (json); 149 break; 150 case MHD_HTTP_NOT_FOUND: 151 cpr.hr.ec = TALER_JSON_get_error_code (json); 152 cpr.hr.hint = TALER_JSON_get_error_hint (json); 153 break; 154 case MHD_HTTP_CONFLICT: 155 cpr.hr.ec = TALER_JSON_get_error_code (json); 156 cpr.hr.hint = TALER_JSON_get_error_hint (json); 157 break; 158 case MHD_HTTP_INTERNAL_SERVER_ERROR: 159 cpr.hr.ec = TALER_JSON_get_error_code (json); 160 cpr.hr.hint = TALER_JSON_get_error_hint (json); 161 break; 162 default: 163 TALER_MERCHANT_parse_error_details_ (json, 164 response_code, 165 &cpr.hr); 166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 167 "Unexpected response code %u/%d\n", 168 (unsigned int) response_code, 169 (int) cpr.hr.ec); 170 GNUNET_break_op (0); 171 break; 172 } 173 ppch->cb (ppch->cb_cls, 174 &cpr); 175 TALER_MERCHANT_post_private_categories_cancel (ppch); 176 } 177 178 179 struct TALER_MERCHANT_PostPrivateCategoriesHandle * 180 TALER_MERCHANT_post_private_categories_create ( 181 struct GNUNET_CURL_Context *ctx, 182 const char *url, 183 const char *name) 184 { 185 struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch; 186 187 ppch = GNUNET_new (struct TALER_MERCHANT_PostPrivateCategoriesHandle); 188 ppch->ctx = ctx; 189 ppch->base_url = GNUNET_strdup (url); 190 ppch->name = GNUNET_strdup (name); 191 return ppch; 192 } 193 194 195 enum GNUNET_GenericReturnValue 196 TALER_MERCHANT_post_private_categories_set_options_ ( 197 struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch, 198 unsigned int num_options, 199 const struct TALER_MERCHANT_PostPrivateCategoriesOptionValue *options) 200 { 201 for (unsigned int i = 0; i < num_options; i++) 202 { 203 switch (options[i].option) 204 { 205 case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_END: 206 return GNUNET_OK; 207 case TALER_MERCHANT_POST_PRIVATE_CATEGORIES_OPTION_NAME_I18N: 208 ppch->name_i18n = options[i].details.name_i18n; 209 break; 210 default: 211 GNUNET_break (0); 212 return GNUNET_SYSERR; 213 } 214 } 215 return GNUNET_OK; 216 } 217 218 219 enum TALER_ErrorCode 220 TALER_MERCHANT_post_private_categories_start ( 221 struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch, 222 TALER_MERCHANT_PostPrivateCategoriesCallback cb, 223 TALER_MERCHANT_POST_PRIVATE_CATEGORIES_RESULT_CLOSURE *cb_cls) 224 { 225 json_t *req_obj; 226 CURL *eh; 227 228 ppch->cb = cb; 229 ppch->cb_cls = cb_cls; 230 ppch->url = TALER_url_join (ppch->base_url, 231 "private/categories", 232 NULL); 233 if (NULL == ppch->url) 234 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 235 req_obj = GNUNET_JSON_PACK ( 236 GNUNET_JSON_pack_string ( 237 "name", 238 ppch->name), 239 GNUNET_JSON_pack_allow_null ( 240 GNUNET_JSON_pack_object_incref ( 241 "name_i18n", 242 (json_t *) ppch->name_i18n)) 243 ); 244 eh = TALER_MERCHANT_curl_easy_get_ (ppch->url); 245 if ( (NULL == eh) || 246 (GNUNET_OK != 247 TALER_curl_easy_post (&ppch->post_ctx, 248 eh, 249 req_obj)) ) 250 { 251 GNUNET_break (0); 252 json_decref (req_obj); 253 if (NULL != eh) 254 curl_easy_cleanup (eh); 255 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 256 } 257 json_decref (req_obj); 258 ppch->job = GNUNET_CURL_job_add2 (ppch->ctx, 259 eh, 260 ppch->post_ctx.headers, 261 &handle_post_categories_finished, 262 ppch); 263 if (NULL == ppch->job) 264 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 265 return TALER_EC_NONE; 266 } 267 268 269 void 270 TALER_MERCHANT_post_private_categories_cancel ( 271 struct TALER_MERCHANT_PostPrivateCategoriesHandle *ppch) 272 { 273 if (NULL != ppch->job) 274 { 275 GNUNET_CURL_job_cancel (ppch->job); 276 ppch->job = NULL; 277 } 278 TALER_curl_easy_post_finished (&ppch->post_ctx); 279 GNUNET_free (ppch->name); 280 GNUNET_free (ppch->url); 281 GNUNET_free (ppch->base_url); 282 GNUNET_free (ppch); 283 } 284 285 286 /* end of merchant_api_post-private-categories-new.c */