LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 237 400 59.2 %
Date: 2025-08-28 06:06:54 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          98 : TMH_location_object_valid (const json_t *location)
     205             : {
     206          98 :   const char *country = NULL;
     207          98 :   const char *subdivision = NULL;
     208          98 :   const char *district = NULL;
     209          98 :   const char *town = NULL;
     210          98 :   const char *town_loc = NULL;
     211          98 :   const char *postcode = NULL;
     212          98 :   const char *street = NULL;
     213          98 :   const char *building = NULL;
     214          98 :   const char *building_no = NULL;
     215          98 :   const json_t *lines = NULL;
     216             :   struct GNUNET_JSON_Specification spec[] = {
     217          98 :     GNUNET_JSON_spec_mark_optional (
     218             :       GNUNET_JSON_spec_string ("country",
     219             :                                &country),
     220             :       NULL),
     221          98 :     GNUNET_JSON_spec_mark_optional (
     222             :       GNUNET_JSON_spec_string ("country_subdivision",
     223             :                                &subdivision),
     224             :       NULL),
     225          98 :     GNUNET_JSON_spec_mark_optional (
     226             :       GNUNET_JSON_spec_string ("district",
     227             :                                &district),
     228             :       NULL),
     229          98 :     GNUNET_JSON_spec_mark_optional (
     230             :       GNUNET_JSON_spec_string ("town",
     231             :                                &town),
     232             :       NULL),
     233          98 :     GNUNET_JSON_spec_mark_optional (
     234             :       GNUNET_JSON_spec_string ("town_location",
     235             :                                &town_loc),
     236             :       NULL),
     237          98 :     GNUNET_JSON_spec_mark_optional (
     238             :       GNUNET_JSON_spec_string ("post_code",
     239             :                                &postcode),
     240             :       NULL),
     241          98 :     GNUNET_JSON_spec_mark_optional (
     242             :       GNUNET_JSON_spec_string ("street",
     243             :                                &street),
     244             :       NULL),
     245          98 :     GNUNET_JSON_spec_mark_optional (
     246             :       GNUNET_JSON_spec_string ("building_name",
     247             :                                &building),
     248             :       NULL),
     249          98 :     GNUNET_JSON_spec_mark_optional (
     250             :       GNUNET_JSON_spec_string ("building_number",
     251             :                                &building_no),
     252             :       NULL),
     253          98 :     GNUNET_JSON_spec_mark_optional (
     254             :       GNUNET_JSON_spec_array_const ("address_lines",
     255             :                                     &lines),
     256             :       NULL),
     257          98 :     GNUNET_JSON_spec_end ()
     258             :   };
     259             :   const char *ename;
     260             :   unsigned int eline;
     261             : 
     262          98 :   if (GNUNET_OK !=
     263          98 :       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          98 :   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          98 :   return true;
     290             : }
     291             : 
     292             : 
     293             : bool
     294          74 : TMH_products_array_valid (const json_t *products)
     295             : {
     296             :   const json_t *product;
     297             :   size_t idx;
     298          74 :   bool valid = true;
     299             : 
     300          74 :   if (! json_is_array (products))
     301             :   {
     302           0 :     GNUNET_break_op (0);
     303           0 :     return false;
     304             :   }
     305          97 :   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          74 :   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          21 : 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          21 :   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          21 :   wm = GNUNET_new (struct TMH_WireMethod);
     511          21 :   if (NULL != credit_facade_url)
     512             :     wm->credit_facade_url
     513           2 :       = GNUNET_strdup (credit_facade_url);
     514          21 :   if (NULL != credit_facade_credentials)
     515             :     wm->credit_facade_credentials
     516           2 :       = json_incref ((json_t*) credit_facade_credentials);
     517          21 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     518          21 :                               &wm->wire_salt,
     519             :                               sizeof (wm->wire_salt));
     520             :   wm->payto_uri.full_payto
     521          21 :     = GNUNET_strdup (payto_uri.full_payto);
     522          21 :   TALER_merchant_wire_signature_hash (payto_uri,
     523          21 :                                       &wm->wire_salt,
     524             :                                       &wm->h_wire);
     525             :   wm->wire_method
     526          21 :     = TALER_payto_get_method (payto_uri.full_payto);
     527          21 :   wm->active = true;
     528          21 :   return wm;
     529             : }
     530             : 
     531             : 
     532             : enum TALER_ErrorCode
     533          49 : 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          49 :   if (NULL == token)
     543             :   {
     544           6 :     *as = TMH_AS_NONE;
     545           6 :     return TALER_EC_NONE;
     546             :   }
     547          43 :   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          43 :   token += strlen (RFC_8959_PREFIX);
     555          43 :   if (GNUNET_OK !=
     556          43 :       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          43 :   qs = TMH_db->select_login_token (TMH_db->cls,
     568             :                                    instance_id,
     569             :                                    &btoken,
     570             :                                    &expiration,
     571             :                                    (uint32_t*) &scope);
     572          43 :   if (qs < 0)
     573             :   {
     574           0 :     GNUNET_break (0);
     575           0 :     return TALER_EC_GENERIC_DB_FETCH_FAILED;
     576             :   }
     577          43 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     578             :   {
     579           9 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     580             :                 "Authorization token `%s' unknown\n",
     581             :                 token);
     582           9 :     return TALER_EC_GENERIC_TOKEN_UNKNOWN;
     583             :   }
     584          34 :   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          34 :   *as = scope;
     592          34 :   return TALER_EC_NONE;
     593             : }
     594             : 
     595             : 
     596             : enum GNUNET_GenericReturnValue
     597          42 : TMH_check_auth_config (struct MHD_Connection *connection,
     598             :                        const json_t *jauth,
     599             :                        const char **auth_password)
     600             : {
     601          42 :   bool auth_wellformed = false;
     602          42 :   const char *auth_method = json_string_value (json_object_get (jauth,
     603             :                                                                 "method"));
     604             : 
     605          42 :   *auth_password = NULL;
     606          42 :   if (NULL == auth_method)
     607             :   {
     608           0 :     GNUNET_break_op (0);
     609             :   }
     610          42 :   else if ((GNUNET_YES != TMH_strict_v19) &&
     611          42 :            (0 == strcmp (auth_method,
     612             :                          "external")))
     613             :   {
     614          29 :     auth_wellformed = true;
     615             :   }
     616          13 :   else if (GNUNET_YES == TMH_auth_disabled)
     617             :   {
     618           0 :     auth_wellformed = true;
     619             :   }
     620          13 :   else if (0 == strcmp (auth_method,
     621             :                         "token"))
     622             :   {
     623             :     json_t *pw_value;
     624             : 
     625          13 :     pw_value = json_object_get (jauth,
     626             :                                 "password");
     627          13 :     if (NULL == pw_value)
     628             :     {
     629           0 :       pw_value = json_object_get (jauth,
     630             :                                   "token");
     631             :     }
     632          13 :     if (NULL == pw_value)
     633             :     {
     634           0 :       auth_wellformed = false;
     635           0 :       GNUNET_break_op (0);
     636             :     }
     637             :     else
     638             :     {
     639          13 :       *auth_password = json_string_value (pw_value);
     640          13 :       if (NULL != *auth_password)
     641             :       {
     642          13 :         if (0 == strncasecmp (RFC_8959_PREFIX,
     643             :                               *auth_password,
     644             :                               strlen (RFC_8959_PREFIX)))
     645             :         {
     646           4 :           *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
     647             :         }
     648          13 :         auth_wellformed = true;
     649             :       }
     650             :     }
     651             :   }
     652             : 
     653          42 :   if (! auth_wellformed)
     654             :   {
     655           0 :     GNUNET_break_op (0);
     656             :     return (MHD_YES ==
     657           0 :             TALER_MHD_reply_with_error (connection,
     658             :                                         MHD_HTTP_BAD_REQUEST,
     659             :                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
     660             :                                         "bad authentication config"))
     661             :       ? GNUNET_NO
     662           0 :       : GNUNET_SYSERR;
     663             :   }
     664          42 :   return GNUNET_OK;
     665             : }
     666             : 
     667             : 
     668             : void
     669          13 : TMH_uuid_from_string (const char *uuids,
     670             :                       struct GNUNET_Uuid *uuid)
     671             : {
     672             :   struct GNUNET_HashCode hc;
     673             : 
     674          13 :   GNUNET_CRYPTO_hash (uuids,
     675             :                       strlen (uuids),
     676             :                       &hc);
     677             :   GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
     678          13 :   GNUNET_memcpy (uuid,
     679             :                  &hc,
     680             :                  sizeof (*uuid));
     681          13 : }
     682             : 
     683             : 
     684             : /**
     685             :  * Closure for #trigger_webhook_cb.
     686             :  *
     687             :  * @param instance which is the instance we work with
     688             :  * @param root JSON data to fill into the template
     689             :  * @param rv, query of the TALER_TEMPLATEING_fill
     690             :  */
     691             : struct Trigger
     692             : {
     693             :   const char *instance;
     694             : 
     695             :   const json_t *root;
     696             : 
     697             :   enum GNUNET_DB_QueryStatus rv;
     698             : 
     699             : };
     700             : 
     701             : /**
     702             :  * Typically called by `TMH_trigger_webhook`.
     703             :  *
     704             :  * @param[in,out] cls a `struct Trigger` with information about the webhook
     705             :  * @param webhook_serial reference to the configured webhook template.
     706             :  * @param event_type is the event/action of the webhook
     707             :  * @param url to make request to
     708             :  * @param http_method use for the webhook
     709             :  * @param header_template of the webhook
     710             :  * @param body_template of the webhook
     711             :  */
     712             : static void
     713           2 : trigger_webhook_cb (void *cls,
     714             :                     uint64_t webhook_serial,
     715             :                     const char *event_type,
     716             :                     const char *url,
     717             :                     const char *http_method,
     718             :                     const char *header_template,
     719             :                     const char *body_template)
     720             : {
     721           2 :   struct Trigger *t = cls;
     722           2 :   void *header = NULL;
     723           2 :   void *body = NULL;
     724             :   size_t header_size;
     725             :   size_t body_size;
     726             : 
     727           2 :   if (NULL != header_template)
     728             :   {
     729             :     int ret;
     730             : 
     731           2 :     ret = TALER_TEMPLATING_fill (header_template,
     732             :                                  t->root,
     733             :                                  &header,
     734             :                                  &header_size);
     735           2 :     if (0 != ret)
     736             :     {
     737           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     738             :                   "Failed to expand webhook header template for webhook %llu (%d)\n",
     739             :                   (unsigned long long) webhook_serial,
     740             :                   ret);
     741           0 :       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
     742           0 :       return;
     743             :     }
     744             :     /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
     745           2 :     GNUNET_assert ('\0' == ((const char *) header)[header_size]);
     746             :   }
     747           2 :   if (NULL != body_template)
     748             :   {
     749             :     int ret;
     750           2 :     ret = TALER_TEMPLATING_fill (body_template,
     751             :                                  t->root,
     752             :                                  &body,
     753             :                                  &body_size);
     754           2 :     if (0 != ret)
     755             :     {
     756           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     757             :                   "Failed to expand webhook body template for webhook %llu (%d)\n",
     758             :                   (unsigned long long) webhook_serial,
     759             :                   ret);
     760           0 :       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
     761           0 :       return;
     762             :     }
     763             :     /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
     764           2 :     GNUNET_assert ('\0' == ((const char *) body)[body_size]);
     765             :   }
     766           2 :   t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
     767             :                                           t->instance,
     768             :                                           webhook_serial,
     769             :                                           url,
     770             :                                           http_method,
     771             :                                           header,
     772             :                                           body);
     773           2 :   if (t->rv > 0)
     774             :   {
     775           2 :     struct GNUNET_DB_EventHeaderP es = {
     776           2 :       .size = htons (sizeof(es)),
     777           2 :       .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
     778             :     };
     779           2 :     const void *extra = NULL;
     780           2 :     size_t extra_size = 0;
     781           2 :     TMH_db->event_notify (TMH_db->cls,
     782             :                           &es,
     783             :                           &extra,
     784             :                           extra_size);
     785             :   }
     786           2 :   free (header);
     787           2 :   free (body);
     788             : }
     789             : 
     790             : 
     791             : /**
     792             :  * TMH_trigger_webhook is a function that need to be use when someone
     793             :  * pay. Merchant need to have a notification.
     794             :  *
     795             :  * @param instance that we need to send the webhook as a notification
     796             :  * @param event of the webhook
     797             :  * @param args argument of the function
     798             :  */
     799             : enum GNUNET_DB_QueryStatus
     800          95 : TMH_trigger_webhook (const char *instance,
     801             :                      const char *event,
     802             :                      const json_t *args)
     803             : {
     804          95 :   struct Trigger t = {
     805             :     .instance = instance,
     806             :     .root = args
     807             :   };
     808             :   enum GNUNET_DB_QueryStatus qs;
     809             : 
     810          95 :   qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
     811             :                                         instance,
     812             :                                         event,
     813             :                                         &trigger_webhook_cb,
     814             :                                         &t);
     815          95 :   if (qs < 0)
     816           0 :     return qs;
     817          95 :   return t.rv;
     818             : }
     819             : 
     820             : 
     821             : enum GNUNET_GenericReturnValue
     822         146 : TMH_base_url_by_connection (struct MHD_Connection *connection,
     823             :                             const char *instance,
     824             :                             struct GNUNET_Buffer *buf)
     825             : {
     826             :   const char *host;
     827             :   const char *forwarded_host;
     828             :   const char *forwarded_port;
     829             :   const char *uri_path;
     830             : 
     831         146 :   memset (buf,
     832             :           0,
     833             :           sizeof (*buf));
     834         146 :   if (NULL != TMH_base_url)
     835             :   {
     836           0 :     GNUNET_buffer_write_str (buf,
     837             :                              TMH_base_url);
     838             :   }
     839             :   else
     840             :   {
     841         146 :     if (GNUNET_YES ==
     842         146 :         TALER_mhd_is_https (connection))
     843           0 :       GNUNET_buffer_write_str (buf,
     844             :                                "https://");
     845             :     else
     846         146 :       GNUNET_buffer_write_str (buf,
     847             :                                "http://");
     848         146 :     host = MHD_lookup_connection_value (connection,
     849             :                                         MHD_HEADER_KIND,
     850             :                                         MHD_HTTP_HEADER_HOST);
     851         146 :     forwarded_host = MHD_lookup_connection_value (connection,
     852             :                                                   MHD_HEADER_KIND,
     853             :                                                   "X-Forwarded-Host");
     854         146 :     if (NULL != forwarded_host)
     855             :     {
     856           0 :       GNUNET_buffer_write_str (buf,
     857             :                                forwarded_host);
     858             :     }
     859             :     else
     860             :     {
     861         146 :       if (NULL == host)
     862             :       {
     863           0 :         GNUNET_buffer_clear (buf);
     864           0 :         GNUNET_break (0);
     865           0 :         return GNUNET_SYSERR;
     866             :       }
     867         146 :       GNUNET_buffer_write_str (buf,
     868             :                                host);
     869             :     }
     870         146 :     forwarded_port = MHD_lookup_connection_value (connection,
     871             :                                                   MHD_HEADER_KIND,
     872             :                                                   "X-Forwarded-Port");
     873         146 :     if (NULL != forwarded_port)
     874             :     {
     875           0 :       GNUNET_buffer_write_str (buf,
     876             :                                ":");
     877           0 :       GNUNET_buffer_write_str (buf,
     878             :                                forwarded_port);
     879             :     }
     880         146 :     uri_path = MHD_lookup_connection_value (connection,
     881             :                                             MHD_HEADER_KIND,
     882             :                                             "X-Forwarded-Prefix");
     883         146 :     if (NULL != uri_path)
     884           0 :       GNUNET_buffer_write_path (buf,
     885             :                                 uri_path);
     886             :   }
     887         146 :   if (0 != strcmp (instance,
     888             :                    "admin"))
     889             :   {
     890          13 :     GNUNET_buffer_write_path (buf,
     891             :                               "/instances/");
     892          13 :     GNUNET_buffer_write_str (buf,
     893             :                              instance);
     894             :   }
     895         146 :   return GNUNET_OK;
     896             : }
     897             : 
     898             : 
     899             : enum GNUNET_GenericReturnValue
     900          35 : TMH_taler_uri_by_connection (struct MHD_Connection *connection,
     901             :                              const char *method,
     902             :                              const char *instance,
     903             :                              struct GNUNET_Buffer *buf)
     904             : {
     905             :   const char *host;
     906             :   const char *forwarded_host;
     907             :   const char *forwarded_port;
     908             :   const char *uri_path;
     909             : 
     910          35 :   memset (buf,
     911             :           0,
     912             :           sizeof (*buf));
     913          35 :   host = MHD_lookup_connection_value (connection,
     914             :                                       MHD_HEADER_KIND,
     915             :                                       "Host");
     916          35 :   forwarded_host = MHD_lookup_connection_value (connection,
     917             :                                                 MHD_HEADER_KIND,
     918             :                                                 "X-Forwarded-Host");
     919          35 :   forwarded_port = MHD_lookup_connection_value (connection,
     920             :                                                 MHD_HEADER_KIND,
     921             :                                                 "X-Forwarded-Port");
     922          35 :   uri_path = MHD_lookup_connection_value (connection,
     923             :                                           MHD_HEADER_KIND,
     924             :                                           "X-Forwarded-Prefix");
     925          35 :   if (NULL != forwarded_host)
     926           0 :     host = forwarded_host;
     927          35 :   if (NULL == host)
     928             :   {
     929           0 :     GNUNET_break (0);
     930           0 :     return GNUNET_SYSERR;
     931             :   }
     932          35 :   GNUNET_buffer_write_str (buf,
     933             :                            "taler");
     934          35 :   if (GNUNET_NO == TALER_mhd_is_https (connection))
     935          35 :     GNUNET_buffer_write_str (buf,
     936             :                              "+http");
     937          35 :   GNUNET_buffer_write_str (buf,
     938             :                            "://");
     939          35 :   GNUNET_buffer_write_str (buf,
     940             :                            method);
     941          35 :   GNUNET_buffer_write_str (buf,
     942             :                            "/");
     943          35 :   GNUNET_buffer_write_str (buf,
     944             :                            host);
     945          35 :   if (NULL != forwarded_port)
     946             :   {
     947           0 :     GNUNET_buffer_write_str (buf,
     948             :                              ":");
     949           0 :     GNUNET_buffer_write_str (buf,
     950             :                              forwarded_port);
     951             :   }
     952          35 :   if (NULL != uri_path)
     953           0 :     GNUNET_buffer_write_path (buf,
     954             :                               uri_path);
     955          35 :   if (0 != strcmp ("admin",
     956             :                    instance))
     957             :   {
     958           3 :     GNUNET_buffer_write_path (buf,
     959             :                               "instances");
     960           3 :     GNUNET_buffer_write_path (buf,
     961             :                               instance);
     962             :   }
     963          35 :   return GNUNET_OK;
     964             : }
     965             : 
     966             : 
     967             : /**
     968             :  * Closure for #add_matching_account().
     969             :  */
     970             : struct ExchangeMatchContext
     971             : {
     972             :   /**
     973             :    * Wire method to match, NULL for all.
     974             :    */
     975             :   const char *wire_method;
     976             : 
     977             :   /**
     978             :    * Array of accounts to return.
     979             :    */
     980             :   json_t *accounts;
     981             : };
     982             : 
     983             : 
     984             : /**
     985             :  * If the given account is feasible, add it to the array
     986             :  * of accounts we return.
     987             :  *
     988             :  * @param cls a `struct PostReserveContext`
     989             :  * @param payto_uri URI of the account
     990             :  * @param conversion_url URL of a conversion service
     991             :  * @param debit_restrictions restrictions for debits from account
     992             :  * @param credit_restrictions restrictions for credits to account
     993             :  * @param master_sig signature affirming the account
     994             :  */
     995             : static void
     996           0 : add_matching_account (
     997             :   void *cls,
     998             :   struct TALER_FullPayto payto_uri,
     999             :   const char *conversion_url,
    1000             :   const json_t *debit_restrictions,
    1001             :   const json_t *credit_restrictions,
    1002             :   const struct TALER_MasterSignatureP *master_sig)
    1003             : {
    1004           0 :   struct ExchangeMatchContext *rc = cls;
    1005             :   char *method;
    1006             : 
    1007           0 :   method = TALER_payto_get_method (payto_uri.full_payto);
    1008           0 :   if ( (NULL == rc->wire_method) ||
    1009           0 :        (0 == strcmp (method,
    1010             :                      rc->wire_method)) )
    1011             :   {
    1012             :     json_t *acc;
    1013             : 
    1014           0 :     acc = GNUNET_JSON_PACK (
    1015             :       TALER_JSON_pack_full_payto ("payto_uri",
    1016             :                                   payto_uri),
    1017             :       GNUNET_JSON_pack_data_auto ("master_sig",
    1018             :                                   master_sig),
    1019             :       GNUNET_JSON_pack_allow_null (
    1020             :         GNUNET_JSON_pack_string ("conversion_url",
    1021             :                                  conversion_url)),
    1022             :       GNUNET_JSON_pack_array_incref ("credit_restrictions",
    1023             :                                      (json_t *) credit_restrictions),
    1024             :       GNUNET_JSON_pack_array_incref ("debit_restrictions",
    1025             :                                      (json_t *) debit_restrictions)
    1026             :       );
    1027           0 :     GNUNET_assert (0 ==
    1028             :                    json_array_append_new (rc->accounts,
    1029             :                                           acc));
    1030             :   }
    1031           0 :   GNUNET_free (method);
    1032           0 : }
    1033             : 
    1034             : 
    1035             : /**
    1036             :  * Return JSON array with all of the exchange accounts
    1037             :  * that support the given @a wire_method.
    1038             :  *
    1039             :  * @param master_pub master public key to match exchange by
    1040             :  * @param wire_method NULL for any
    1041             :  * @return JSON array with information about all matching accounts
    1042             :  */
    1043             : json_t *
    1044           0 : TMH_exchange_accounts_by_method (
    1045             :   const struct TALER_MasterPublicKeyP *master_pub,
    1046             :   const char *wire_method)
    1047             : {
    1048           0 :   struct ExchangeMatchContext emc = {
    1049             :     .wire_method = wire_method,
    1050           0 :     .accounts = json_array ()
    1051             :   };
    1052             :   enum GNUNET_DB_QueryStatus qs;
    1053             : 
    1054           0 :   GNUNET_assert (NULL != emc.accounts);
    1055           0 :   qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
    1056             :                                             master_pub,
    1057             :                                             &add_matching_account,
    1058             :                                             &emc);
    1059           0 :   if (qs < 0)
    1060             :   {
    1061           0 :     json_decref (emc.accounts);
    1062           0 :     return NULL;
    1063             :   }
    1064           0 :   return emc.accounts;
    1065             : }
    1066             : 
    1067             : 
    1068             : char *
    1069          75 : TMH_make_order_status_url (struct MHD_Connection *con,
    1070             :                            const char *order_id,
    1071             :                            const char *session_id,
    1072             :                            const char *instance_id,
    1073             :                            struct TALER_ClaimTokenP *claim_token,
    1074             :                            struct TALER_PrivateContractHashP *h_contract)
    1075             : {
    1076             :   struct GNUNET_Buffer buf;
    1077             :   /* Number of query parameters written so far */
    1078          75 :   unsigned int num_qp = 0;
    1079             : 
    1080          75 :   GNUNET_assert (NULL != instance_id);
    1081          75 :   GNUNET_assert (NULL != order_id);
    1082          75 :   if (GNUNET_OK !=
    1083          75 :       TMH_base_url_by_connection (con,
    1084             :                                   instance_id,
    1085             :                                   &buf))
    1086             :   {
    1087           0 :     GNUNET_break (0);
    1088           0 :     return NULL;
    1089             :   }
    1090          75 :   GNUNET_buffer_write_path (&buf,
    1091             :                             "/orders");
    1092          75 :   GNUNET_buffer_write_path (&buf,
    1093             :                             order_id);
    1094          75 :   if ( (NULL != claim_token) &&
    1095          75 :        (! GNUNET_is_zero (claim_token)) )
    1096             :   {
    1097             :     /* 'token=' for human readability */
    1098          61 :     GNUNET_buffer_write_str (&buf,
    1099             :                              "?token=");
    1100          61 :     GNUNET_buffer_write_data_encoded (&buf,
    1101             :                                       (char *) claim_token,
    1102             :                                       sizeof (*claim_token));
    1103          61 :     num_qp++;
    1104             :   }
    1105             : 
    1106          75 :   if (NULL != session_id)
    1107             :   {
    1108          16 :     if (num_qp > 0)
    1109          10 :       GNUNET_buffer_write_str (&buf,
    1110             :                                "&session_id=");
    1111             :     else
    1112           6 :       GNUNET_buffer_write_str (&buf,
    1113             :                                "?session_id=");
    1114          16 :     GNUNET_buffer_write_str (&buf,
    1115             :                              session_id);
    1116          16 :     num_qp++;
    1117             :   }
    1118             : 
    1119          75 :   if (NULL != h_contract)
    1120             :   {
    1121           2 :     if (num_qp > 0)
    1122           2 :       GNUNET_buffer_write_str (&buf,
    1123             :                                "&h_contract=");
    1124             :     else
    1125           0 :       GNUNET_buffer_write_str (&buf,
    1126             :                                "?h_contract=");
    1127           2 :     GNUNET_buffer_write_data_encoded (&buf,
    1128             :                                       (char *) h_contract,
    1129             :                                       sizeof (*h_contract));
    1130             :   }
    1131             : 
    1132          75 :   return GNUNET_buffer_reap_str (&buf);
    1133             : }
    1134             : 
    1135             : 
    1136             : char *
    1137          29 : TMH_make_taler_pay_uri (struct MHD_Connection *con,
    1138             :                         const char *order_id,
    1139             :                         const char *session_id,
    1140             :                         const char *instance_id,
    1141             :                         struct TALER_ClaimTokenP *claim_token)
    1142             : {
    1143             :   struct GNUNET_Buffer buf;
    1144             : 
    1145          29 :   GNUNET_assert (NULL != instance_id);
    1146          29 :   GNUNET_assert (NULL != order_id);
    1147          29 :   if (GNUNET_OK !=
    1148          29 :       TMH_taler_uri_by_connection (con,
    1149             :                                    "pay",
    1150             :                                    instance_id,
    1151             :                                    &buf))
    1152             :   {
    1153           0 :     GNUNET_break (0);
    1154           0 :     return NULL;
    1155             :   }
    1156          29 :   GNUNET_buffer_write_path (&buf,
    1157             :                             order_id);
    1158          29 :   GNUNET_buffer_write_path (&buf,
    1159             :                             (NULL == session_id)
    1160             :                             ? ""
    1161             :                             : session_id);
    1162          29 :   if ( (NULL != claim_token) &&
    1163          29 :        (! GNUNET_is_zero (claim_token)))
    1164             :   {
    1165             :     /* Just 'c=' because this goes into QR
    1166             :        codes, so this is more compact. */
    1167          17 :     GNUNET_buffer_write_str (&buf,
    1168             :                              "?c=");
    1169          17 :     GNUNET_buffer_write_data_encoded (&buf,
    1170             :                                       (char *) claim_token,
    1171             :                                       sizeof (struct TALER_ClaimTokenP));
    1172             :   }
    1173             : 
    1174          29 :   return GNUNET_buffer_reap_str (&buf);
    1175             : }

Generated by: LCOV version 1.16