LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 60.7 % 486 295
Test Date: 2026-09-11 17:45:24 Functions: 80.8 % 26 21

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

Generated by: LCOV version 2.0-1