merchant

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

taler-merchant-httpd_helper.c (36402B)


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