LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 59.2 % 400 237
Test Date: 2025-10-31 14:20:14 Functions: 78.9 % 19 15

            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 2.0-1