Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2018, 2020 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing_api_cmd_abort_order.c
21 : * @brief command to test the abort feature.
22 : * @author Marcello Stanisci
23 : */
24 : #include "platform.h"
25 : #include <taler/taler_exchange_service.h>
26 : #include <taler/taler_testing_lib.h>
27 : #include <taler/taler_signatures.h>
28 : #include "taler_merchant_service.h"
29 : #include "taler_merchant_testing_lib.h"
30 :
31 : /**
32 : * State for a " abort" CMD.
33 : */
34 : struct AbortState
35 : {
36 :
37 : /**
38 : * Reference to the "pay" command to abort.
39 : */
40 : const char *pay_reference;
41 :
42 : /**
43 : * Merchant URL.
44 : */
45 : const char *merchant_url;
46 :
47 : /**
48 : * Handle to a "abort" operation.
49 : */
50 : struct TALER_MERCHANT_OrderAbortHandle *oah;
51 :
52 : /**
53 : * Interpreter state.
54 : */
55 : struct TALER_TESTING_Interpreter *is;
56 :
57 : /**
58 : * The actual abort/refund data.
59 : */
60 : struct TALER_MERCHANT_AbortedCoin *acs;
61 :
62 : /**
63 : * Expected HTTP response code.
64 : */
65 : unsigned int http_status;
66 :
67 : /**
68 : * How many refund permissions this CMD got
69 : * the right for. Roughly, there is one refund
70 : * permission for one coin.
71 : */
72 : unsigned int acs_length;
73 :
74 : };
75 :
76 :
77 : /**
78 : * Parse the @a coins specification and grow the @a ac
79 : * array with the coins found, updating @a nac.
80 : *
81 : * @param[in,out] ac pointer to array of coins found
82 : * @param[in,out] nac length of array at @a pc
83 : * @param[in] coins string specifying coins to add to @a pc,
84 : * clobbered in the process
85 : * @param is interpreter state
86 : * @return #GNUNET_OK on success
87 : */
88 : static enum GNUNET_GenericReturnValue
89 0 : build_coins (struct TALER_MERCHANT_AbortCoin **ac,
90 : unsigned int *nac,
91 : char *coins,
92 : struct TALER_TESTING_Interpreter *is)
93 : {
94 0 : for (char *token = strtok (coins, ";");
95 : NULL != token;
96 0 : token = strtok (NULL, ";"))
97 : {
98 : char *ctok;
99 : unsigned int ci;
100 : struct TALER_MERCHANT_AbortCoin *icoin;
101 :
102 : /* Token syntax is "LABEL[/NUMBER]" */
103 0 : ctok = strchr (token, '/');
104 0 : ci = 0;
105 0 : if (NULL != ctok)
106 : {
107 0 : *ctok = '\0';
108 0 : ctok++;
109 0 : if (1 != sscanf (ctok,
110 : "%u",
111 : &ci))
112 : {
113 0 : GNUNET_break (0);
114 0 : return GNUNET_SYSERR;
115 : }
116 : }
117 : {
118 : const struct TALER_TESTING_Command *coin_cmd;
119 : const char **exchange_url;
120 :
121 0 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
122 : token);
123 0 : if (NULL == coin_cmd)
124 : {
125 0 : GNUNET_break (0);
126 0 : return GNUNET_SYSERR;
127 : }
128 0 : GNUNET_array_grow (*ac,
129 : *nac,
130 : (*nac) + 1);
131 0 : icoin = &((*ac)[(*nac) - 1]);
132 :
133 : {
134 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
135 :
136 0 : GNUNET_assert (GNUNET_OK ==
137 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
138 : ci,
139 : &coin_priv));
140 0 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
141 : &icoin->coin_pub.eddsa_pub);
142 : }
143 0 : GNUNET_assert (GNUNET_OK ==
144 : TALER_TESTING_get_trait_exchange_url (coin_cmd,
145 : &exchange_url));
146 0 : icoin->exchange_url = *exchange_url;
147 : {
148 : const struct TALER_Amount *denom_value;
149 :
150 0 : GNUNET_assert (GNUNET_OK ==
151 : TALER_TESTING_get_trait_amount (coin_cmd,
152 : &denom_value));
153 0 : icoin->amount_with_fee = *denom_value;
154 : }
155 :
156 : }
157 : }
158 0 : return GNUNET_OK;
159 : }
160 :
161 :
162 : /**
163 : * Callback for a "pay abort" operation. Mainly, check HTTP
164 : * response code was as expected and stores refund permissions
165 : * in the state.
166 : *
167 : * @param cls closure.
168 : * @param hr HTTP response
169 : * @param merchant_pub public key of the merchant refunding the
170 : * contract.
171 : * @param num_aborts length of the @a res array
172 : * @param res array containing the abort confirmations
173 : */
174 : static void
175 0 : abort_cb (void *cls,
176 : const struct TALER_MERCHANT_HttpResponse *hr,
177 : const struct TALER_MerchantPublicKeyP *merchant_pub,
178 : unsigned int num_aborts,
179 : const struct TALER_MERCHANT_AbortedCoin res[])
180 : {
181 0 : struct AbortState *as = cls;
182 :
183 0 : as->oah = NULL;
184 0 : if (as->http_status != hr->http_status)
185 : {
186 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187 : "Unexpected response code %u (%d) to command `%s' (expected %u)\n",
188 : hr->http_status,
189 : (int) hr->ec,
190 : TALER_TESTING_interpreter_get_current_label (as->is),
191 : as->http_status);
192 0 : TALER_TESTING_FAIL (as->is);
193 : }
194 0 : if ( (MHD_HTTP_OK == hr->http_status) &&
195 0 : (TALER_EC_NONE == hr->ec) )
196 : {
197 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 : "Received %u refunds\n",
199 : num_aborts);
200 0 : as->acs_length = num_aborts;
201 0 : as->acs = GNUNET_new_array (num_aborts,
202 : struct TALER_MERCHANT_AbortedCoin);
203 0 : memcpy (as->acs,
204 : res,
205 : num_aborts * sizeof (struct TALER_MERCHANT_AbortedCoin));
206 : }
207 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
208 : "Successful pay-abort (HTTP status: %u)\n",
209 : hr->http_status);
210 0 : TALER_TESTING_interpreter_next (as->is);
211 : }
212 :
213 :
214 : /**
215 : * Run an "abort" CMD.
216 : *
217 : * @param cls closure
218 : * @param cmd command being run.
219 : * @param is interpreter state
220 : */
221 : static void
222 0 : abort_run (void *cls,
223 : const struct TALER_TESTING_Command *cmd,
224 : struct TALER_TESTING_Interpreter *is)
225 : {
226 0 : struct AbortState *as = cls;
227 : const struct TALER_TESTING_Command *pay_cmd;
228 : const char **proposal_reference;
229 : const char **coin_reference;
230 : const struct TALER_TESTING_Command *proposal_cmd;
231 : const char *order_id;
232 : const struct TALER_PrivateContractHashP *h_proposal;
233 : struct TALER_MerchantPublicKeyP merchant_pub;
234 : struct TALER_Amount total_amount;
235 : const char *error_name;
236 : unsigned int error_line;
237 : struct TALER_MERCHANT_AbortCoin *abort_coins;
238 : unsigned int nabort_coins;
239 : char *cr;
240 :
241 0 : as->is = is;
242 0 : pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
243 : as->pay_reference);
244 0 : if (NULL == pay_cmd)
245 0 : TALER_TESTING_FAIL (is);
246 0 : if (GNUNET_OK !=
247 0 : TALER_TESTING_get_trait_proposal_reference (pay_cmd,
248 : &proposal_reference))
249 0 : TALER_TESTING_FAIL (is);
250 0 : if (GNUNET_OK !=
251 0 : TALER_TESTING_get_trait_coin_reference (pay_cmd,
252 : 0,
253 : &coin_reference))
254 0 : TALER_TESTING_FAIL (is);
255 0 : proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
256 : *proposal_reference);
257 :
258 0 : if (NULL == proposal_cmd)
259 0 : TALER_TESTING_FAIL (is);
260 :
261 : {
262 : const json_t *contract_terms;
263 :
264 0 : if (GNUNET_OK !=
265 0 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
266 : &contract_terms))
267 0 : TALER_TESTING_FAIL (is);
268 : {
269 : /* Get information that needs to be put verbatim in the
270 : * deposit permission */
271 : struct GNUNET_JSON_Specification spec[] = {
272 0 : GNUNET_JSON_spec_string ("order_id",
273 : &order_id),
274 0 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
275 : &merchant_pub),
276 0 : TALER_JSON_spec_amount_any ("amount",
277 : &total_amount),
278 0 : GNUNET_JSON_spec_end ()
279 : };
280 :
281 0 : if (GNUNET_OK !=
282 0 : GNUNET_JSON_parse (contract_terms,
283 : spec,
284 : &error_name,
285 : &error_line))
286 : {
287 : char *js;
288 :
289 0 : js = json_dumps (contract_terms,
290 : JSON_INDENT (1));
291 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
292 : "Parser failed on %s:%u for input `%s'\n",
293 : error_name,
294 : error_line,
295 : js);
296 0 : free (js);
297 0 : TALER_TESTING_FAIL (is);
298 : }
299 : }
300 : }
301 :
302 0 : cr = GNUNET_strdup (*coin_reference);
303 0 : abort_coins = NULL;
304 0 : nabort_coins = 0;
305 0 : if (GNUNET_OK !=
306 0 : build_coins (&abort_coins,
307 : &nabort_coins,
308 : cr,
309 : is))
310 : {
311 0 : GNUNET_array_grow (abort_coins,
312 : nabort_coins,
313 : 0);
314 0 : GNUNET_free (cr);
315 0 : TALER_TESTING_FAIL (is);
316 : }
317 0 : GNUNET_free (cr);
318 :
319 0 : if (GNUNET_OK !=
320 0 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
321 : &h_proposal))
322 0 : TALER_TESTING_FAIL (is);
323 0 : as->oah = TALER_MERCHANT_order_abort (is->ctx,
324 : as->merchant_url,
325 : order_id,
326 : &merchant_pub,
327 : h_proposal,
328 : nabort_coins,
329 : abort_coins,
330 : &abort_cb,
331 : as);
332 0 : GNUNET_array_grow (abort_coins,
333 : nabort_coins,
334 : 0);
335 0 : if (NULL == as->oah)
336 0 : TALER_TESTING_FAIL (is);
337 : }
338 :
339 :
340 : /**
341 : * Free a "pay abort" CMD, and cancel it if need be.
342 : *
343 : * @param cls closure.
344 : * @param cmd command currently being freed.
345 : */
346 : static void
347 0 : abort_cleanup (void *cls,
348 : const struct TALER_TESTING_Command *cmd)
349 : {
350 0 : struct AbortState *as = cls;
351 :
352 0 : if (NULL != as->oah)
353 : {
354 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
355 : "Command `%s' did not complete.\n",
356 : TALER_TESTING_interpreter_get_current_label (
357 : as->is));
358 0 : TALER_MERCHANT_order_abort_cancel (as->oah);
359 : }
360 0 : GNUNET_array_grow (as->acs,
361 : as->acs_length,
362 : 0);
363 0 : GNUNET_free (as);
364 0 : }
365 :
366 :
367 : /**
368 : * Offer internal data useful to other commands.
369 : *
370 : * @param cls closure
371 : * @param[out] ret result (could be anything)
372 : * @param trait name of the trait
373 : * @param index index number of the object to extract.
374 : * @return #GNUNET_OK on success
375 : */
376 : static int
377 0 : abort_traits (void *cls,
378 : const void **ret,
379 : const char *trait,
380 : unsigned int index)
381 : {
382 0 : struct AbortState *as = cls;
383 : struct TALER_TESTING_Trait traits[] = {
384 0 : TALER_TESTING_trait_end ()
385 : };
386 :
387 : (void) as;
388 0 : return TALER_TESTING_get_trait (traits,
389 : ret,
390 : trait,
391 : index);
392 : }
393 :
394 :
395 : struct TALER_TESTING_Command
396 0 : TALER_TESTING_cmd_merchant_order_abort (const char *label,
397 : const char *merchant_url,
398 : const char *pay_reference,
399 : unsigned int http_status)
400 : {
401 : struct AbortState *as;
402 :
403 0 : as = GNUNET_new (struct AbortState);
404 0 : as->http_status = http_status;
405 0 : as->pay_reference = pay_reference;
406 0 : as->merchant_url = merchant_url;
407 : {
408 0 : struct TALER_TESTING_Command cmd = {
409 : .cls = as,
410 : .label = label,
411 : .run = &abort_run,
412 : .cleanup = &abort_cleanup,
413 : .traits = &abort_traits
414 : };
415 :
416 0 : return cmd;
417 : }
418 : }
419 :
420 :
421 : /* end of testing_api_cmd_abort_order.c */
|