merchant

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

taler-merchant-httpd_helper.c (35888B)


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