merchant_api_post-private-categories.c (6145B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free 7 Software Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU Lesser General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with TALER; see the file COPYING.LGPL. 16 If not, see <http://www.gnu.org/licenses/> 17 */ 18 /** 19 * @file merchant_api_post-private-categories.c 20 * @brief Implementation of POST /private/categories 21 * @author Bohdan Potuzhnyi 22 */ 23 #include "taler/platform.h" 24 #include <curl/curl.h> 25 #include <jansson.h> 26 #include <microhttpd.h> 27 #include <gnunet/gnunet_util_lib.h> 28 #include "taler/taler_merchant_service.h" 29 #include "merchant_api_curl_defaults.h" 30 #include "merchant_api_common.h" 31 #include <taler/taler_json_lib.h> 32 #include <taler/taler_curl_lib.h> 33 34 /** 35 * Handle for a POST /private/categories operation. 36 */ 37 struct TALER_MERCHANT_CategoriesPostHandle 38 { 39 /** 40 * Fully qualified request URL. 41 */ 42 char *url; 43 44 /** 45 * CURL job handle. 46 */ 47 struct GNUNET_CURL_Job *job; 48 49 /** 50 * Callback to invoke with the result. 51 */ 52 TALER_MERCHANT_CategoriesPostCallback cb; 53 54 /** 55 * Closure for @a cb. 56 */ 57 void *cb_cls; 58 59 /** 60 * Execution context. 61 */ 62 struct GNUNET_CURL_Context *ctx; 63 64 /** 65 * Helper keeping POST body and headers alive. 66 */ 67 struct TALER_CURL_PostContext post_ctx; 68 }; 69 70 /** 71 * Called when the HTTP transfer finishes. 72 * 73 * @param cls operation handle 74 * @param response_code HTTP status (0 on network / parsing failures) 75 * @param response parsed JSON reply (NULL if unavailable) 76 */ 77 static void 78 handle_post_categories_finished (void *cls, 79 long response_code, 80 const void *response) 81 { 82 struct TALER_MERCHANT_CategoriesPostHandle *cph = cls; 83 const json_t *json = response; 84 struct TALER_MERCHANT_CategoriesPostResponse cpr = { 85 .hr.http_status = (unsigned int) response_code, 86 .hr.reply = json 87 }; 88 89 cph->job = NULL; 90 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 91 "POST /private/categories completed with status %u\n", 92 (unsigned int) response_code); 93 switch (response_code) 94 { 95 case MHD_HTTP_OK: 96 { 97 const char *err_name; 98 unsigned int err_line; 99 struct GNUNET_JSON_Specification spec[] = { 100 GNUNET_JSON_spec_uint64 ("category_id", 101 &cpr.category_id), 102 GNUNET_JSON_spec_end () 103 }; 104 105 if (GNUNET_OK != 106 GNUNET_JSON_parse (json, 107 spec, 108 &err_name, 109 &err_line)) 110 { 111 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 112 "Invalid response for field %s\n", 113 err_name); 114 cpr.hr.http_status = 0; 115 cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 116 } 117 break; 118 } 119 case MHD_HTTP_BAD_REQUEST: 120 case MHD_HTTP_UNAUTHORIZED: 121 case MHD_HTTP_FORBIDDEN: 122 case MHD_HTTP_NOT_FOUND: 123 case MHD_HTTP_CONFLICT: 124 case MHD_HTTP_INTERNAL_SERVER_ERROR: 125 cpr.hr.ec = TALER_JSON_get_error_code (json); 126 cpr.hr.hint = TALER_JSON_get_error_hint (json); 127 break; 128 case 0: 129 cpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 130 break; 131 default: 132 TALER_MERCHANT_parse_error_details_ (json, 133 response_code, 134 &cpr.hr); 135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 136 "Unexpected response %u/%d for POST /private/categories\n", 137 (unsigned int) response_code, 138 (int) cpr.hr.ec); 139 GNUNET_break_op (0); 140 break; 141 } 142 cph->cb (cph->cb_cls, 143 &cpr); 144 TALER_MERCHANT_categories_post_cancel (cph); 145 } 146 147 148 struct TALER_MERCHANT_CategoriesPostHandle * 149 TALER_MERCHANT_categories_post ( 150 struct GNUNET_CURL_Context *ctx, 151 const char *backend_url, 152 const char *name, 153 const json_t *name_i18n, 154 TALER_MERCHANT_CategoriesPostCallback cb, 155 void *cb_cls) 156 { 157 struct TALER_MERCHANT_CategoriesPostHandle *cph; 158 json_t *req_obj; 159 160 req_obj = GNUNET_JSON_PACK ( 161 GNUNET_JSON_pack_string ("name", 162 name), 163 GNUNET_JSON_pack_allow_null ( 164 GNUNET_JSON_pack_object_incref ("name_i18n", 165 (json_t *) name_i18n))); 166 cph = GNUNET_new (struct TALER_MERCHANT_CategoriesPostHandle); 167 cph->ctx = ctx; 168 cph->cb = cb; 169 cph->cb_cls = cb_cls; 170 cph->url = TALER_url_join (backend_url, 171 "private/categories", 172 NULL); 173 if (NULL == cph->url) 174 { 175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 176 "Failed to build /private/categories URL\n"); 177 json_decref (req_obj); 178 GNUNET_free (cph); 179 return NULL; 180 } 181 { 182 CURL *eh; 183 184 eh = TALER_MERCHANT_curl_easy_get_ (cph->url); 185 if (GNUNET_OK != 186 TALER_curl_easy_post (&cph->post_ctx, 187 eh, 188 req_obj)) 189 { 190 GNUNET_break (0); 191 curl_easy_cleanup (eh); 192 json_decref (req_obj); 193 GNUNET_free (cph->url); 194 GNUNET_free (cph); 195 return NULL; 196 } 197 json_decref (req_obj); 198 cph->job = GNUNET_CURL_job_add2 (ctx, 199 eh, 200 cph->post_ctx.headers, 201 &handle_post_categories_finished, 202 cph); 203 } 204 return cph; 205 } 206 207 208 void 209 TALER_MERCHANT_categories_post_cancel ( 210 struct TALER_MERCHANT_CategoriesPostHandle *cph) 211 { 212 if (NULL != cph->job) 213 { 214 GNUNET_CURL_job_cancel (cph->job); 215 cph->job = NULL; 216 } 217 TALER_curl_easy_post_finished (&cph->post_ctx); 218 GNUNET_free (cph->url); 219 GNUNET_free (cph); 220 } 221 222 223 /* end of merchant_api_post_categories.c */