LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 235 397 59.2 %
Date: 2025-06-23 16:22:09 Functions: 15 19 78.9 %

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

Generated by: LCOV version 1.16