merchant

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

taler-merchant-httpd_helper.c (35919B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014--2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-merchant-httpd_helper.c
     18  * @brief shared logic for various handlers
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_db_lib.h>
     24 #include <taler/taler_json_lib.h>
     25 #include "taler-merchant-httpd_helper.h"
     26 #include <taler/taler_templating_lib.h>
     27 #include <taler/taler_dbevents.h>
     28 
     29 
     30 void
     31 TMH_quantity_defaults_from_unit (const struct TMH_MerchantInstance *mi,
     32                                  const char *unit,
     33                                  bool *allow_fractional,
     34                                  uint32_t *precision_level)
     35 {
     36   GNUNET_assert (NULL != allow_fractional);
     37   GNUNET_assert (NULL != precision_level);
     38   if (GNUNET_OK !=
     39       TMH_unit_defaults_for_instance (mi,
     40                                       unit,
     41                                       allow_fractional,
     42                                       precision_level))
     43   {
     44     *allow_fractional = false;
     45     *precision_level = 0;
     46   }
     47 }
     48 
     49 
     50 enum GNUNET_GenericReturnValue
     51 TMH_unit_defaults_for_instance (const struct TMH_MerchantInstance *mi,
     52                                 const char *unit,
     53                                 bool *allow_fractional,
     54                                 uint32_t *precision_level)
     55 {
     56   struct TALER_MERCHANTDB_UnitDetails ud = { 0 };
     57   enum GNUNET_DB_QueryStatus qs;
     58   bool allow = false;
     59   uint32_t precision = 0;
     60 
     61   GNUNET_assert (NULL != allow_fractional);
     62   GNUNET_assert (NULL != precision_level);
     63 
     64   qs = TMH_db->select_unit (TMH_db->cls,
     65                             mi->settings.id,
     66                             unit,
     67                             &ud);
     68   switch (qs)
     69   {
     70   case GNUNET_DB_STATUS_HARD_ERROR:
     71   case GNUNET_DB_STATUS_SOFT_ERROR:
     72     TALER_MERCHANTDB_unit_details_free (&ud);
     73     return GNUNET_SYSERR;
     74   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     75     allow = ud.unit_allow_fraction;
     76     precision = ud.unit_precision_level;
     77     break;
     78   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     79     break;
     80   default:
     81     GNUNET_break (0);
     82     TALER_MERCHANTDB_unit_details_free (&ud);
     83     return GNUNET_SYSERR;
     84   }
     85   TALER_MERCHANTDB_unit_details_free (&ud);
     86 
     87   /* This is definitely not supposed to happen
     88      combination of allow -> false, and precision > 0
     89      yet let's fix it */
     90   if ( (! allow) &&
     91        (0 != precision) )
     92   {
     93     GNUNET_break (0);
     94     precision = 0;
     95   }
     96   *allow_fractional = allow;
     97   *precision_level = precision;
     98   return GNUNET_OK;
     99 }
    100 
    101 
    102 enum GNUNET_GenericReturnValue
    103 TMH_cmp_wire_account (
    104   const json_t *account,
    105   const struct TMH_WireMethod *wm)
    106 {
    107   const char *credit_facade_url = NULL;
    108   const json_t *credit_facade_credentials = NULL;
    109   struct TALER_FullPayto uri;
    110   struct GNUNET_JSON_Specification ispec[] = {
    111     TALER_JSON_spec_full_payto_uri ("payto_uri",
    112                                     &uri),
    113     GNUNET_JSON_spec_mark_optional (
    114       TALER_JSON_spec_web_url ("credit_facade_url",
    115                                &credit_facade_url),
    116       NULL),
    117     GNUNET_JSON_spec_mark_optional (
    118       GNUNET_JSON_spec_object_const ("credit_facade_credentials",
    119                                      &credit_facade_credentials),
    120       NULL),
    121     GNUNET_JSON_spec_end ()
    122   };
    123   enum GNUNET_GenericReturnValue res;
    124   const char *ename;
    125   unsigned int eline;
    126 
    127   res = GNUNET_JSON_parse (account,
    128                            ispec,
    129                            &ename,
    130                            &eline);
    131   if (GNUNET_OK != res)
    132   {
    133     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    134                 "Failed to parse account spec: %s (%u)\n",
    135                 ename,
    136                 eline);
    137     return GNUNET_SYSERR;
    138   }
    139   if (0 !=
    140       TALER_full_payto_cmp (wm->payto_uri,
    141                             uri))
    142   {
    143     return GNUNET_SYSERR;
    144   }
    145   if ( (NULL == credit_facade_url) !=
    146        (NULL == wm->credit_facade_url) ||
    147        (NULL == credit_facade_credentials) !=
    148        (NULL == wm->credit_facade_credentials) )
    149   {
    150     return GNUNET_NO;
    151   }
    152   if ( (NULL != credit_facade_url) &&
    153        (0 != strcmp (credit_facade_url,
    154                      wm->credit_facade_url)) )
    155   {
    156     return GNUNET_NO;
    157   }
    158   if ( (NULL != credit_facade_credentials) &&
    159        (0 != json_equal (credit_facade_credentials,
    160                          wm->credit_facade_credentials)) )
    161   {
    162     return GNUNET_NO;
    163   }
    164   return GNUNET_YES;
    165 }
    166 
    167 
    168 bool
    169 TMH_accounts_array_valid (const json_t *accounts)
    170 {
    171   size_t len;
    172 
    173   if (! json_is_array (accounts))
    174   {
    175     GNUNET_break_op (0);
    176     return false;
    177   }
    178   len = json_array_size (accounts);
    179   for (size_t i = 0; i<len; i++)
    180   {
    181     json_t *payto_uri = json_array_get (accounts,
    182                                         i);
    183     const char *credit_facade_url = NULL;
    184     const json_t *credit_facade_credentials = NULL;
    185     struct TALER_FullPayto uri;
    186     struct GNUNET_JSON_Specification ispec[] = {
    187       TALER_JSON_spec_full_payto_uri ("payto_uri",
    188                                       &uri),
    189       GNUNET_JSON_spec_mark_optional (
    190         TALER_JSON_spec_web_url ("credit_facade_url",
    191                                  &credit_facade_url),
    192         NULL),
    193       GNUNET_JSON_spec_mark_optional (
    194         GNUNET_JSON_spec_object_const ("credit_facade_credentials",
    195                                        &credit_facade_credentials),
    196         NULL),
    197       GNUNET_JSON_spec_end ()
    198     };
    199     enum GNUNET_GenericReturnValue res;
    200     const char *ename;
    201     unsigned int eline;
    202 
    203     res = GNUNET_JSON_parse (payto_uri,
    204                              ispec,
    205                              &ename,
    206                              &eline);
    207     if (GNUNET_OK != res)
    208     {
    209       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    210                   "Failed to parse account spec: %s (%u)\n",
    211                   ename,
    212                   eline);
    213       return false;
    214     }
    215 
    216     /* Test for the same payto:// URI being given twice */
    217     for (size_t j = 0; j<i; j++)
    218     {
    219       json_t *old_uri = json_array_get (accounts,
    220                                         j);
    221       if (0 == strcmp (uri.full_payto,
    222                        json_string_value (
    223                          json_object_get (old_uri,
    224                                           "payto_uri"))))
    225       {
    226         GNUNET_break_op (0);
    227         return false;
    228       }
    229     }
    230     {
    231       char *err;
    232 
    233       if (NULL !=
    234           (err = TALER_payto_validate (uri)))
    235       {
    236         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    237                     "Encountered invalid payto://-URI `%s': %s\n",
    238                     uri.full_payto,
    239                     err);
    240         GNUNET_free (err);
    241         return false;
    242       }
    243     }
    244     if ( (NULL == credit_facade_url) !=
    245          (NULL == credit_facade_credentials) )
    246     {
    247       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    248                   "If credit_facade_url is given, credit_facade_credentials must also be specified (violated for %s)\n",
    249                   uri.full_payto);
    250       return false;
    251     }
    252     if ( (NULL != credit_facade_url) ||
    253          (NULL != credit_facade_credentials) )
    254     {
    255       struct TALER_MERCHANT_BANK_AuthenticationData auth;
    256 
    257       if (GNUNET_OK !=
    258           TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
    259                                                credit_facade_url,
    260                                                &auth))
    261       {
    262         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    263                     "Invalid credit facade URL or credentials `%s'\n",
    264                     credit_facade_url);
    265         return false;
    266       }
    267       TALER_MERCHANT_BANK_auth_free (&auth);
    268     }
    269   } /* end for all accounts */
    270   return true;
    271 }
    272 
    273 
    274 bool
    275 TMH_location_object_valid (const json_t *location)
    276 {
    277   const char *country = NULL;
    278   const char *subdivision = NULL;
    279   const char *district = NULL;
    280   const char *town = NULL;
    281   const char *town_loc = NULL;
    282   const char *postcode = NULL;
    283   const char *street = NULL;
    284   const char *building = NULL;
    285   const char *building_no = NULL;
    286   const json_t *lines = NULL;
    287   struct GNUNET_JSON_Specification spec[] = {
    288     GNUNET_JSON_spec_mark_optional (
    289       GNUNET_JSON_spec_string ("country",
    290                                &country),
    291       NULL),
    292     GNUNET_JSON_spec_mark_optional (
    293       GNUNET_JSON_spec_string ("country_subdivision",
    294                                &subdivision),
    295       NULL),
    296     GNUNET_JSON_spec_mark_optional (
    297       GNUNET_JSON_spec_string ("district",
    298                                &district),
    299       NULL),
    300     GNUNET_JSON_spec_mark_optional (
    301       GNUNET_JSON_spec_string ("town",
    302                                &town),
    303       NULL),
    304     GNUNET_JSON_spec_mark_optional (
    305       GNUNET_JSON_spec_string ("town_location",
    306                                &town_loc),
    307       NULL),
    308     GNUNET_JSON_spec_mark_optional (
    309       GNUNET_JSON_spec_string ("post_code",
    310                                &postcode),
    311       NULL),
    312     GNUNET_JSON_spec_mark_optional (
    313       GNUNET_JSON_spec_string ("street",
    314                                &street),
    315       NULL),
    316     GNUNET_JSON_spec_mark_optional (
    317       GNUNET_JSON_spec_string ("building_name",
    318                                &building),
    319       NULL),
    320     GNUNET_JSON_spec_mark_optional (
    321       GNUNET_JSON_spec_string ("building_number",
    322                                &building_no),
    323       NULL),
    324     GNUNET_JSON_spec_mark_optional (
    325       GNUNET_JSON_spec_array_const ("address_lines",
    326                                     &lines),
    327       NULL),
    328     GNUNET_JSON_spec_end ()
    329   };
    330   const char *ename;
    331   unsigned int eline;
    332 
    333   if (GNUNET_OK !=
    334       GNUNET_JSON_parse (location,
    335                          spec,
    336                          &ename,
    337                          &eline))
    338   {
    339     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    340                 "Invalid location for field %s\n",
    341                 ename);
    342     return false;
    343   }
    344   if (NULL != lines)
    345   {
    346     size_t idx;
    347     json_t *line;
    348 
    349     json_array_foreach (lines, idx, line)
    350     {
    351       if (! json_is_string (line))
    352       {
    353         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    354                     "Invalid address line #%u in location\n",
    355                     (unsigned int) idx);
    356         return false;
    357       }
    358     }
    359   }
    360   return true;
    361 }
    362 
    363 
    364 bool
    365 TMH_products_array_valid (const json_t *products)
    366 {
    367   const json_t *product;
    368   size_t idx;
    369   bool valid = true;
    370 
    371   if (! json_is_array (products))
    372   {
    373     GNUNET_break_op (0);
    374     return false;
    375   }
    376   json_array_foreach ((json_t *) products, idx, product)
    377   {
    378     /* FIXME: use TALER_MERCHANT_parse_product() instead? */
    379     const char *product_id = NULL;
    380     const char *description;
    381     uint64_t quantity = 0;
    382     bool quantity_missing = true;
    383     const char *unit_quantity = NULL;
    384     bool unit_quantity_missing = true;
    385     const char *unit = NULL;
    386     struct TALER_Amount price = { .value = 0 };
    387     const char *image_data_url = NULL;
    388     const json_t *taxes = NULL;
    389     struct GNUNET_TIME_Timestamp delivery_date = { 0 };
    390     struct GNUNET_JSON_Specification spec[] = {
    391       GNUNET_JSON_spec_mark_optional (
    392         GNUNET_JSON_spec_string ("product_id",
    393                                  &product_id),
    394         NULL),
    395       TALER_JSON_spec_i18n_str ("description",
    396                                 &description),
    397       GNUNET_JSON_spec_mark_optional (
    398         GNUNET_JSON_spec_uint64 ("quantity",
    399                                  &quantity),
    400         &quantity_missing),
    401       GNUNET_JSON_spec_mark_optional (
    402         GNUNET_JSON_spec_string ("unit_quantity",
    403                                  &unit_quantity),
    404         &unit_quantity_missing),
    405       GNUNET_JSON_spec_mark_optional (
    406         GNUNET_JSON_spec_string ("unit",
    407                                  &unit),
    408         NULL),
    409       GNUNET_JSON_spec_mark_optional (
    410         TALER_JSON_spec_amount_any ("price",
    411                                     &price),
    412         NULL),
    413       GNUNET_JSON_spec_mark_optional (
    414         GNUNET_JSON_spec_string ("image",
    415                                  &image_data_url),
    416         NULL),
    417       GNUNET_JSON_spec_mark_optional (
    418         GNUNET_JSON_spec_array_const ("taxes",
    419                                       &taxes),
    420         NULL),
    421       GNUNET_JSON_spec_mark_optional (
    422         GNUNET_JSON_spec_timestamp ("delivery_date",
    423                                     &delivery_date),
    424         NULL),
    425       GNUNET_JSON_spec_end ()
    426     };
    427     const char *ename;
    428     unsigned int eline;
    429 
    430     if (GNUNET_OK !=
    431         GNUNET_JSON_parse (product,
    432                            spec,
    433                            &ename,
    434                            &eline))
    435     {
    436       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    437                   "Invalid product #%u for field %s\n",
    438                   (unsigned int) idx,
    439                   ename);
    440       return false;
    441     }
    442     if (quantity_missing)
    443     {
    444       if (unit_quantity_missing)
    445       {
    446         GNUNET_break_op (0);
    447         valid = false;
    448       }
    449     }
    450     if ( (NULL != image_data_url) &&
    451          (! TALER_MERCHANT_image_data_url_valid (image_data_url)) )
    452     {
    453       GNUNET_break_op (0);
    454       valid = false;
    455     }
    456     if ( (NULL != taxes) &&
    457          (! TALER_MERCHANT_taxes_array_valid (taxes)) )
    458     {
    459       GNUNET_break_op (0);
    460       valid = false;
    461     }
    462     GNUNET_JSON_parse_free (spec);
    463     if (! valid)
    464       break;
    465   }
    466 
    467   return valid;
    468 }
    469 
    470 
    471 enum GNUNET_GenericReturnValue
    472 TMH_validate_unit_price_array (const struct TALER_Amount *prices,
    473                                size_t prices_len)
    474 {
    475   /* Check for duplicate currencies */
    476   for (size_t i = 0; i < prices_len; i++)
    477   {
    478     for (size_t j = i + 1; j < prices_len; j++)
    479     {
    480       if (GNUNET_YES ==
    481           TALER_amount_cmp_currency (&prices[i],
    482                                      &prices[j]))
    483       {
    484         /* duplicate currency, not allowed! */
    485         GNUNET_break_op (0);
    486         return GNUNET_SYSERR;
    487       }
    488     }
    489   }
    490   return GNUNET_OK;
    491 }
    492 
    493 
    494 bool
    495 TMH_category_set_contains (const struct TMH_CategorySet *set,
    496                            uint64_t id)
    497 {
    498   for (size_t i = 0; i < set->len; i++)
    499     if (set->ids[i] == id)
    500       return true;
    501   return false;
    502 }
    503 
    504 
    505 void
    506 TMH_category_set_add (struct TMH_CategorySet *set,
    507                       uint64_t id)
    508 {
    509   if (TMH_category_set_contains (set,
    510                                  id))
    511     return;
    512   GNUNET_array_append (set->ids,
    513                        set->len,
    514                        id);
    515 }
    516 
    517 
    518 bool
    519 TMH_unit_set_contains (const struct TMH_UnitSet *set,
    520                        const char *unit)
    521 {
    522   for (unsigned int i = 0; i < set->len; i++)
    523     if (0 == strcmp (set->units[i],
    524                      unit))
    525       return true;
    526   return false;
    527 }
    528 
    529 
    530 void
    531 TMH_unit_set_add (struct TMH_UnitSet *set,
    532                   const char *unit)
    533 {
    534   if (TMH_unit_set_contains (set,
    535                              unit))
    536     return;
    537   GNUNET_array_append (set->units,
    538                        set->len,
    539                        GNUNET_strdup (unit));
    540 }
    541 
    542 
    543 void
    544 TMH_unit_set_clear (struct TMH_UnitSet *set)
    545 {
    546   for (unsigned int i = 0; i < set->len; i++)
    547     GNUNET_free (set->units[i]);
    548   GNUNET_free (set->units);
    549   set->units = NULL;
    550   set->len = 0;
    551 }
    552 
    553 
    554 struct TMH_WireMethod *
    555 TMH_setup_wire_account (
    556   struct TALER_FullPayto payto_uri,
    557   const char *credit_facade_url,
    558   const json_t *credit_facade_credentials)
    559 {
    560   struct TMH_WireMethod *wm;
    561   char *emsg;
    562 
    563   if (NULL != (emsg = TALER_payto_validate (payto_uri)))
    564   {
    565     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    566                 "Invalid URI `%s': %s\n",
    567                 payto_uri.full_payto,
    568                 emsg);
    569     GNUNET_free (emsg);
    570     return NULL;
    571   }
    572 
    573   wm = GNUNET_new (struct TMH_WireMethod);
    574   if (NULL != credit_facade_url)
    575     wm->credit_facade_url
    576       = GNUNET_strdup (credit_facade_url);
    577   if (NULL != credit_facade_credentials)
    578     wm->credit_facade_credentials
    579       = json_incref ((json_t*) credit_facade_credentials);
    580   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    581                               &wm->wire_salt,
    582                               sizeof (wm->wire_salt));
    583   wm->payto_uri.full_payto
    584     = GNUNET_strdup (payto_uri.full_payto);
    585   TALER_merchant_wire_signature_hash (payto_uri,
    586                                       &wm->wire_salt,
    587                                       &wm->h_wire);
    588   wm->wire_method
    589     = TALER_payto_get_method (payto_uri.full_payto);
    590   wm->active = true;
    591   return wm;
    592 }
    593 
    594 
    595 enum TALER_ErrorCode
    596 TMH_check_token (const char *token,
    597                  const char *instance_id,
    598                  enum TMH_AuthScope *as)
    599 {
    600   enum TMH_AuthScope scope;
    601   struct GNUNET_TIME_Timestamp expiration;
    602   enum GNUNET_DB_QueryStatus qs;
    603   struct TALER_MERCHANTDB_LoginTokenP btoken;
    604 
    605   if (NULL == token)
    606   {
    607     *as = TMH_AS_NONE;
    608     return TALER_EC_NONE;
    609   }
    610   if (0 != strncasecmp (token,
    611                         RFC_8959_PREFIX,
    612                         strlen (RFC_8959_PREFIX)))
    613   {
    614     *as = TMH_AS_NONE;
    615     return TALER_EC_NONE;
    616   }
    617   token += strlen (RFC_8959_PREFIX);
    618   if (GNUNET_OK !=
    619       GNUNET_STRINGS_string_to_data (token,
    620                                      strlen (token),
    621                                      &btoken,
    622                                      sizeof (btoken)))
    623   {
    624     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    625                 "Given authorization token `%s' is malformed\n",
    626                 token);
    627     GNUNET_break_op (0);
    628     return TALER_EC_GENERIC_TOKEN_MALFORMED;
    629   }
    630   qs = TMH_db->select_login_token (TMH_db->cls,
    631                                    instance_id,
    632                                    &btoken,
    633                                    &expiration,
    634                                    (uint32_t*) &scope);
    635   if (qs < 0)
    636   {
    637     GNUNET_break (0);
    638     return TALER_EC_GENERIC_DB_FETCH_FAILED;
    639   }
    640   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    641   {
    642     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    643                 "Authorization token `%s' unknown\n",
    644                 token);
    645     return TALER_EC_GENERIC_TOKEN_UNKNOWN;
    646   }
    647   if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
    648   {
    649     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    650                 "Authorization token `%s' expired\n",
    651                 token);
    652     return TALER_EC_GENERIC_TOKEN_EXPIRED;
    653   }
    654   *as = scope;
    655   return TALER_EC_NONE;
    656 }
    657 
    658 
    659 enum GNUNET_GenericReturnValue
    660 TMH_check_auth_config (struct MHD_Connection *connection,
    661                        const json_t *jauth,
    662                        const char **auth_password)
    663 {
    664   bool auth_wellformed = false;
    665   const char *auth_method = json_string_value (json_object_get (jauth,
    666                                                                 "method"));
    667 
    668   *auth_password = NULL;
    669   if (NULL == auth_method)
    670   {
    671     GNUNET_break_op (0);
    672   }
    673   else if ((GNUNET_YES != TMH_strict_v19) &&
    674            (0 == strcmp (auth_method,
    675                          "external")))
    676   {
    677     auth_wellformed = true;
    678   }
    679   else if (GNUNET_YES == TMH_auth_disabled)
    680   {
    681     auth_wellformed = true;
    682   }
    683   else if (0 == strcmp (auth_method,
    684                         "token"))
    685   {
    686     json_t *pw_value;
    687 
    688     pw_value = json_object_get (jauth,
    689                                 "password");
    690     if (NULL == pw_value)
    691     {
    692       pw_value = json_object_get (jauth,
    693                                   "token");
    694     }
    695     if (NULL == pw_value)
    696     {
    697       auth_wellformed = false;
    698       GNUNET_break_op (0);
    699     }
    700     else
    701     {
    702       *auth_password = json_string_value (pw_value);
    703       if (NULL != *auth_password)
    704       {
    705         if (0 == strncasecmp (RFC_8959_PREFIX,
    706                               *auth_password,
    707                               strlen (RFC_8959_PREFIX)))
    708         {
    709           *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
    710         }
    711         auth_wellformed = true;
    712       }
    713     }
    714   }
    715 
    716   if (! auth_wellformed)
    717   {
    718     GNUNET_break_op (0);
    719     return (MHD_YES ==
    720             TALER_MHD_reply_with_error (connection,
    721                                         MHD_HTTP_BAD_REQUEST,
    722                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
    723                                         "bad authentication config"))
    724       ? GNUNET_NO
    725       : GNUNET_SYSERR;
    726   }
    727   return GNUNET_OK;
    728 }
    729 
    730 
    731 void
    732 TMH_uuid_from_string (const char *uuids,
    733                       struct GNUNET_Uuid *uuid)
    734 {
    735   struct GNUNET_HashCode hc;
    736 
    737   GNUNET_CRYPTO_hash (uuids,
    738                       strlen (uuids),
    739                       &hc);
    740   GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
    741   GNUNET_memcpy (uuid,
    742                  &hc,
    743                  sizeof (*uuid));
    744 }
    745 
    746 
    747 /**
    748  * Closure for #trigger_webhook_cb.
    749  *
    750  * @param instance which is the instance we work with
    751  * @param root JSON data to fill into the template
    752  * @param rv, query of the TALER_TEMPLATEING_fill
    753  */
    754 struct Trigger
    755 {
    756   const char *instance;
    757 
    758   const json_t *root;
    759 
    760   enum GNUNET_DB_QueryStatus rv;
    761 
    762 };
    763 
    764 /**
    765  * Typically called by `TMH_trigger_webhook`.
    766  *
    767  * @param[in,out] cls a `struct Trigger` with information about the webhook
    768  * @param webhook_serial reference to the configured webhook template.
    769  * @param event_type is the event/action of the webhook
    770  * @param url to make request to
    771  * @param http_method use for the webhook
    772  * @param header_template of the webhook
    773  * @param body_template of the webhook
    774  */
    775 static void
    776 trigger_webhook_cb (void *cls,
    777                     uint64_t webhook_serial,
    778                     const char *event_type,
    779                     const char *url,
    780                     const char *http_method,
    781                     const char *header_template,
    782                     const char *body_template)
    783 {
    784   struct Trigger *t = cls;
    785   void *header = NULL;
    786   void *body = NULL;
    787   size_t header_size;
    788   size_t body_size;
    789 
    790   if (NULL != header_template)
    791   {
    792     int ret;
    793 
    794     ret = TALER_TEMPLATING_fill (header_template,
    795                                  t->root,
    796                                  &header,
    797                                  &header_size);
    798     if (0 != ret)
    799     {
    800       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    801                   "Failed to expand webhook header template for webhook %llu (%d)\n",
    802                   (unsigned long long) webhook_serial,
    803                   ret);
    804       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
    805       return;
    806     }
    807     /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
    808     GNUNET_assert ('\0' == ((const char *) header)[header_size]);
    809   }
    810   if (NULL != body_template)
    811   {
    812     int ret;
    813 
    814     ret = TALER_TEMPLATING_fill (body_template,
    815                                  t->root,
    816                                  &body,
    817                                  &body_size);
    818     if (0 != ret)
    819     {
    820       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    821                   "Failed to expand webhook body template for webhook %llu (%d)\n",
    822                   (unsigned long long) webhook_serial,
    823                   ret);
    824       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
    825       return;
    826     }
    827     /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
    828     GNUNET_assert ('\0' == ((const char *) body)[body_size]);
    829   }
    830   t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
    831                                           t->instance,
    832                                           webhook_serial,
    833                                           url,
    834                                           http_method,
    835                                           header,
    836                                           body);
    837   if (t->rv > 0)
    838   {
    839     struct GNUNET_DB_EventHeaderP es = {
    840       .size = htons (sizeof(es)),
    841       .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
    842     };
    843     const void *extra = NULL;
    844     size_t extra_size = 0;
    845     TMH_db->event_notify (TMH_db->cls,
    846                           &es,
    847                           &extra,
    848                           extra_size);
    849   }
    850   /* allocated by mustach, hence use free(), not GNUNET_free() */
    851   free (header);
    852   free (body);
    853 }
    854 
    855 
    856 /**
    857  * TMH_trigger_webhook is a function that need to be use when someone
    858  * pay. Merchant need to have a notification.
    859  *
    860  * @param instance that we need to send the webhook as a notification
    861  * @param event of the webhook
    862  * @param args argument of the function
    863  */
    864 enum GNUNET_DB_QueryStatus
    865 TMH_trigger_webhook (const char *instance,
    866                      const char *event,
    867                      const json_t *args)
    868 {
    869   struct Trigger t = {
    870     .instance = instance,
    871     .root = args
    872   };
    873   enum GNUNET_DB_QueryStatus qs;
    874 
    875   qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
    876                                         instance,
    877                                         event,
    878                                         &trigger_webhook_cb,
    879                                         &t);
    880   if (qs < 0)
    881     return qs;
    882   return t.rv;
    883 }
    884 
    885 
    886 enum GNUNET_GenericReturnValue
    887 TMH_base_url_by_connection (struct MHD_Connection *connection,
    888                             const char *instance,
    889                             struct GNUNET_Buffer *buf)
    890 {
    891   const char *host;
    892   const char *forwarded_host;
    893   const char *forwarded_port;
    894   const char *uri_path;
    895 
    896   memset (buf,
    897           0,
    898           sizeof (*buf));
    899   if (NULL != TMH_base_url)
    900   {
    901     GNUNET_buffer_write_str (buf,
    902                              TMH_base_url);
    903   }
    904   else
    905   {
    906     if (GNUNET_YES ==
    907         TALER_mhd_is_https (connection))
    908       GNUNET_buffer_write_str (buf,
    909                                "https://");
    910     else
    911       GNUNET_buffer_write_str (buf,
    912                                "http://");
    913     host = MHD_lookup_connection_value (connection,
    914                                         MHD_HEADER_KIND,
    915                                         MHD_HTTP_HEADER_HOST);
    916     forwarded_host = MHD_lookup_connection_value (connection,
    917                                                   MHD_HEADER_KIND,
    918                                                   "X-Forwarded-Host");
    919     if (NULL != forwarded_host)
    920     {
    921       GNUNET_buffer_write_str (buf,
    922                                forwarded_host);
    923     }
    924     else
    925     {
    926       if (NULL == host)
    927       {
    928         GNUNET_buffer_clear (buf);
    929         GNUNET_break (0);
    930         return GNUNET_SYSERR;
    931       }
    932       GNUNET_buffer_write_str (buf,
    933                                host);
    934     }
    935     forwarded_port = MHD_lookup_connection_value (connection,
    936                                                   MHD_HEADER_KIND,
    937                                                   "X-Forwarded-Port");
    938     if (NULL != forwarded_port)
    939     {
    940       GNUNET_buffer_write_str (buf,
    941                                ":");
    942       GNUNET_buffer_write_str (buf,
    943                                forwarded_port);
    944     }
    945     uri_path = MHD_lookup_connection_value (connection,
    946                                             MHD_HEADER_KIND,
    947                                             "X-Forwarded-Prefix");
    948     if (NULL != uri_path)
    949       GNUNET_buffer_write_path (buf,
    950                                 uri_path);
    951   }
    952   if (0 != strcmp (instance,
    953                    "admin"))
    954   {
    955     GNUNET_buffer_write_path (buf,
    956                               "/instances/");
    957     GNUNET_buffer_write_str (buf,
    958                              instance);
    959   }
    960   return GNUNET_OK;
    961 }
    962 
    963 
    964 enum GNUNET_GenericReturnValue
    965 TMH_taler_uri_by_connection (struct MHD_Connection *connection,
    966                              const char *method,
    967                              const char *instance,
    968                              struct GNUNET_Buffer *buf)
    969 {
    970   const char *host;
    971   const char *forwarded_host;
    972   const char *forwarded_port;
    973   const char *uri_path;
    974 
    975   memset (buf,
    976           0,
    977           sizeof (*buf));
    978   host = MHD_lookup_connection_value (connection,
    979                                       MHD_HEADER_KIND,
    980                                       "Host");
    981   forwarded_host = MHD_lookup_connection_value (connection,
    982                                                 MHD_HEADER_KIND,
    983                                                 "X-Forwarded-Host");
    984   forwarded_port = MHD_lookup_connection_value (connection,
    985                                                 MHD_HEADER_KIND,
    986                                                 "X-Forwarded-Port");
    987   uri_path = MHD_lookup_connection_value (connection,
    988                                           MHD_HEADER_KIND,
    989                                           "X-Forwarded-Prefix");
    990   if (NULL != forwarded_host)
    991     host = forwarded_host;
    992   if (NULL == host)
    993   {
    994     GNUNET_break (0);
    995     return GNUNET_SYSERR;
    996   }
    997   GNUNET_buffer_write_str (buf,
    998                            "taler");
    999   if (GNUNET_NO == TALER_mhd_is_https (connection))
   1000     GNUNET_buffer_write_str (buf,
   1001                              "+http");
   1002   GNUNET_buffer_write_str (buf,
   1003                            "://");
   1004   GNUNET_buffer_write_str (buf,
   1005                            method);
   1006   GNUNET_buffer_write_str (buf,
   1007                            "/");
   1008   GNUNET_buffer_write_str (buf,
   1009                            host);
   1010   if (NULL != forwarded_port)
   1011   {
   1012     GNUNET_buffer_write_str (buf,
   1013                              ":");
   1014     GNUNET_buffer_write_str (buf,
   1015                              forwarded_port);
   1016   }
   1017   if (NULL != uri_path)
   1018     GNUNET_buffer_write_path (buf,
   1019                               uri_path);
   1020   if (0 != strcmp ("admin",
   1021                    instance))
   1022   {
   1023     GNUNET_buffer_write_path (buf,
   1024                               "instances");
   1025     GNUNET_buffer_write_path (buf,
   1026                               instance);
   1027   }
   1028   return GNUNET_OK;
   1029 }
   1030 
   1031 
   1032 /**
   1033  * Closure for #add_matching_account().
   1034  */
   1035 struct ExchangeMatchContext
   1036 {
   1037   /**
   1038    * Wire method to match, NULL for all.
   1039    */
   1040   const char *wire_method;
   1041 
   1042   /**
   1043    * Array of accounts to return.
   1044    */
   1045   json_t *accounts;
   1046 };
   1047 
   1048 
   1049 /**
   1050  * If the given account is feasible, add it to the array
   1051  * of accounts we return.
   1052  *
   1053  * @param cls a `struct PostReserveContext`
   1054  * @param payto_uri URI of the account
   1055  * @param conversion_url URL of a conversion service
   1056  * @param debit_restrictions restrictions for debits from account
   1057  * @param credit_restrictions restrictions for credits to account
   1058  * @param master_sig signature affirming the account
   1059  */
   1060 static void
   1061 add_matching_account (
   1062   void *cls,
   1063   struct TALER_FullPayto payto_uri,
   1064   const char *conversion_url,
   1065   const json_t *debit_restrictions,
   1066   const json_t *credit_restrictions,
   1067   const struct TALER_MasterSignatureP *master_sig)
   1068 {
   1069   struct ExchangeMatchContext *rc = cls;
   1070   char *method;
   1071 
   1072   method = TALER_payto_get_method (payto_uri.full_payto);
   1073   if ( (NULL == rc->wire_method) ||
   1074        (0 == strcmp (method,
   1075                      rc->wire_method)) )
   1076   {
   1077     json_t *acc;
   1078 
   1079     acc = GNUNET_JSON_PACK (
   1080       TALER_JSON_pack_full_payto ("payto_uri",
   1081                                   payto_uri),
   1082       GNUNET_JSON_pack_data_auto ("master_sig",
   1083                                   master_sig),
   1084       GNUNET_JSON_pack_allow_null (
   1085         GNUNET_JSON_pack_string ("conversion_url",
   1086                                  conversion_url)),
   1087       GNUNET_JSON_pack_array_incref ("credit_restrictions",
   1088                                      (json_t *) credit_restrictions),
   1089       GNUNET_JSON_pack_array_incref ("debit_restrictions",
   1090                                      (json_t *) debit_restrictions)
   1091       );
   1092     GNUNET_assert (0 ==
   1093                    json_array_append_new (rc->accounts,
   1094                                           acc));
   1095   }
   1096   GNUNET_free (method);
   1097 }
   1098 
   1099 
   1100 /**
   1101  * Return JSON array with all of the exchange accounts
   1102  * that support the given @a wire_method.
   1103  *
   1104  * @param master_pub master public key to match exchange by
   1105  * @param wire_method NULL for any
   1106  * @return JSON array with information about all matching accounts
   1107  */
   1108 json_t *
   1109 TMH_exchange_accounts_by_method (
   1110   const struct TALER_MasterPublicKeyP *master_pub,
   1111   const char *wire_method)
   1112 {
   1113   struct ExchangeMatchContext emc = {
   1114     .wire_method = wire_method,
   1115     .accounts = json_array ()
   1116   };
   1117   enum GNUNET_DB_QueryStatus qs;
   1118 
   1119   GNUNET_assert (NULL != emc.accounts);
   1120   qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
   1121                                             master_pub,
   1122                                             &add_matching_account,
   1123                                             &emc);
   1124   if (qs < 0)
   1125   {
   1126     json_decref (emc.accounts);
   1127     return NULL;
   1128   }
   1129   return emc.accounts;
   1130 }
   1131 
   1132 
   1133 char *
   1134 TMH_make_order_status_url (struct MHD_Connection *con,
   1135                            const char *order_id,
   1136                            const char *session_id,
   1137                            const char *instance_id,
   1138                            struct TALER_ClaimTokenP *claim_token,
   1139                            struct TALER_PrivateContractHashP *h_contract)
   1140 {
   1141   struct GNUNET_Buffer buf;
   1142   /* Number of query parameters written so far */
   1143   unsigned int num_qp = 0;
   1144 
   1145   GNUNET_assert (NULL != instance_id);
   1146   GNUNET_assert (NULL != order_id);
   1147   if (GNUNET_OK !=
   1148       TMH_base_url_by_connection (con,
   1149                                   instance_id,
   1150                                   &buf))
   1151   {
   1152     GNUNET_break (0);
   1153     return NULL;
   1154   }
   1155   GNUNET_buffer_write_path (&buf,
   1156                             "/orders");
   1157   GNUNET_buffer_write_path (&buf,
   1158                             order_id);
   1159   if ( (NULL != claim_token) &&
   1160        (! GNUNET_is_zero (claim_token)) )
   1161   {
   1162     /* 'token=' for human readability */
   1163     GNUNET_buffer_write_str (&buf,
   1164                              "?token=");
   1165     GNUNET_buffer_write_data_encoded (&buf,
   1166                                       (char *) claim_token,
   1167                                       sizeof (*claim_token));
   1168     num_qp++;
   1169   }
   1170 
   1171   if (NULL != session_id)
   1172   {
   1173     if (num_qp > 0)
   1174       GNUNET_buffer_write_str (&buf,
   1175                                "&session_id=");
   1176     else
   1177       GNUNET_buffer_write_str (&buf,
   1178                                "?session_id=");
   1179     GNUNET_buffer_write_str (&buf,
   1180                              session_id);
   1181     num_qp++;
   1182   }
   1183 
   1184   if (NULL != h_contract)
   1185   {
   1186     if (num_qp > 0)
   1187       GNUNET_buffer_write_str (&buf,
   1188                                "&h_contract=");
   1189     else
   1190       GNUNET_buffer_write_str (&buf,
   1191                                "?h_contract=");
   1192     GNUNET_buffer_write_data_encoded (&buf,
   1193                                       (char *) h_contract,
   1194                                       sizeof (*h_contract));
   1195   }
   1196 
   1197   return GNUNET_buffer_reap_str (&buf);
   1198 }
   1199 
   1200 
   1201 char *
   1202 TMH_make_taler_pay_uri (struct MHD_Connection *con,
   1203                         const char *order_id,
   1204                         const char *session_id,
   1205                         const char *instance_id,
   1206                         struct TALER_ClaimTokenP *claim_token)
   1207 {
   1208   struct GNUNET_Buffer buf;
   1209 
   1210   GNUNET_assert (NULL != instance_id);
   1211   GNUNET_assert (NULL != order_id);
   1212   if (GNUNET_OK !=
   1213       TMH_taler_uri_by_connection (con,
   1214                                    "pay",
   1215                                    instance_id,
   1216                                    &buf))
   1217   {
   1218     GNUNET_break (0);
   1219     return NULL;
   1220   }
   1221   GNUNET_buffer_write_path (&buf,
   1222                             order_id);
   1223   GNUNET_buffer_write_path (&buf,
   1224                             (NULL == session_id)
   1225                             ? ""
   1226                             : session_id);
   1227   if ( (NULL != claim_token) &&
   1228        (! GNUNET_is_zero (claim_token)))
   1229   {
   1230     /* Just 'c=' because this goes into QR
   1231        codes, so this is more compact. */
   1232     GNUNET_buffer_write_str (&buf,
   1233                              "?c=");
   1234     GNUNET_buffer_write_data_encoded (&buf,
   1235                                       (char *) claim_token,
   1236                                       sizeof (struct TALER_ClaimTokenP));
   1237   }
   1238 
   1239   return GNUNET_buffer_reap_str (&buf);
   1240 }