merchant_api_get_instances.c (7468B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 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 Software 7 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, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file merchant_api_get_instances.c 19 * @brief Implementation of the GET /instances request of the merchant's HTTP API 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <curl/curl.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "taler_merchant_service.h" 29 #include "merchant_api_curl_defaults.h" 30 #include <taler/taler_json_lib.h> 31 #include <taler/taler_signatures.h> 32 33 34 /** 35 * Maximum number of instances permitted. 36 */ 37 #define MAX_INSTANCES 1024 38 39 /** 40 * Handle for a GET /instances operation. 41 */ 42 struct TALER_MERCHANT_InstancesGetHandle 43 { 44 /** 45 * The url for this request. 46 */ 47 char *url; 48 49 /** 50 * Handle for the request. 51 */ 52 struct GNUNET_CURL_Job *job; 53 54 /** 55 * Function to call with the result. 56 */ 57 TALER_MERCHANT_InstancesGetCallback cb; 58 59 /** 60 * Closure for @a cb. 61 */ 62 void *cb_cls; 63 64 /** 65 * Reference to the execution context. 66 */ 67 struct GNUNET_CURL_Context *ctx; 68 69 }; 70 71 72 /** 73 * Parse instance information from @a ia. 74 * 75 * @param json overall reply body 76 * @param ia JSON array (or NULL!) with instance data 77 * @param igh operation handle 78 * @return #GNUNET_OK on success 79 */ 80 static enum GNUNET_GenericReturnValue 81 parse_instances (const json_t *json, 82 const json_t *ia, 83 struct TALER_MERCHANT_InstancesGetHandle *igh) 84 { 85 unsigned int iis_len = (unsigned int) json_array_size (ia); 86 87 if ( (json_array_size (ia) != (size_t) iis_len) || 88 (iis_len > MAX_INSTANCES) ) 89 { 90 GNUNET_break (0); 91 return GNUNET_SYSERR; 92 } 93 { 94 struct TALER_MERCHANT_InstanceInformation iis[GNUNET_NZL (iis_len)]; 95 size_t index; 96 json_t *value; 97 struct TALER_MERCHANT_InstancesGetResponse igr = { 98 .hr.http_status = MHD_HTTP_OK, 99 .hr.reply = json, 100 .details.ok.iis_length = iis_len, 101 .details.ok.iis = iis 102 }; 103 104 json_array_foreach (ia, index, value) { 105 struct TALER_MERCHANT_InstanceInformation *ii = &iis[index]; 106 struct GNUNET_JSON_Specification spec[] = { 107 GNUNET_JSON_spec_string ("name", 108 &ii->name), 109 GNUNET_JSON_spec_string ("id", 110 &ii->id), 111 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 112 &ii->merchant_pub), 113 GNUNET_JSON_spec_array_const ("payment_targets", 114 &ii->payment_targets), 115 GNUNET_JSON_spec_end () 116 }; 117 118 if (GNUNET_OK != 119 GNUNET_JSON_parse (value, 120 spec, 121 NULL, NULL)) 122 { 123 GNUNET_break_op (0); 124 return GNUNET_SYSERR; 125 } 126 for (size_t i = 0; i<json_array_size (ii->payment_targets); i++) 127 { 128 if (! json_is_string (json_array_get (ii->payment_targets, 129 i))) 130 { 131 GNUNET_break_op (0); 132 return GNUNET_SYSERR; 133 } 134 } 135 } /* for all instances */ 136 igh->cb (igh->cb_cls, 137 &igr); 138 igh->cb = NULL; /* just to be sure */ 139 } 140 return GNUNET_OK; 141 } 142 143 144 /** 145 * Function called when we're done processing the 146 * HTTP /instances request. 147 * 148 * @param cls the `struct TALER_MERCHANT_InstancesGetHandle` 149 * @param response_code HTTP response code, 0 on error 150 * @param response response body, NULL if not in JSON 151 */ 152 static void 153 handle_instances_finished (void *cls, 154 long response_code, 155 const void *response) 156 { 157 struct TALER_MERCHANT_InstancesGetHandle *igh = cls; 158 const json_t *json = response; 159 struct TALER_MERCHANT_InstancesGetResponse igr = { 160 .hr.http_status = (unsigned int) response_code, 161 .hr.reply = json 162 }; 163 164 igh->job = NULL; 165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 166 "Got /instances response with status code %u\n", 167 (unsigned int) response_code); 168 switch (response_code) 169 { 170 case MHD_HTTP_OK: 171 { 172 const json_t *instances; 173 struct GNUNET_JSON_Specification spec[] = { 174 GNUNET_JSON_spec_array_const ("instances", 175 &instances), 176 GNUNET_JSON_spec_end () 177 }; 178 179 if (GNUNET_OK != 180 GNUNET_JSON_parse (json, 181 spec, 182 NULL, NULL)) 183 { 184 igr.hr.http_status = 0; 185 igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 186 break; 187 } 188 if (GNUNET_OK == 189 parse_instances (json, 190 instances, 191 igh)) 192 { 193 TALER_MERCHANT_instances_get_cancel (igh); 194 return; 195 } 196 igr.hr.http_status = 0; 197 igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 198 break; 199 } 200 case MHD_HTTP_UNAUTHORIZED: 201 igr.hr.ec = TALER_JSON_get_error_code (json); 202 igr.hr.hint = TALER_JSON_get_error_hint (json); 203 break; 204 default: 205 /* unexpected response code */ 206 igr.hr.ec = TALER_JSON_get_error_code (json); 207 igr.hr.hint = TALER_JSON_get_error_hint (json); 208 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 209 "Unexpected response code %u/%d\n", 210 (unsigned int) response_code, 211 (int) igr.hr.ec); 212 break; 213 } 214 igh->cb (igh->cb_cls, 215 &igr); 216 TALER_MERCHANT_instances_get_cancel (igh); 217 } 218 219 220 struct TALER_MERCHANT_InstancesGetHandle * 221 TALER_MERCHANT_instances_get (struct GNUNET_CURL_Context *ctx, 222 const char *backend_url, 223 TALER_MERCHANT_InstancesGetCallback instances_cb, 224 void *instances_cb_cls) 225 { 226 struct TALER_MERCHANT_InstancesGetHandle *igh; 227 CURL *eh; 228 229 igh = GNUNET_new (struct TALER_MERCHANT_InstancesGetHandle); 230 igh->ctx = ctx; 231 igh->cb = instances_cb; 232 igh->cb_cls = instances_cb_cls; 233 igh->url = TALER_url_join (backend_url, 234 "management/instances", 235 NULL); 236 if (NULL == igh->url) 237 { 238 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 239 "Could not construct request URL.\n"); 240 GNUNET_free (igh); 241 return NULL; 242 } 243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 244 "Requesting URL '%s'\n", 245 igh->url); 246 eh = TALER_MERCHANT_curl_easy_get_ (igh->url); 247 igh->job = GNUNET_CURL_job_add (ctx, 248 eh, 249 &handle_instances_finished, 250 igh); 251 return igh; 252 } 253 254 255 void 256 TALER_MERCHANT_instances_get_cancel ( 257 struct TALER_MERCHANT_InstancesGetHandle *igh) 258 { 259 if (NULL != igh->job) 260 GNUNET_CURL_job_cancel (igh->job); 261 GNUNET_free (igh->url); 262 GNUNET_free (igh); 263 }