merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_post-private-templates.c (9155B)


      1 /*
      2   This file is part of TALER
      3   (C) 2022-2024 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 src/backend/taler-merchant-httpd_post-private-templates.c
     22  * @brief implementing POST /templates request handling
     23  * @author Priscilla HUANG
     24  */
     25 #include "platform.h"
     26 #include "taler-merchant-httpd_post-private-templates.h"
     27 #include "taler-merchant-httpd_helper.h"
     28 #include <taler/taler_json_lib.h>
     29 #include "merchant-database/insert_template.h"
     30 #include "merchant-database/lookup_template.h"
     31 #include "merchant-database/select_otp_serial.h"
     32 
     33 
     34 /**
     35  * Check if the two templates are identical.
     36  *
     37  * @param t1 template to compare
     38  * @param t2 other template to compare
     39  * @return true if they are 'equal', false if not or of payto_uris is not an array
     40  */
     41 static bool
     42 templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
     43                  const struct TALER_MERCHANTDB_TemplateDetails *t2)
     44 {
     45   return ( (0 == strcmp (t1->template_description,
     46                          t2->template_description)) &&
     47            ( ( (NULL == t1->otp_id) &&
     48                (NULL == t2->otp_id) ) ||
     49              ( (NULL != t1->otp_id) &&
     50                (NULL != t2->otp_id) &&
     51                (0 == strcmp (t1->otp_id,
     52                              t2->otp_id))) ) &&
     53            ( ( (NULL == t1->editable_defaults) &&
     54                (NULL == t2->editable_defaults) ) ||
     55              ( (NULL != t1->editable_defaults) &&
     56                (NULL != t2->editable_defaults) &&
     57                (1 == json_equal (t1->editable_defaults,
     58                                  t2->editable_defaults))) ) &&
     59            (1 == json_equal (t1->template_contract,
     60                              t2->template_contract)) );
     61 }
     62 
     63 
     64 enum MHD_Result
     65 TMH_private_post_templates (const struct TMH_RequestHandler *rh,
     66                             struct MHD_Connection *connection,
     67                             struct TMH_HandlerContext *hc)
     68 {
     69   struct TMH_MerchantInstance *mi = hc->instance;
     70   struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
     71   const char *template_id;
     72   enum GNUNET_DB_QueryStatus qs;
     73   struct GNUNET_JSON_Specification spec[] = {
     74     GNUNET_JSON_spec_string ("template_id",
     75                              &template_id),
     76     GNUNET_JSON_spec_string ("template_description",
     77                              (const char **) &tp.template_description),
     78     GNUNET_JSON_spec_mark_optional (
     79       GNUNET_JSON_spec_string ("otp_id",
     80                                (const char **) &tp.otp_id),
     81       NULL),
     82     GNUNET_JSON_spec_json ("template_contract",
     83                            &tp.template_contract),
     84     GNUNET_JSON_spec_mark_optional (
     85       GNUNET_JSON_spec_json ("editable_defaults",
     86                              &tp.editable_defaults),
     87       NULL),
     88     GNUNET_JSON_spec_end ()
     89   };
     90   uint64_t otp_serial = 0;
     91 
     92   GNUNET_assert (NULL != mi);
     93   {
     94     enum GNUNET_GenericReturnValue res;
     95 
     96     res = TALER_MHD_parse_json_data (connection,
     97                                      hc->request_body,
     98                                      spec);
     99     if (GNUNET_OK != res)
    100     {
    101       GNUNET_break_op (0);
    102       return (GNUNET_NO == res)
    103              ? MHD_YES
    104              : MHD_NO;
    105     }
    106   }
    107   if (! TALER_MERCHANT_template_contract_valid (tp.template_contract))
    108   {
    109     GNUNET_break_op (0);
    110     json_dumpf (tp.template_contract,
    111                 stderr,
    112                 JSON_INDENT (2));
    113     GNUNET_JSON_parse_free (spec);
    114     return TALER_MHD_reply_with_error (connection,
    115                                        MHD_HTTP_BAD_REQUEST,
    116                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    117                                        "template_contract");
    118   }
    119 
    120   if (NULL != tp.editable_defaults)
    121   {
    122     const char *key;
    123     json_t *val;
    124 
    125     json_object_foreach (tp.editable_defaults, key, val)
    126     {
    127       if (NULL !=
    128           json_object_get (tp.template_contract,
    129                            key))
    130       {
    131         char *msg;
    132         enum MHD_Result ret;
    133 
    134         GNUNET_break_op (0);
    135         GNUNET_asprintf (&msg,
    136                          "editable_defaults::%s conflicts with template_contract",
    137                          key);
    138         GNUNET_JSON_parse_free (spec);
    139         ret = TALER_MHD_reply_with_error (connection,
    140                                           MHD_HTTP_BAD_REQUEST,
    141                                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    142                                           msg);
    143         GNUNET_free (msg);
    144         return ret;
    145       }
    146     }
    147   }
    148 
    149   if (NULL != tp.otp_id)
    150   {
    151     qs = TALER_MERCHANTDB_select_otp_serial (TMH_db,
    152                                              mi->settings.id,
    153                                              tp.otp_id,
    154                                              &otp_serial);
    155     switch (qs)
    156     {
    157     case GNUNET_DB_STATUS_HARD_ERROR:
    158     case GNUNET_DB_STATUS_SOFT_ERROR:
    159       GNUNET_break (0);
    160       GNUNET_JSON_parse_free (spec);
    161       return TALER_MHD_reply_with_error (connection,
    162                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    163                                          TALER_EC_GENERIC_DB_STORE_FAILED,
    164                                          "select_otp_serial");
    165     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    166       GNUNET_JSON_parse_free (spec);
    167       return TALER_MHD_reply_with_error (connection,
    168                                          MHD_HTTP_NOT_FOUND,
    169                                          TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
    170                                          NULL);
    171     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    172       break;
    173     }
    174   }
    175 
    176   qs = TALER_MERCHANTDB_insert_template (TMH_db,
    177                                          mi->settings.id,
    178                                          template_id,
    179                                          otp_serial,
    180                                          &tp);
    181   switch (qs)
    182   {
    183   case GNUNET_DB_STATUS_HARD_ERROR:
    184   case GNUNET_DB_STATUS_SOFT_ERROR:
    185     GNUNET_break (0);
    186     GNUNET_JSON_parse_free (spec);
    187     return TALER_MHD_reply_with_error (connection,
    188                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    189                                        TALER_EC_GENERIC_DB_STORE_FAILED,
    190                                        NULL);
    191   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    192     GNUNET_JSON_parse_free (spec);
    193     return TALER_MHD_reply_static (connection,
    194                                    MHD_HTTP_NO_CONTENT,
    195                                    NULL,
    196                                    NULL,
    197                                    0);
    198   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    199     break;
    200   }
    201 
    202   {
    203     /* Test if a template of this id is known */
    204     struct TALER_MERCHANTDB_TemplateDetails etp;
    205 
    206     qs = TALER_MERCHANTDB_lookup_template (TMH_db,
    207                                            mi->settings.id,
    208                                            template_id,
    209                                            &etp);
    210     switch (qs)
    211     {
    212     case GNUNET_DB_STATUS_HARD_ERROR:
    213     case GNUNET_DB_STATUS_SOFT_ERROR:
    214       /* Clean up and fail hard */
    215       GNUNET_break (0);
    216       GNUNET_JSON_parse_free (spec);
    217       return TALER_MHD_reply_with_error (connection,
    218                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    219                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    220                                          NULL);
    221     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    222       GNUNET_break (0);
    223       GNUNET_JSON_parse_free (spec);
    224       return TALER_MHD_reply_with_error (connection,
    225                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    226                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    227                                          "logic error");
    228     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    229       break;
    230     }
    231     /* idempotency check: is etp == tp? */
    232     {
    233       bool eq;
    234 
    235       eq = templates_equal (&tp,
    236                             &etp);
    237       TALER_MERCHANTDB_template_details_free (&etp);
    238       GNUNET_JSON_parse_free (spec);
    239       return eq
    240         ? TALER_MHD_reply_static (connection,
    241                                   MHD_HTTP_NO_CONTENT,
    242                                   NULL,
    243                                   NULL,
    244                                   0)
    245         : TALER_MHD_reply_with_error (connection,
    246                                       MHD_HTTP_CONFLICT,
    247                                       TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
    248                                       template_id);
    249     }
    250   }
    251 }
    252 
    253 
    254 /* end of taler-merchant-httpd_post-private-templates.c */