Line data Source code
1 : /*
2 : This file is part of SYNC
3 : Copyright (C) 2014-2019 Taler Systems SA
4 :
5 : SYNC 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 : SYNC 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 SYNC; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file lib/testing_api_cmd_backup_upload.c
21 : * @brief command to upload data to the sync backend service.
22 : * @author Christian Grothoff
23 : */
24 : #include "platform.h"
25 : #include "sync_service.h"
26 : #include "sync_testing_lib.h"
27 : #include <taler/taler_util.h>
28 : #include <taler/taler_testing_lib.h>
29 : #include <taler/taler_merchant_service.h>
30 : #include "sync_testing_lib.h"
31 :
32 : /**
33 : * State for a "backup upload" CMD.
34 : */
35 : struct BackupUploadState
36 : {
37 :
38 : /**
39 : * Eddsa private key.
40 : */
41 : struct SYNC_AccountPrivateKeyP sync_priv;
42 :
43 : /**
44 : * Eddsa public key.
45 : */
46 : struct SYNC_AccountPublicKeyP sync_pub;
47 :
48 : /**
49 : * Hash of the previous upload (maybe bogus if
50 : * #SYNC_TESTING_UO_PREV_HASH_WRONG is set in @e uo).
51 : * Maybe all zeros if there was no previous upload.
52 : */
53 : struct GNUNET_HashCode prev_hash;
54 :
55 : /**
56 : * Hash of the current upload.
57 : */
58 : struct GNUNET_HashCode curr_hash;
59 :
60 : /**
61 : * The /backups POST operation handle.
62 : */
63 : struct SYNC_UploadOperation *uo;
64 :
65 : /**
66 : * URL of the sync backend.
67 : */
68 : const char *sync_url;
69 :
70 : /**
71 : * Previous upload, or NULL for none. Used to calculate what THIS
72 : * upload is based on.
73 : */
74 : const char *prev_upload;
75 :
76 : /**
77 : * Last upload, or NULL for none, usually same as @e prev_upload.
78 : * Used to check the response on #MHD_HTTP_CONFLICT.
79 : */
80 : const char *last_upload;
81 :
82 : /**
83 : * Payment order ID we got back, if any. Otherwise NULL.
84 : */
85 : char *payment_order_id;
86 :
87 : /**
88 : * Claim token we got back, if any. Otherwise all zeros.
89 : */
90 : struct TALER_ClaimTokenP token;
91 :
92 : /**
93 : * Payment order ID we are to provide in the request, may be NULL.
94 : */
95 : const char *payment_order_req;
96 :
97 : /**
98 : * The interpreter state.
99 : */
100 : struct TALER_TESTING_Interpreter *is;
101 :
102 : /**
103 : * The backup data we are uploading.
104 : */
105 : const void *backup;
106 :
107 : /**
108 : * Number of bytes in @e backup.
109 : */
110 : size_t backup_size;
111 :
112 : /**
113 : * Expected status code.
114 : */
115 : unsigned int http_status;
116 :
117 : /**
118 : * Options for how we are supposed to do the upload.
119 : */
120 : enum SYNC_TESTING_UploadOption uopt;
121 :
122 : };
123 :
124 :
125 : /**
126 : * Function called with the results of a #SYNC_upload().
127 : *
128 : * @param cls closure
129 : * @param ec Taler error code
130 : * @param http_status HTTP status of the request
131 : * @param ud details about the upload operation
132 : */
133 : static void
134 5 : backup_upload_cb (void *cls,
135 : enum TALER_ErrorCode ec,
136 : unsigned int http_status,
137 : const struct SYNC_UploadDetails *ud)
138 : {
139 5 : struct BackupUploadState *bus = cls;
140 :
141 5 : bus->uo = NULL;
142 5 : if (http_status != bus->http_status)
143 : {
144 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
145 : "Unexpected response code %u to command %s in %s:%u\n",
146 : http_status,
147 : bus->is->commands[bus->is->ip].label,
148 : __FILE__,
149 : __LINE__);
150 0 : TALER_TESTING_interpreter_fail (bus->is);
151 0 : return;
152 : }
153 5 : if (NULL != ud)
154 : {
155 5 : switch (ud->us)
156 : {
157 2 : case SYNC_US_SUCCESS:
158 2 : if (0 != GNUNET_memcmp (&bus->curr_hash,
159 : ud->details.curr_backup_hash))
160 : {
161 0 : GNUNET_break (0);
162 0 : TALER_TESTING_interpreter_fail (bus->is);
163 0 : return;
164 : }
165 2 : break;
166 2 : case SYNC_US_PAYMENT_REQUIRED:
167 : {
168 : struct TALER_MERCHANT_PayUriData pd;
169 :
170 2 : if (GNUNET_OK !=
171 2 : TALER_MERCHANT_parse_pay_uri (ud->details.payment_request,
172 : &pd))
173 : {
174 0 : GNUNET_break (0);
175 0 : TALER_TESTING_interpreter_fail (bus->is);
176 0 : return;
177 : }
178 2 : bus->payment_order_id = GNUNET_strdup (pd.order_id);
179 2 : if (NULL != pd.claim_token)
180 0 : bus->token = *pd.claim_token;
181 2 : TALER_MERCHANT_parse_pay_uri_free (&pd);
182 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
183 : "Order ID from Sync service is `%s'\n",
184 : bus->payment_order_id);
185 2 : memset (&bus->curr_hash,
186 : 0,
187 : sizeof (struct GNUNET_HashCode));
188 : }
189 2 : break;
190 1 : case SYNC_US_CONFLICTING_BACKUP:
191 : {
192 : const struct TALER_TESTING_Command *ref;
193 : const struct GNUNET_HashCode *h;
194 :
195 1 : ref = TALER_TESTING_interpreter_lookup_command
196 : (bus->is,
197 : bus->last_upload);
198 1 : GNUNET_assert (NULL != ref);
199 1 : GNUNET_assert (GNUNET_OK ==
200 : SYNC_TESTING_get_trait_hash (ref,
201 : SYNC_TESTING_TRAIT_HASH_CURRENT,
202 : &h));
203 1 : if (0 != GNUNET_memcmp (h,
204 : &ud->details.recovered_backup.
205 : existing_backup_hash))
206 : {
207 0 : GNUNET_break (0);
208 0 : TALER_TESTING_interpreter_fail (bus->is);
209 0 : return;
210 : }
211 : }
212 : case SYNC_US_HTTP_ERROR:
213 1 : break;
214 0 : case SYNC_US_CLIENT_ERROR:
215 0 : GNUNET_break (0);
216 0 : TALER_TESTING_interpreter_fail (bus->is);
217 0 : return;
218 0 : case SYNC_US_SERVER_ERROR:
219 0 : GNUNET_break (0);
220 0 : TALER_TESTING_interpreter_fail (bus->is);
221 0 : return;
222 : }
223 0 : }
224 5 : TALER_TESTING_interpreter_next (bus->is);
225 : }
226 :
227 :
228 : /**
229 : * Run a "backup upload" CMD.
230 : *
231 : * @param cls closure.
232 : * @param cmd command currently being run.
233 : * @param is interpreter state.
234 : */
235 : static void
236 5 : backup_upload_run (void *cls,
237 : const struct TALER_TESTING_Command *cmd,
238 : struct TALER_TESTING_Interpreter *is)
239 : {
240 5 : struct BackupUploadState *bus = cls;
241 :
242 5 : bus->is = is;
243 5 : if (NULL != bus->prev_upload)
244 : {
245 : const struct TALER_TESTING_Command *ref;
246 :
247 4 : ref = TALER_TESTING_interpreter_lookup_command (
248 : is,
249 : bus->prev_upload);
250 4 : if (NULL == ref)
251 : {
252 0 : GNUNET_break (0);
253 0 : TALER_TESTING_interpreter_fail (bus->is);
254 0 : return;
255 : }
256 : {
257 : const struct GNUNET_HashCode *h;
258 :
259 4 : if (GNUNET_OK ==
260 4 : SYNC_TESTING_get_trait_hash (ref,
261 : SYNC_TESTING_TRAIT_HASH_CURRENT,
262 : &h))
263 : {
264 4 : bus->prev_hash = *h;
265 : }
266 : }
267 : {
268 : const struct SYNC_AccountPrivateKeyP *priv;
269 :
270 4 : if (GNUNET_OK !=
271 4 : SYNC_TESTING_get_trait_account_priv (ref,
272 : 0,
273 : &priv))
274 : {
275 0 : GNUNET_break (0);
276 0 : TALER_TESTING_interpreter_fail (bus->is);
277 0 : return;
278 : }
279 4 : bus->sync_priv = *priv;
280 : }
281 : {
282 : const struct SYNC_AccountPublicKeyP *pub;
283 :
284 4 : if (GNUNET_OK !=
285 4 : SYNC_TESTING_get_trait_account_pub (ref,
286 : 0,
287 : &pub))
288 : {
289 0 : GNUNET_break (0);
290 0 : TALER_TESTING_interpreter_fail (bus->is);
291 0 : return;
292 : }
293 4 : bus->sync_pub = *pub;
294 : }
295 4 : if (0 != (SYNC_TESTING_UO_REFERENCE_ORDER_ID & bus->uopt))
296 : {
297 : const char *order_id;
298 :
299 0 : if (GNUNET_OK !=
300 0 : TALER_TESTING_get_trait_order_id (ref,
301 : 0,
302 : &order_id))
303 : {
304 0 : GNUNET_break (0);
305 0 : TALER_TESTING_interpreter_fail (bus->is);
306 0 : return;
307 : }
308 0 : bus->payment_order_req = order_id;
309 0 : if (NULL == bus->payment_order_req)
310 : {
311 0 : GNUNET_break (0);
312 0 : TALER_TESTING_interpreter_fail (bus->is);
313 0 : return;
314 : }
315 : }
316 : }
317 : else
318 : {
319 1 : GNUNET_CRYPTO_eddsa_key_create (&bus->sync_priv.eddsa_priv);
320 1 : GNUNET_CRYPTO_eddsa_key_get_public (&bus->sync_priv.eddsa_priv,
321 : &bus->sync_pub.eddsa_pub);
322 : }
323 5 : if (0 != (SYNC_TESTING_UO_PREV_HASH_WRONG & bus->uopt))
324 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
325 0 : &bus->prev_hash,
326 : sizeof (struct GNUNET_HashCode));
327 5 : GNUNET_CRYPTO_hash (bus->backup,
328 : bus->backup_size,
329 : &bus->curr_hash);
330 5 : bus->uo = SYNC_upload (is->ctx,
331 : bus->sync_url,
332 : &bus->sync_priv,
333 4 : ( ( (NULL != bus->prev_upload) &&
334 4 : (GNUNET_NO == GNUNET_is_zero (
335 2 : &bus->prev_hash)) ) ||
336 : (0 != (SYNC_TESTING_UO_PREV_HASH_WRONG
337 2 : & bus->uopt)) )
338 : ? &bus->prev_hash
339 : : NULL,
340 : bus->backup_size,
341 : bus->backup,
342 5 : (0 != (SYNC_TESTING_UO_REQUEST_PAYMENT & bus->uopt))
343 : ? SYNC_PO_FORCE_PAYMENT
344 5 : : SYNC_PO_NONE,
345 : bus->payment_order_req,
346 : &backup_upload_cb,
347 : bus);
348 5 : if (NULL == bus->uo)
349 : {
350 0 : GNUNET_break (0);
351 0 : TALER_TESTING_interpreter_fail (bus->is);
352 0 : return;
353 : }
354 : }
355 :
356 :
357 : /**
358 : * Free the state of a "backup upload" CMD, and possibly
359 : * cancel it if it did not complete.
360 : *
361 : * @param cls closure.
362 : * @param cmd command being freed.
363 : */
364 : static void
365 5 : backup_upload_cleanup (void *cls,
366 : const struct TALER_TESTING_Command *cmd)
367 : {
368 5 : struct BackupUploadState *bus = cls;
369 :
370 5 : if (NULL != bus->uo)
371 : {
372 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
373 : "Command '%s' did not complete (backup upload)\n",
374 : cmd->label);
375 0 : SYNC_upload_cancel (bus->uo);
376 0 : bus->uo = NULL;
377 : }
378 5 : GNUNET_free (bus->payment_order_id);
379 5 : GNUNET_free (bus);
380 5 : }
381 :
382 :
383 : /**
384 : * Offer internal data to other commands.
385 : *
386 : * @param cls closure
387 : * @param ret[out] result (could be anything)
388 : * @param trait name of the trait
389 : * @param index index number of the object to extract.
390 : * @return #GNUNET_OK on success
391 : */
392 : static int
393 22 : backup_upload_traits (void *cls,
394 : const void **ret,
395 : const char *trait,
396 : unsigned int index)
397 : {
398 22 : struct BackupUploadState *bus = cls;
399 : struct TALER_TESTING_Trait straits[] = {
400 22 : SYNC_TESTING_make_trait_hash (SYNC_TESTING_TRAIT_HASH_CURRENT,
401 22 : &bus->curr_hash),
402 22 : SYNC_TESTING_make_trait_hash (SYNC_TESTING_TRAIT_HASH_PREVIOUS,
403 22 : &bus->prev_hash),
404 22 : TALER_TESTING_make_trait_claim_token (0,
405 22 : &bus->token),
406 22 : SYNC_TESTING_make_trait_account_pub (0,
407 22 : &bus->sync_pub),
408 22 : SYNC_TESTING_make_trait_account_priv (0,
409 22 : &bus->sync_priv),
410 22 : TALER_TESTING_make_trait_order_id (0,
411 22 : bus->payment_order_id),
412 22 : TALER_TESTING_trait_end ()
413 : };
414 : struct TALER_TESTING_Trait ftraits[] = {
415 22 : TALER_TESTING_make_trait_claim_token (0,
416 22 : &bus->token),
417 22 : SYNC_TESTING_make_trait_account_pub (0,
418 22 : &bus->sync_pub),
419 22 : SYNC_TESTING_make_trait_account_priv (0,
420 22 : &bus->sync_priv),
421 22 : TALER_TESTING_make_trait_order_id (0,
422 22 : bus->payment_order_id),
423 22 : TALER_TESTING_trait_end ()
424 : };
425 :
426 :
427 22 : return TALER_TESTING_get_trait ((NULL != bus->payment_order_req)
428 : ? ftraits
429 : : straits,
430 : ret,
431 : trait,
432 : index);
433 : }
434 :
435 :
436 : /**
437 : * Make the "backup upload" command.
438 : *
439 : * @param label command label
440 : * @param sync_url base URL of the sync serving
441 : * the policy store request.
442 : * @param prev_upload reference to a previous upload we are
443 : * supposed to update, NULL for none
444 : * @param last_upload reference to the last upload for the
445 : * same account, used to check result on MHD_HTTP_CONFLICT
446 : * @param uo upload options
447 : * @param http_status expected HTTP status.
448 : * @param backup_data data to upload
449 : * @param backup_data_size number of bytes in @a backup_data
450 : * @return the command
451 : */
452 : struct TALER_TESTING_Command
453 5 : SYNC_TESTING_cmd_backup_upload (const char *label,
454 : const char *sync_url,
455 : const char *prev_upload,
456 : const char *last_upload,
457 : enum SYNC_TESTING_UploadOption uo,
458 : unsigned int http_status,
459 : const void *backup_data,
460 : size_t backup_data_size)
461 : {
462 : struct BackupUploadState *bus;
463 :
464 5 : bus = GNUNET_new (struct BackupUploadState);
465 5 : bus->http_status = http_status;
466 5 : bus->prev_upload = prev_upload;
467 5 : bus->last_upload = last_upload;
468 5 : bus->uopt = uo;
469 5 : bus->sync_url = sync_url;
470 5 : bus->backup = backup_data;
471 5 : bus->backup_size = backup_data_size;
472 : {
473 5 : struct TALER_TESTING_Command cmd = {
474 : .cls = bus,
475 : .label = label,
476 : .run = &backup_upload_run,
477 : .cleanup = &backup_upload_cleanup,
478 : .traits = &backup_upload_traits
479 : };
480 :
481 5 : return cmd;
482 : }
483 : }
|