merchant_api_post-private-fountains.c (7849B)
1 /* 2 This file is part of TALER 3 Copyright (C) 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_post-private-fountains.c 21 * @brief Implementation of the POST /private/fountains request 22 * @author Bohdan Potuzhnyi 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/post-private-fountains.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/fountains operation. 39 */ 40 struct TALER_MERCHANT_PostPrivateFountainsHandle 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_PostPrivateFountainsCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_POST_PRIVATE_FOUNTAINS_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 * Request body. 79 */ 80 json_t *req_obj; 81 }; 82 83 84 /** 85 * Function called when we're done processing the 86 * HTTP POST /private/fountains request. 87 * 88 * @param cls the `struct TALER_MERCHANT_PostPrivateFountainsHandle` 89 * @param response_code HTTP response code, 0 on error 90 * @param response response body, NULL if not in JSON 91 */ 92 static void 93 handle_post_fountains_finished (void *cls, 94 long response_code, 95 const void *response) 96 { 97 struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh = cls; 98 const json_t *json = response; 99 struct TALER_MERCHANT_PostPrivateFountainsResponse pfr = { 100 .hr.http_status = (unsigned int) response_code, 101 .hr.reply = json 102 }; 103 104 ppfh->job = NULL; 105 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 106 "POST /private/fountains completed with response code %u\n", 107 (unsigned int) response_code); 108 switch (response_code) 109 { 110 case 0: 111 pfr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 112 break; 113 case MHD_HTTP_OK: 114 { 115 struct GNUNET_JSON_Specification spec[] = { 116 GNUNET_JSON_spec_string ("fountain_id", 117 &pfr.details.ok.fountain_id), 118 GNUNET_JSON_spec_string ("fountain_secret", 119 &pfr.details.ok.fountain_secret), 120 GNUNET_JSON_spec_end () 121 }; 122 123 if (GNUNET_OK != 124 GNUNET_JSON_parse (json, 125 spec, 126 NULL, 127 NULL)) 128 { 129 GNUNET_break_op (0); 130 pfr.hr.http_status = 0; 131 pfr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 132 } 133 break; 134 } 135 case MHD_HTTP_BAD_REQUEST: 136 case MHD_HTTP_UNAUTHORIZED: 137 case MHD_HTTP_FORBIDDEN: 138 case MHD_HTTP_NOT_FOUND: 139 case MHD_HTTP_INTERNAL_SERVER_ERROR: 140 pfr.hr.ec = TALER_JSON_get_error_code (json); 141 pfr.hr.hint = TALER_JSON_get_error_hint (json); 142 break; 143 default: 144 TALER_MERCHANT_parse_error_details_ (json, 145 response_code, 146 &pfr.hr); 147 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 148 "Unexpected response code %u/%d\n", 149 (unsigned int) response_code, 150 (int) pfr.hr.ec); 151 GNUNET_break_op (0); 152 break; 153 } 154 ppfh->cb (ppfh->cb_cls, 155 &pfr); 156 TALER_MERCHANT_post_private_fountains_cancel (ppfh); 157 } 158 159 160 struct TALER_MERCHANT_PostPrivateFountainsHandle * 161 TALER_MERCHANT_post_private_fountains_create ( 162 struct GNUNET_CURL_Context *ctx, 163 const char *url, 164 const char *description, 165 struct GNUNET_TIME_Relative poll_freq, 166 unsigned int num_grants, 167 const struct TALER_MERCHANT_FountainGrant *grants) 168 { 169 struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh; 170 json_t *jgrants; 171 172 jgrants = json_array (); 173 GNUNET_assert (NULL != jgrants); 174 for (unsigned int i = 0; i < num_grants; i++) 175 { 176 GNUNET_assert (0 == 177 json_array_append_new ( 178 jgrants, 179 GNUNET_JSON_PACK ( 180 GNUNET_JSON_pack_string ("token_family_slug", 181 grants[i].token_family_slug), 182 GNUNET_JSON_pack_uint64 ("tokens_per_period_limit", 183 grants[i]. 184 tokens_per_period_limit), 185 GNUNET_JSON_pack_uint64 ("tokens_per_period_stash", 186 grants[i]. 187 tokens_per_period_stash), 188 GNUNET_JSON_pack_uint64 ("key_window_size", 189 grants[i].key_window_size)))); 190 } 191 ppfh = GNUNET_new (struct TALER_MERCHANT_PostPrivateFountainsHandle); 192 ppfh->ctx = ctx; 193 ppfh->base_url = GNUNET_strdup (url); 194 ppfh->req_obj = GNUNET_JSON_PACK ( 195 GNUNET_JSON_pack_string ("description", 196 description), 197 GNUNET_JSON_pack_time_rel ("poll_freq", 198 poll_freq), 199 GNUNET_JSON_pack_array_steal ("grants", 200 jgrants)); 201 return ppfh; 202 } 203 204 205 enum TALER_ErrorCode 206 TALER_MERCHANT_post_private_fountains_start ( 207 struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh, 208 TALER_MERCHANT_PostPrivateFountainsCallback cb, 209 TALER_MERCHANT_POST_PRIVATE_FOUNTAINS_RESULT_CLOSURE *cb_cls) 210 { 211 CURL *eh; 212 213 ppfh->cb = cb; 214 ppfh->cb_cls = cb_cls; 215 ppfh->url = TALER_url_join (ppfh->base_url, 216 "private/fountains", 217 NULL); 218 if (NULL == ppfh->url) 219 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 220 eh = TALER_MERCHANT_curl_easy_get_ (ppfh->url); 221 if ( (NULL == eh) || 222 (GNUNET_OK != 223 TALER_curl_easy_post (&ppfh->post_ctx, 224 eh, 225 ppfh->req_obj)) ) 226 { 227 GNUNET_break (0); 228 if (NULL != eh) 229 curl_easy_cleanup (eh); 230 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 231 } 232 ppfh->job = GNUNET_CURL_job_add2 (ppfh->ctx, 233 eh, 234 ppfh->post_ctx.headers, 235 &handle_post_fountains_finished, 236 ppfh); 237 if (NULL == ppfh->job) 238 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 239 return TALER_EC_NONE; 240 } 241 242 243 void 244 TALER_MERCHANT_post_private_fountains_cancel ( 245 struct TALER_MERCHANT_PostPrivateFountainsHandle *ppfh) 246 { 247 if (NULL != ppfh->job) 248 { 249 GNUNET_CURL_job_cancel (ppfh->job); 250 ppfh->job = NULL; 251 } 252 TALER_curl_easy_post_finished (&ppfh->post_ctx); 253 json_decref (ppfh->req_obj); 254 GNUNET_free (ppfh->url); 255 GNUNET_free (ppfh->base_url); 256 GNUNET_free (ppfh); 257 } 258 259 260 /* end of merchant_api_post-private-fountains.c */