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: 0 94 0.0 %
Date: 2022-06-30 06:15:34 Functions: 0 2 0.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_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           0 : 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           0 :   bool paid = false;
      66             : 
      67           0 :   if (GNUNET_OK !=
      68           0 :       TMH_db->start (TMH_db->cls,
      69             :                      "claim order"))
      70             :   {
      71           0 :     GNUNET_break (0);
      72           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
      73             :   }
      74           0 :   qs = TMH_db->lookup_contract_terms (TMH_db->cls,
      75             :                                       instance_id,
      76             :                                       order_id,
      77             :                                       contract_terms,
      78             :                                       &order_serial,
      79             :                                       &paid,
      80             :                                       NULL);
      81           0 :   if (0 > qs)
      82             :   {
      83           0 :     TMH_db->rollback (TMH_db->cls);
      84           0 :     return qs;
      85             :   }
      86             : 
      87           0 :   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           0 :       GNUNET_JSON_spec_string ("nonce",
      93             :                                &stored_nonce),
      94           0 :       GNUNET_JSON_spec_end ()
      95             :     };
      96             : 
      97           0 :     TMH_db->rollback (TMH_db->cls);
      98           0 :     GNUNET_assert (NULL != *contract_terms);
      99             : 
     100           0 :     if (GNUNET_OK !=
     101           0 :         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           0 :     if (0 != strcmp (stored_nonce,
     113             :                      nonce))
     114             :     {
     115           0 :       GNUNET_JSON_parse_free (spec);
     116           0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     117             :     }
     118           0 :     GNUNET_JSON_parse_free (spec);
     119           0 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     120             :   }
     121             : 
     122           0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
     123             : 
     124             :   /* Now we need to claim the order. */
     125             :   {
     126             :     struct TALER_MerchantPostDataHashP unused;
     127             :     struct GNUNET_TIME_Timestamp timestamp;
     128             :     struct GNUNET_JSON_Specification spec[] = {
     129           0 :       GNUNET_JSON_spec_timestamp ("timestamp",
     130             :                                   &timestamp),
     131           0 :       GNUNET_JSON_spec_end ()
     132             :     };
     133             : 
     134             :     /* see if we have this order in our table of unclaimed orders */
     135           0 :     qs = TMH_db->lookup_order (TMH_db->cls,
     136             :                                instance_id,
     137             :                                order_id,
     138             :                                &order_ct,
     139             :                                &unused,
     140             :                                contract_terms);
     141           0 :     if (0 >= qs)
     142             :     {
     143           0 :       TMH_db->rollback (TMH_db->cls);
     144           0 :       return qs;
     145             :     }
     146           0 :     GNUNET_assert (NULL != *contract_terms);
     147           0 :     if (GNUNET_OK !=
     148           0 :         GNUNET_JSON_parse (*contract_terms,
     149             :                            spec,
     150             :                            NULL,
     151             :                            NULL))
     152             :     {
     153             :       /* this should not be possible: contract_terms should always
     154             :          have a timestamp! */
     155           0 :       GNUNET_break (0);
     156           0 :       TMH_db->rollback (TMH_db->cls);
     157           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     158             :     }
     159             : 
     160           0 :     GNUNET_assert (0 ==
     161             :                    json_object_set_new (*contract_terms,
     162             :                                         "nonce",
     163             :                                         json_string (nonce)));
     164           0 :     if (0 != GNUNET_memcmp_priv (&order_ct,
     165             :                                  claim_token))
     166             :     {
     167           0 :       TMH_db->rollback (TMH_db->cls);
     168           0 :       json_decref (*contract_terms);
     169           0 :       *contract_terms = NULL;
     170           0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     171             :     }
     172           0 :     qs = TMH_db->insert_contract_terms (TMH_db->cls,
     173             :                                         instance_id,
     174             :                                         order_id,
     175             :                                         *contract_terms);
     176           0 :     if (0 > qs)
     177             :     {
     178           0 :       TMH_db->rollback (TMH_db->cls);
     179           0 :       json_decref (*contract_terms);
     180           0 :       *contract_terms = NULL;
     181           0 :       return qs;
     182             :     }
     183           0 :     TMH_notify_order_change (TMH_lookup_instance (instance_id),
     184             :                              TMH_OSF_CLAIMED,
     185             :                              timestamp,
     186             :                              order_serial);
     187           0 :     qs = TMH_db->commit (TMH_db->cls);
     188           0 :     if (0 > qs)
     189           0 :       return qs;
     190           0 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     191             :   }
     192             : }
     193             : 
     194             : 
     195             : MHD_RESULT
     196           0 : TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
     197             :                           struct MHD_Connection *connection,
     198             :                           struct TMH_HandlerContext *hc)
     199             : {
     200           0 :   const char *order_id = hc->infix;
     201             :   const char *nonce;
     202             :   enum GNUNET_DB_QueryStatus qs;
     203             :   json_t *contract_terms;
     204           0 :   struct TALER_ClaimTokenP claim_token = { 0 };
     205             : 
     206             :   {
     207             :     struct GNUNET_JSON_Specification spec[] = {
     208           0 :       GNUNET_JSON_spec_string ("nonce",
     209             :                                &nonce),
     210           0 :       GNUNET_JSON_spec_mark_optional (
     211             :         GNUNET_JSON_spec_fixed_auto ("token",
     212             :                                      &claim_token),
     213             :         NULL),
     214           0 :       GNUNET_JSON_spec_end ()
     215             :     };
     216             :     enum GNUNET_GenericReturnValue res;
     217             : 
     218           0 :     res = TALER_MHD_parse_json_data (connection,
     219           0 :                                      hc->request_body,
     220             :                                      spec);
     221           0 :     if (GNUNET_OK != res)
     222             :     {
     223           0 :       GNUNET_break_op (0);
     224           0 :       json_dumpf (hc->request_body,
     225             :                   stderr,
     226             :                   JSON_INDENT (2));
     227             :       return (GNUNET_NO == res)
     228             :              ? MHD_YES
     229           0 :              : MHD_NO;
     230             :     }
     231             :   }
     232           0 :   contract_terms = NULL;
     233           0 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     234             :   {
     235           0 :     TMH_db->preflight (TMH_db->cls);
     236           0 :     qs = claim_order (hc->instance->settings.id,
     237             :                       order_id,
     238             :                       nonce,
     239             :                       &claim_token,
     240             :                       &contract_terms);
     241           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     242           0 :       break;
     243             :   }
     244           0 :   switch (qs)
     245             :   {
     246           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     247           0 :     return TALER_MHD_reply_with_error (connection,
     248             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     249             :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     250             :                                        NULL);
     251           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     252           0 :     return TALER_MHD_reply_with_error (connection,
     253             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     254             :                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
     255             :                                        NULL);
     256           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     257           0 :     if (NULL == contract_terms)
     258           0 :       return TALER_MHD_reply_with_error (connection,
     259             :                                          MHD_HTTP_NOT_FOUND,
     260             :                                          TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_NOT_FOUND,
     261             :                                          order_id);
     262             :     /* already claimed! */
     263           0 :     json_decref (contract_terms);
     264           0 :     return TALER_MHD_reply_with_error (connection,
     265             :                                        MHD_HTTP_CONFLICT,
     266             :                                        TALER_EC_MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED,
     267             :                                        order_id);
     268           0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     269           0 :     GNUNET_assert (NULL != contract_terms);
     270           0 :     break; /* Good! return signature (below) */
     271             :   }
     272             : 
     273             :   /* create contract signature */
     274           0 :   {
     275             :     struct TALER_PrivateContractHashP hash;
     276             :     struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
     277             : 
     278             :     /**
     279             :      * Hash of the JSON contract in UTF-8 including 0-termination,
     280             :      * using JSON_COMPACT | JSON_SORT_KEYS
     281             :      */
     282             : 
     283           0 :     if (GNUNET_OK !=
     284           0 :         TALER_JSON_contract_hash (contract_terms,
     285             :                                   &hash))
     286             :     {
     287           0 :       GNUNET_break (0);
     288           0 :       json_decref (contract_terms);
     289           0 :       return TALER_MHD_reply_with_error (connection,
     290             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     291             :                                          TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     292             :                                          NULL);
     293             :     }
     294             : 
     295           0 :     TALER_merchant_contract_sign (&hash,
     296           0 :                                   &hc->instance->merchant_priv,
     297             :                                   &merchant_sig);
     298           0 :     return TALER_MHD_REPLY_JSON_PACK (
     299             :       connection,
     300             :       MHD_HTTP_OK,
     301             :       GNUNET_JSON_pack_object_steal ("contract_terms",
     302             :                                      contract_terms),
     303             :       GNUNET_JSON_pack_data_auto ("sig",
     304             :                                   &merchant_sig));
     305             :   }
     306             : }
     307             : 
     308             : 
     309             : /* end of taler-merchant-httpd_post-orders-ID-claim.c */

Generated by: LCOV version 1.14