LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 60.5 % 420 254
Test Date: 2026-01-01 16:44:56 Functions: 73.7 % 19 14

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

Generated by: LCOV version 2.0-1