merchant_api_get_accounts.c (6810B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022 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_accounts.c 19 * @brief Implementation of the GET /accounts 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 * Maximum number of accounts permitted. 35 */ 36 #define MAX_ACCOUNTS 1024 37 38 /** 39 * Handle for a GET /accounts operation. 40 */ 41 struct TALER_MERCHANT_AccountsGetHandle 42 { 43 /** 44 * The url for this request. 45 */ 46 char *url; 47 48 /** 49 * Handle for the request. 50 */ 51 struct GNUNET_CURL_Job *job; 52 53 /** 54 * Function to call with the result. 55 */ 56 TALER_MERCHANT_AccountsGetCallback cb; 57 58 /** 59 * Closure for @a cb. 60 */ 61 void *cb_cls; 62 63 /** 64 * Reference to the execution context. 65 */ 66 struct GNUNET_CURL_Context *ctx; 67 68 }; 69 70 71 /** 72 * Parse account information from @a ia. 73 * 74 * @param ia JSON array (or NULL!) with account data 75 * @param[in] tgr partially filled response 76 * @param tgh operation handle 77 * @return #GNUNET_OK on success 78 */ 79 static enum GNUNET_GenericReturnValue 80 parse_accounts (const json_t *ia, 81 struct TALER_MERCHANT_AccountsGetResponse *tgr, 82 struct TALER_MERCHANT_AccountsGetHandle *tgh) 83 { 84 unsigned int tmpl_len = (unsigned int) json_array_size (ia); 85 86 if ( (json_array_size (ia) != (size_t) tmpl_len) || 87 (tmpl_len > MAX_ACCOUNTS) ) 88 { 89 GNUNET_break (0); 90 return GNUNET_SYSERR; 91 } 92 { 93 struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)]; 94 size_t index; 95 json_t *value; 96 97 json_array_foreach (ia, index, value) { 98 struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index]; 99 struct GNUNET_JSON_Specification spec[] = { 100 TALER_JSON_spec_full_payto_uri ("payto_uri", 101 &ie->payto_uri), 102 GNUNET_JSON_spec_fixed_auto ("h_wire", 103 &ie->h_wire), 104 GNUNET_JSON_spec_end () 105 }; 106 107 if (GNUNET_OK != 108 GNUNET_JSON_parse (value, 109 spec, 110 NULL, NULL)) 111 { 112 GNUNET_break_op (0); 113 return GNUNET_SYSERR; 114 } 115 } 116 tgr->details.ok.accounts_length = tmpl_len; 117 tgr->details.ok.accounts = tmpl; 118 tgh->cb (tgh->cb_cls, 119 tgr); 120 tgh->cb = NULL; /* just to be sure */ 121 } 122 return GNUNET_OK; 123 } 124 125 126 /** 127 * Function called when we're done processing the 128 * HTTP /accounts request. 129 * 130 * @param cls the `struct TALER_MERCHANT_AccountsGetHandle` 131 * @param response_code HTTP response code, 0 on error 132 * @param response response body, NULL if not in JSON 133 */ 134 static void 135 handle_get_accounts_finished (void *cls, 136 long response_code, 137 const void *response) 138 { 139 struct TALER_MERCHANT_AccountsGetHandle *tgh = cls; 140 const json_t *json = response; 141 struct TALER_MERCHANT_AccountsGetResponse tgr = { 142 .hr.http_status = (unsigned int) response_code, 143 .hr.reply = json 144 }; 145 146 tgh->job = NULL; 147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 148 "Got /accounts response with status code %u\n", 149 (unsigned int) response_code); 150 switch (response_code) 151 { 152 case MHD_HTTP_OK: 153 { 154 const json_t *accounts; 155 struct GNUNET_JSON_Specification spec[] = { 156 GNUNET_JSON_spec_array_const ("accounts", 157 &accounts), 158 GNUNET_JSON_spec_end () 159 }; 160 161 if (GNUNET_OK != 162 GNUNET_JSON_parse (json, 163 spec, 164 NULL, NULL)) 165 { 166 tgr.hr.http_status = 0; 167 tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 168 break; 169 } 170 if (GNUNET_OK == 171 parse_accounts (accounts, 172 &tgr, 173 tgh)) 174 { 175 TALER_MERCHANT_accounts_get_cancel (tgh); 176 return; 177 } 178 tgr.hr.http_status = 0; 179 tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 180 break; 181 } 182 case MHD_HTTP_UNAUTHORIZED: 183 tgr.hr.ec = TALER_JSON_get_error_code (json); 184 tgr.hr.hint = TALER_JSON_get_error_hint (json); 185 /* Nothing really to verify, merchant says we need to authenticate. */ 186 break; 187 default: 188 /* unexpected response code */ 189 tgr.hr.ec = TALER_JSON_get_error_code (json); 190 tgr.hr.hint = TALER_JSON_get_error_hint (json); 191 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 192 "Unexpected response code %u/%d\n", 193 (unsigned int) response_code, 194 (int) tgr.hr.ec); 195 break; 196 } 197 tgh->cb (tgh->cb_cls, 198 &tgr); 199 TALER_MERCHANT_accounts_get_cancel (tgh); 200 } 201 202 203 struct TALER_MERCHANT_AccountsGetHandle * 204 TALER_MERCHANT_accounts_get ( 205 struct GNUNET_CURL_Context *ctx, 206 const char *backend_url, 207 TALER_MERCHANT_AccountsGetCallback cb, 208 void *cb_cls) 209 { 210 struct TALER_MERCHANT_AccountsGetHandle *tgh; 211 CURL *eh; 212 213 tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle); 214 tgh->ctx = ctx; 215 tgh->cb = cb; 216 tgh->cb_cls = cb_cls; 217 tgh->url = TALER_url_join (backend_url, 218 "private/accounts", 219 NULL); 220 if (NULL == tgh->url) 221 { 222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 223 "Could not construct request URL.\n"); 224 GNUNET_free (tgh); 225 return NULL; 226 } 227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 228 "Requesting URL '%s'\n", 229 tgh->url); 230 eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); 231 tgh->job = GNUNET_CURL_job_add (ctx, 232 eh, 233 &handle_get_accounts_finished, 234 tgh); 235 return tgh; 236 } 237 238 239 void 240 TALER_MERCHANT_accounts_get_cancel ( 241 struct TALER_MERCHANT_AccountsGetHandle *tgh) 242 { 243 if (NULL != tgh->job) 244 GNUNET_CURL_job_cancel (tgh->job); 245 GNUNET_free (tgh->url); 246 GNUNET_free (tgh); 247 }