LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-orders-ID-claim.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 66 85 77.6 %
Date: 2021-08-30 06:54:17 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14