LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-orders-ID-claim.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.6 % 92 64
Test Date: 2025-10-31 14:20:14 Functions: 100.0 % 2 2

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2014, 2015, 2016, 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 Affero General Public License as
       7              :   published by the Free Software Foundation; either version 3,
       8              :   or (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,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : 
      20              : /**
      21              :  * @file taler-merchant-httpd_post-orders-ID-claim.c
      22              :  * @brief headers for POST /orders/$ID/claim handler
      23              :  * @author Marcello Stanisci
      24              :  * @author Christian Grothoff
      25              :  */
      26              : #include "platform.h"
      27              : #include <jansson.h>
      28              : #include <taler/taler_signatures.h>
      29              : #include <taler/taler_json_lib.h>
      30              : #include "taler-merchant-httpd_private-get-orders.h"
      31              : #include "taler-merchant-httpd_post-orders-ID-claim.h"
      32              : 
      33              : 
      34              : /**
      35              :  * How often do we retry the database transaction?
      36              :  */
      37              : #define MAX_RETRIES 3
      38              : 
      39              : 
      40              : /**
      41              :  * Run transaction to claim @a order_id for @a nonce.
      42              :  *
      43              :  * @param instance_id instance to claim order at
      44              :  * @param order_id order to claim
      45              :  * @param nonce nonce to use for the claim
      46              :  * @param claim_token the token that should be used to verify the claim
      47              :  * @param[out] contract_terms set to the resulting contract terms
      48              :  *             (for any non-negative result;
      49              :  * @return transaction status code
      50              :  *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the order was claimed by a different
      51              :  *         nonce (@a contract_terms set to non-NULL)
      52              :  *                OR if the order is is unknown (@a contract_terms is NULL)
      53              :  *         #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the order was successfully claimed
      54              :  */
      55              : static enum GNUNET_DB_QueryStatus
      56           66 : claim_order (const char *instance_id,
      57              :              const char *order_id,
      58              :              const char *nonce,
      59              :              const struct TALER_ClaimTokenP *claim_token,
      60              :              json_t **contract_terms)
      61              : {
      62              :   struct TALER_ClaimTokenP order_ct;
      63              :   enum GNUNET_DB_QueryStatus qs;
      64              :   uint64_t order_serial;
      65              : 
      66           66 :   if (GNUNET_OK !=
      67           66 :       TMH_db->start (TMH_db->cls,
      68              :                      "claim order"))
      69              :   {
      70            0 :     GNUNET_break (0);
      71            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
      72              :   }
      73           66 :   qs = TMH_db->lookup_contract_terms (TMH_db->cls,
      74              :                                       instance_id,
      75              :                                       order_id,
      76              :                                       contract_terms,
      77              :                                       &order_serial,
      78              :                                       NULL);
      79           66 :   if (0 > qs)
      80              :   {
      81            0 :     TMH_db->rollback (TMH_db->cls);
      82            0 :     return qs;
      83              :   }
      84              : 
      85           66 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
      86              :   {
      87              :     /* We already have claimed contract terms for this order_id */
      88              :     const char *stored_nonce;
      89              :     struct GNUNET_JSON_Specification spec[] = {
      90           11 :       GNUNET_JSON_spec_string ("nonce",
      91              :                                &stored_nonce),
      92           11 :       GNUNET_JSON_spec_end ()
      93              :     };
      94              : 
      95           11 :     TMH_db->rollback (TMH_db->cls);
      96           11 :     GNUNET_assert (NULL != *contract_terms);
      97              : 
      98           11 :     if (GNUNET_OK !=
      99           11 :         GNUNET_JSON_parse (*contract_terms,
     100              :                            spec,
     101              :                            NULL,
     102              :                            NULL))
     103              :     {
     104              :       /* this should not be possible: contract_terms should always
     105              :          have a nonce! */
     106            0 :       GNUNET_break (0);
     107            0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     108              :     }
     109              : 
     110           11 :     if (0 != strcmp (stored_nonce,
     111              :                      nonce))
     112              :     {
     113            2 :       GNUNET_JSON_parse_free (spec);
     114            2 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     115              :     }
     116            9 :     GNUNET_JSON_parse_free (spec);
     117            9 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     118              :   }
     119              : 
     120           55 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
     121              : 
     122              :   /* Now we need to claim the order. */
     123              :   {
     124              :     struct TALER_MerchantPostDataHashP unused;
     125              :     struct GNUNET_TIME_Timestamp timestamp;
     126              :     struct GNUNET_JSON_Specification spec[] = {
     127           55 :       GNUNET_JSON_spec_timestamp ("timestamp",
     128              :                                   &timestamp),
     129           55 :       GNUNET_JSON_spec_end ()
     130              :     };
     131              : 
     132              :     /* see if we have this order in our table of unclaimed orders */
     133           55 :     qs = TMH_db->lookup_order (TMH_db->cls,
     134              :                                instance_id,
     135              :                                order_id,
     136              :                                &order_ct,
     137              :                                &unused,
     138              :                                contract_terms);
     139           55 :     if (0 >= qs)
     140              :     {
     141            2 :       TMH_db->rollback (TMH_db->cls);
     142            2 :       return qs;
     143              :     }
     144           53 :     GNUNET_assert (NULL != *contract_terms);
     145           53 :     if (GNUNET_OK !=
     146           53 :         GNUNET_JSON_parse (*contract_terms,
     147              :                            spec,
     148              :                            NULL,
     149              :                            NULL))
     150              :     {
     151              :       /* this should not be possible: contract_terms should always
     152              :          have a timestamp! */
     153            0 :       GNUNET_break (0);
     154            0 :       TMH_db->rollback (TMH_db->cls);
     155            0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     156              :     }
     157              : 
     158           53 :     GNUNET_assert (0 ==
     159              :                    json_object_set_new (*contract_terms,
     160              :                                         "nonce",
     161              :                                         json_string (nonce)));
     162           53 :     if (0 != GNUNET_memcmp_priv (&order_ct,
     163              :                                  claim_token))
     164              :     {
     165            0 :       TMH_db->rollback (TMH_db->cls);
     166            0 :       json_decref (*contract_terms);
     167            0 :       *contract_terms = NULL;
     168            0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     169              :     }
     170           53 :     qs = TMH_db->insert_contract_terms (TMH_db->cls,
     171              :                                         instance_id,
     172              :                                         order_id,
     173              :                                         *contract_terms,
     174              :                                         &order_serial);
     175           53 :     if (0 >= qs)
     176              :     {
     177            0 :       TMH_db->rollback (TMH_db->cls);
     178            0 :       json_decref (*contract_terms);
     179            0 :       *contract_terms = NULL;
     180            0 :       return qs;
     181              :     }
     182           53 :     TMH_notify_order_change (TMH_lookup_instance (instance_id),
     183              :                              TMH_OSF_CLAIMED,
     184              :                              timestamp,
     185              :                              order_serial);
     186           53 :     qs = TMH_db->commit (TMH_db->cls);
     187           53 :     if (0 > qs)
     188            0 :       return qs;
     189           53 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     190              :   }
     191              : }
     192              : 
     193              : 
     194              : MHD_RESULT
     195           66 : TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
     196              :                           struct MHD_Connection *connection,
     197              :                           struct TMH_HandlerContext *hc)
     198              : {
     199           66 :   const char *order_id = hc->infix;
     200              :   const char *nonce;
     201              :   enum GNUNET_DB_QueryStatus qs;
     202              :   json_t *contract_terms;
     203           66 :   struct TALER_ClaimTokenP claim_token = { 0 };
     204              : 
     205              :   {
     206              :     struct GNUNET_JSON_Specification spec[] = {
     207           66 :       GNUNET_JSON_spec_string ("nonce",
     208              :                                &nonce),
     209           66 :       GNUNET_JSON_spec_mark_optional (
     210              :         GNUNET_JSON_spec_fixed_auto ("token",
     211              :                                      &claim_token),
     212              :         NULL),
     213           66 :       GNUNET_JSON_spec_end ()
     214              :     };
     215              :     enum GNUNET_GenericReturnValue res;
     216              : 
     217           66 :     res = TALER_MHD_parse_json_data (connection,
     218           66 :                                      hc->request_body,
     219              :                                      spec);
     220           66 :     if (GNUNET_OK != res)
     221              :     {
     222            0 :       GNUNET_break_op (0);
     223            0 :       json_dumpf (hc->request_body,
     224              :                   stderr,
     225              :                   JSON_INDENT (2));
     226              :       return (GNUNET_NO == res)
     227              :              ? MHD_YES
     228            0 :              : MHD_NO;
     229              :     }
     230              :   }
     231           66 :   contract_terms = NULL;
     232           66 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     233              :   {
     234           66 :     TMH_db->preflight (TMH_db->cls);
     235           66 :     qs = claim_order (hc->instance->settings.id,
     236              :                       order_id,
     237              :                       nonce,
     238              :                       &claim_token,
     239              :                       &contract_terms);
     240           66 :     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     241           66 :       break;
     242              :   }
     243           66 :   switch (qs)
     244              :   {
     245            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     246            0 :     return TALER_MHD_reply_with_error (connection,
     247              :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     248              :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     249              :                                        NULL);
     250            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     251            0 :     return TALER_MHD_reply_with_error (connection,
     252              :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     253              :                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
     254              :                                        NULL);
     255            4 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     256            4 :     if (NULL == contract_terms)
     257            2 :       return TALER_MHD_reply_with_error (connection,
     258              :                                          MHD_HTTP_NOT_FOUND,
     259              :                                          TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_NOT_FOUND,
     260              :                                          order_id);
     261              :     /* already claimed! */
     262            2 :     json_decref (contract_terms);
     263            2 :     return TALER_MHD_reply_with_error (connection,
     264              :                                        MHD_HTTP_CONFLICT,
     265              :                                        TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED,
     266              :                                        order_id);
     267           62 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     268           62 :     GNUNET_assert (NULL != contract_terms);
     269           62 :     break; /* Good! return signature (below) */
     270              :   }
     271              : 
     272              :   /* create contract signature */
     273              :   {
     274              :     struct TALER_PrivateContractHashP hash;
     275              :     struct TALER_MerchantSignatureP merchant_sig;
     276              : 
     277              :     /**
     278              :      * Hash of the JSON contract in UTF-8 including 0-termination,
     279              :      * using JSON_COMPACT | JSON_SORT_KEYS
     280              :      */
     281              : 
     282           62 :     if (GNUNET_OK !=
     283           62 :         TALER_JSON_contract_hash (contract_terms,
     284              :                                   &hash))
     285              :     {
     286            0 :       GNUNET_break (0);
     287            0 :       json_decref (contract_terms);
     288            0 :       return TALER_MHD_reply_with_error (connection,
     289              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     290              :                                          TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     291              :                                          NULL);
     292              :     }
     293              : 
     294           62 :     TALER_merchant_contract_sign (&hash,
     295           62 :                                   &hc->instance->merchant_priv,
     296              :                                   &merchant_sig);
     297           62 :     return TALER_MHD_REPLY_JSON_PACK (
     298              :       connection,
     299              :       MHD_HTTP_OK,
     300              :       GNUNET_JSON_pack_object_steal ("contract_terms",
     301              :                                      contract_terms),
     302              :       GNUNET_JSON_pack_data_auto ("sig",
     303              :                                   &merchant_sig));
     304              :   }
     305              : }
     306              : 
     307              : 
     308              : /* end of taler-merchant-httpd_post-orders-ID-claim.c */
        

Generated by: LCOV version 2.0-1