LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_helper.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 61.3 % 576 353
Test Date: 2025-12-16 19:21:50 Functions: 79.2 % 24 19

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

Generated by: LCOV version 2.0-1