LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-management-instances.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 50.2 % 293 147
Test Date: 2026-09-04 23:42:01 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2020-2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify
       6              :   it under the terms of the GNU Affero General Public License as
       7              :   published by the Free Software Foundation; either version 3,
       8              :   or (at your option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful, but
      11              :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13              :   GNU General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : 
      20              : /**
      21              :  * @file src/backend/taler-merchant-httpd_post-management-instances.c
      22              :  * @brief implementing POST /instances request handling
      23              :  * @author Christian Grothoff
      24              :  */
      25              : #include "platform.h"
      26              : #include "taler-merchant-httpd_post-management-instances.h"
      27              : #include "taler-merchant-httpd_helper.h"
      28              : #include "taler-merchant-httpd.h"
      29              : #include "taler-merchant-httpd_auth.h"
      30              : #include "taler-merchant-httpd_mfa.h"
      31              : #include "taler/taler_merchant_bank_lib.h"
      32              : #include <taler/taler_dbevents.h>
      33              : #include <taler/taler_json_lib.h>
      34              : #include <regex.h>
      35              : #include "merchant-database/insert_instance.h"
      36              : #include "merchant-database/set_instance.h"
      37              : #include "merchant-database/insert_login_token.h"
      38              : #include "merchant-database/start.h"
      39              : 
      40              : /**
      41              :  * How often do we retry the simple INSERT database transaction?
      42              :  */
      43              : #define MAX_RETRIES 3
      44              : 
      45              : 
      46              : /**
      47              :  * Generate an instance, given its configuration.
      48              :  *
      49              :  * @param rh context of the handler
      50              :  * @param connection the MHD connection to handle
      51              :  * @param[in,out] hc context with further information about the request
      52              :  * @param login_token_expiration set to how long a login token validity
      53              :  *   should be, use zero if no login token should be created
      54              :  * @param validation_needed true if self-provisioned and
      55              :  *   email/phone registration is required before the
      56              :  *   instance can become fully active
      57              :  * @return MHD result code
      58              :  */
      59              : static enum MHD_Result
      60           42 : post_instances (const struct TMH_RequestHandler *rh,
      61              :                 struct MHD_Connection *connection,
      62              :                 struct TMH_HandlerContext *hc,
      63              :                 struct GNUNET_TIME_Relative login_token_expiration,
      64              :                 bool validation_needed)
      65              : {
      66           42 :   struct TALER_MERCHANTDB_InstanceSettings is = { 0 };
      67              :   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
      68           42 :   const char *auth_password = NULL;
      69           42 :   struct TMH_WireMethod *wm_head = NULL;
      70           42 :   struct TMH_WireMethod *wm_tail = NULL;
      71              :   const json_t *jauth;
      72           42 :   const char *iphone = NULL;
      73              :   char *id_lower;
      74              :   bool no_pay_delay;
      75              :   bool no_refund_delay;
      76              :   bool no_transfer_delay;
      77              :   bool no_rounding_interval;
      78              :   struct GNUNET_JSON_Specification spec[] = {
      79           42 :     TALER_JSON_spec_slug ("id",
      80              :                           (const char **) &is.id),
      81           42 :     GNUNET_JSON_spec_string ("name",
      82              :                              (const char **) &is.name),
      83           42 :     GNUNET_JSON_spec_mark_optional (
      84              :       GNUNET_JSON_spec_string ("email",
      85              :                                (const char **) &is.email),
      86              :       NULL),
      87           42 :     GNUNET_JSON_spec_mark_optional (
      88              :       GNUNET_JSON_spec_string ("phone_number",
      89              :                                &iphone),
      90              :       NULL),
      91           42 :     GNUNET_JSON_spec_mark_optional (
      92              :       TALER_JSON_spec_web_url ("website",
      93              :                                (const char **) &is.website),
      94              :       NULL),
      95           42 :     GNUNET_JSON_spec_mark_optional (
      96              :       GNUNET_JSON_spec_string ("logo",
      97              :                                (const char **) &is.logo),
      98              :       NULL),
      99           42 :     GNUNET_JSON_spec_object_const ("auth",
     100              :                                    &jauth),
     101           42 :     GNUNET_JSON_spec_json ("address",
     102              :                            &is.address),
     103           42 :     GNUNET_JSON_spec_json ("jurisdiction",
     104              :                            &is.jurisdiction),
     105           42 :     GNUNET_JSON_spec_bool ("use_stefan",
     106              :                            &is.use_stefan),
     107           42 :     GNUNET_JSON_spec_mark_optional (
     108              :       GNUNET_JSON_spec_relative_time ("default_pay_delay",
     109              :                                       &is.default_pay_delay),
     110              :       &no_pay_delay),
     111           42 :     GNUNET_JSON_spec_mark_optional (
     112              :       GNUNET_JSON_spec_relative_time ("default_refund_delay",
     113              :                                       &is.default_refund_delay),
     114              :       &no_refund_delay),
     115           42 :     GNUNET_JSON_spec_mark_optional (
     116              :       GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
     117              :                                       &is.default_wire_transfer_delay),
     118              :       &no_transfer_delay),
     119           42 :     GNUNET_JSON_spec_mark_optional (
     120              :       GNUNET_JSON_spec_time_rounder_interval (
     121              :         "default_wire_transfer_rounding_interval",
     122              :         &is.default_wire_transfer_rounding_interval),
     123              :       &no_rounding_interval),
     124           42 :     GNUNET_JSON_spec_end ()
     125              :   };
     126              : 
     127              :   {
     128              :     enum GNUNET_GenericReturnValue res;
     129              : 
     130           42 :     res = TALER_MHD_parse_json_data (connection,
     131           42 :                                      hc->request_body,
     132              :                                      spec);
     133           42 :     if (GNUNET_OK != res)
     134              :       return (GNUNET_NO == res)
     135              :              ? MHD_YES
     136            0 :              : MHD_NO;
     137              :   }
     138           42 :   if (no_pay_delay)
     139            0 :     is.default_pay_delay = TMH_default_pay_delay;
     140           42 :   if (no_refund_delay)
     141           23 :     is.default_refund_delay = TMH_default_refund_delay;
     142           42 :   if (no_transfer_delay)
     143            0 :     is.default_wire_transfer_delay = TMH_default_wire_transfer_delay;
     144           42 :   if (no_rounding_interval)
     145              :     is.default_wire_transfer_rounding_interval
     146           42 :       = TMH_default_wire_transfer_rounding_interval;
     147           42 :   if (GNUNET_TIME_relative_is_forever (is.default_pay_delay))
     148              :   {
     149            0 :     GNUNET_break_op (0);
     150            0 :     GNUNET_JSON_parse_free (spec);
     151            0 :     return TALER_MHD_reply_with_error (connection,
     152              :                                        MHD_HTTP_BAD_REQUEST,
     153              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     154              :                                        "default_pay_delay");
     155              :   }
     156           42 :   if (GNUNET_TIME_relative_is_forever (is.default_refund_delay))
     157              :   {
     158            0 :     GNUNET_break_op (0);
     159            0 :     GNUNET_JSON_parse_free (spec);
     160            0 :     return TALER_MHD_reply_with_error (connection,
     161              :                                        MHD_HTTP_BAD_REQUEST,
     162              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     163              :                                        "default_refund_delay");
     164              :   }
     165           42 :   if (GNUNET_TIME_relative_is_forever (is.default_wire_transfer_delay))
     166              :   {
     167            0 :     GNUNET_break_op (0);
     168            0 :     GNUNET_JSON_parse_free (spec);
     169            0 :     return TALER_MHD_reply_with_error (connection,
     170              :                                        MHD_HTTP_BAD_REQUEST,
     171              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     172              :                                        "default_wire_transfer_delay");
     173              :   }
     174           42 :   if (NULL != iphone)
     175              :   {
     176            5 :     is.phone = TALER_MERCHANT_phone_validate_normalize (iphone,
     177              :                                                         false);
     178            5 :     if (NULL == is.phone)
     179              :     {
     180            0 :       GNUNET_break_op (0);
     181            0 :       GNUNET_JSON_parse_free (spec);
     182            0 :       return TALER_MHD_reply_with_error (connection,
     183              :                                          MHD_HTTP_BAD_REQUEST,
     184              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     185              :                                          "phone_number");
     186              :     }
     187            5 :     if ( (NULL != TMH_phone_regex) &&
     188              :          (0 !=
     189            0 :           regexec (&TMH_phone_rx,
     190            0 :                    is.phone,
     191              :                    0,
     192              :                    NULL,
     193              :                    0)) )
     194              :     {
     195            0 :       GNUNET_break_op (0);
     196            0 :       GNUNET_JSON_parse_free (spec);
     197            0 :       return TALER_MHD_reply_with_error (connection,
     198              :                                          MHD_HTTP_BAD_REQUEST,
     199              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     200              :                                          "phone_number");
     201              :     }
     202              :   }
     203           42 :   if ( (NULL != is.email) &&
     204            5 :        (! TALER_MERCHANT_email_valid (is.email)) )
     205              :   {
     206            0 :     GNUNET_break_op (0);
     207            0 :     GNUNET_JSON_parse_free (spec);
     208            0 :     GNUNET_free (is.phone);
     209            0 :     return TALER_MHD_reply_with_error (connection,
     210              :                                        MHD_HTTP_BAD_REQUEST,
     211              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     212              :                                        "email");
     213              :   }
     214              : 
     215              :   {
     216              :     enum GNUNET_GenericReturnValue ret;
     217              : 
     218           42 :     ret = TMH_check_auth_config (connection,
     219              :                                  jauth,
     220              :                                  &auth_password);
     221           42 :     if (GNUNET_OK != ret)
     222              :     {
     223            0 :       GNUNET_free (is.phone);
     224            0 :       GNUNET_JSON_parse_free (spec);
     225            0 :       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
     226              :     }
     227              :   }
     228              : 
     229              :   /* check 'id' well-formed */
     230              :   {
     231              :     static bool once;
     232              :     static regex_t reg;
     233           42 :     bool id_wellformed = true;
     234              : 
     235           42 :     if (! once)
     236              :     {
     237           19 :       once = true;
     238           19 :       GNUNET_assert (0 ==
     239              :                      regcomp (&reg,
     240              :                               "^[A-Za-z0-9][A-Za-z0-9_.@-]+$",
     241              :                               REG_EXTENDED));
     242              :     }
     243              : 
     244           42 :     if (0 != regexec (&reg,
     245           42 :                       is.id,
     246              :                       0, NULL, 0))
     247            0 :       id_wellformed = false;
     248           42 :     if (! id_wellformed)
     249              :     {
     250            0 :       GNUNET_JSON_parse_free (spec);
     251            0 :       GNUNET_free (is.phone);
     252            0 :       return TALER_MHD_reply_with_error (connection,
     253              :                                          MHD_HTTP_BAD_REQUEST,
     254              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     255              :                                          "id");
     256              :     }
     257              :   }
     258              : 
     259           42 :   if (! TMH_location_object_valid (is.address))
     260              :   {
     261            0 :     GNUNET_break_op (0);
     262            0 :     GNUNET_JSON_parse_free (spec);
     263            0 :     GNUNET_free (is.phone);
     264            0 :     return TALER_MHD_reply_with_error (connection,
     265              :                                        MHD_HTTP_BAD_REQUEST,
     266              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     267              :                                        "address");
     268              :   }
     269              : 
     270           42 :   if (! TMH_location_object_valid (is.jurisdiction))
     271              :   {
     272            0 :     GNUNET_break_op (0);
     273            0 :     GNUNET_JSON_parse_free (spec);
     274            0 :     GNUNET_free (is.phone);
     275            0 :     return TALER_MHD_reply_with_error (connection,
     276              :                                        MHD_HTTP_BAD_REQUEST,
     277              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     278              :                                        "jurisdiction");
     279              :   }
     280              : 
     281           42 :   if ( (NULL != is.logo) &&
     282            0 :        (! TALER_MERCHANT_image_data_url_valid (is.logo)) )
     283              :   {
     284            0 :     GNUNET_break_op (0);
     285            0 :     GNUNET_JSON_parse_free (spec);
     286            0 :     GNUNET_free (is.phone);
     287            0 :     return TALER_MHD_reply_with_error (connection,
     288              :                                        MHD_HTTP_BAD_REQUEST,
     289              :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     290              :                                        "logo");
     291              :   }
     292              : 
     293           42 :   id_lower = GNUNET_STRINGS_utf8_tolower (is.id);
     294              :   {
     295              :     /* Test if an instance of this id is known */
     296              :     struct TMH_MerchantInstance *mi;
     297              : 
     298           42 :     mi = TMH_lookup_instance (id_lower);
     299           42 :     if (NULL != mi)
     300              :     {
     301              :       enum MHD_Result ret;
     302              : 
     303            2 :       if (mi->deleted)
     304              :       {
     305            0 :         GNUNET_JSON_parse_free (spec);
     306            0 :         GNUNET_free (is.phone);
     307            0 :         GNUNET_free (id_lower);
     308            0 :         return TALER_MHD_reply_with_error (
     309              :           connection,
     310              :           MHD_HTTP_CONFLICT,
     311              :           TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED,
     312            0 :           is.id);
     313              :       }
     314              :       /* Check for idempotency */
     315            2 :       if ( (0 == strcmp (mi->settings.id,
     316            2 :                          id_lower)) &&
     317            2 :            (0 == strcmp (mi->settings.name,
     318            2 :                          is.name)) &&
     319            2 :            ((mi->settings.email == is.email) ||
     320            0 :             (NULL != is.email && NULL != mi->settings.email &&
     321            0 :              0 == strcmp (mi->settings.email,
     322            0 :                           is.email))) &&
     323            2 :            ((mi->settings.website == is.website) ||
     324            0 :             (NULL != is.website && NULL != mi->settings.website &&
     325            0 :              0 == strcmp (mi->settings.website,
     326            0 :                           is.website))) &&
     327            2 :            ((mi->settings.logo == is.logo) ||
     328            0 :             (NULL != is.logo && NULL != mi->settings.logo &&
     329            0 :              0 == strcmp (mi->settings.logo,
     330            0 :                           is.logo))) &&
     331            2 :            ( ( (NULL != auth_password) &&
     332              :                (GNUNET_OK ==
     333            0 :                 TMH_check_auth (auth_password,
     334              :                                 &mi->auth.auth_salt,
     335            2 :                                 &mi->auth.auth_hash)) ) ||
     336            4 :              ( (NULL == auth_password) &&
     337              :                (GNUNET_YES ==
     338            4 :                 GNUNET_is_zero (&mi->auth.auth_hash))) ) &&
     339            2 :            (1 == json_equal (mi->settings.address,
     340            4 :                              is.address)) &&
     341            2 :            (1 == json_equal (mi->settings.jurisdiction,
     342            2 :                              is.jurisdiction)) &&
     343            2 :            (mi->settings.use_stefan == is.use_stefan) &&
     344            2 :            (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
     345              :                                       ==,
     346            2 :                                       is.default_wire_transfer_delay)) &&
     347            2 :            (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
     348              :                                       ==,
     349            2 :                                       is.default_pay_delay)) &&
     350            2 :            (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay,
     351              :                                       ==,
     352              :                                       is.default_refund_delay)) )
     353              :       {
     354            2 :         GNUNET_JSON_parse_free (spec);
     355            2 :         GNUNET_free (is.phone);
     356            2 :         GNUNET_free (id_lower);
     357            2 :         return TALER_MHD_reply_static (connection,
     358              :                                        MHD_HTTP_NO_CONTENT,
     359              :                                        NULL,
     360              :                                        NULL,
     361              :                                        0);
     362              :       }
     363            0 :       GNUNET_JSON_parse_free (spec);
     364            0 :       GNUNET_free (is.phone);
     365            0 :       ret = TALER_MHD_reply_with_error (connection,
     366              :                                         MHD_HTTP_CONFLICT,
     367              :                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
     368              :                                         id_lower);
     369            0 :       GNUNET_free (id_lower);
     370            0 :       return ret;
     371              :     }
     372              :   }
     373              : 
     374              :   /* Check MFA is satisfied */
     375           40 :   if (validation_needed)
     376              :   {
     377            5 :     enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
     378              : 
     379            5 :     if ( (0 != (TMH_TCS_SMS & TEH_mandatory_tan_channels)) &&
     380            5 :          (NULL == is.phone) )
     381              :     {
     382            0 :       GNUNET_break_op (0);
     383            0 :       GNUNET_JSON_parse_free (spec);
     384            0 :       GNUNET_free (is.phone); /* does nothing... */
     385            0 :       GNUNET_free (id_lower);
     386            0 :       return TALER_MHD_reply_with_error (connection,
     387              :                                          MHD_HTTP_BAD_REQUEST,
     388              :                                          TALER_EC_GENERIC_PARAMETER_MISSING,
     389              :                                          "phone_number");
     390              : 
     391              :     }
     392            5 :     if ( (0 != (TMH_TCS_EMAIL & TEH_mandatory_tan_channels)) &&
     393            5 :          (NULL == is.email) )
     394              :     {
     395            0 :       GNUNET_break_op (0);
     396            0 :       GNUNET_JSON_parse_free (spec);
     397            0 :       GNUNET_free (is.phone);
     398            0 :       GNUNET_free (id_lower);
     399            0 :       return TALER_MHD_reply_with_error (connection,
     400              :                                          MHD_HTTP_BAD_REQUEST,
     401              :                                          TALER_EC_GENERIC_PARAMETER_MISSING,
     402              :                                          "email");
     403              :     }
     404            5 :     switch (TEH_mandatory_tan_channels)
     405              :     {
     406            0 :     case TMH_TCS_NONE:
     407            0 :       GNUNET_assert (0);
     408              :       ret = GNUNET_OK;
     409              :       break;
     410            0 :     case TMH_TCS_SMS:
     411            0 :       is.phone_validated = true;
     412            0 :       ret = TMH_mfa_challenges_do (hc,
     413              :                                    id_lower,
     414              :                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
     415              :                                    true,
     416              :                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
     417              :                                    is.phone,
     418              :                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
     419            0 :       break;
     420            0 :     case TMH_TCS_EMAIL:
     421            0 :       is.email_validated = true;
     422            0 :       ret = TMH_mfa_challenges_do (hc,
     423              :                                    id_lower,
     424              :                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
     425              :                                    true,
     426              :                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
     427              :                                    is.email,
     428              :                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
     429            0 :       break;
     430            5 :     case TMH_TCS_EMAIL_AND_SMS:
     431            5 :       is.phone_validated = true;
     432            5 :       is.email_validated = true;
     433            5 :       ret = TMH_mfa_challenges_do (hc,
     434              :                                    id_lower,
     435              :                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
     436              :                                    true,
     437              :                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
     438              :                                    is.email,
     439              :                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
     440              :                                    is.phone,
     441              :                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
     442            5 :       break;
     443              :     }
     444            5 :     if (GNUNET_OK != ret)
     445              :     {
     446            3 :       GNUNET_JSON_parse_free (spec);
     447            3 :       GNUNET_free (is.phone);
     448            3 :       GNUNET_free (id_lower);
     449              :       return (GNUNET_NO == ret)
     450              :         ? MHD_YES
     451            3 :         : MHD_NO;
     452              :     }
     453              :   }
     454              : 
     455              :   /* handle authentication token setup */
     456           37 :   if (NULL == auth_password)
     457              :   {
     458           29 :     memset (&ias.auth_salt,
     459              :             0,
     460              :             sizeof (ias.auth_salt));
     461           29 :     memset (&ias.auth_hash,
     462              :             0,
     463              :             sizeof (ias.auth_hash));
     464              :   }
     465              :   else
     466              :   {
     467              :     /* Sets 'auth_salt' and 'auth_hash' */
     468            8 :     TMH_compute_auth (auth_password,
     469              :                       &ias.auth_salt,
     470              :                       &ias.auth_hash);
     471              :   }
     472              : 
     473              :   /* create in-memory data structure */
     474              :   {
     475              :     struct TMH_MerchantInstance *mi;
     476              :     enum GNUNET_DB_QueryStatus qs;
     477              : 
     478           37 :     mi = GNUNET_new (struct TMH_MerchantInstance);
     479           37 :     mi->wm_head = wm_head;
     480           37 :     mi->wm_tail = wm_tail;
     481           37 :     mi->settings = is;
     482           37 :     mi->settings.address = json_incref (mi->settings.address);
     483           37 :     mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
     484           37 :     mi->settings.id = id_lower;
     485           37 :     mi->settings.name = GNUNET_strdup (is.name);
     486           37 :     if (NULL != is.email)
     487            2 :       mi->settings.email = GNUNET_strdup (is.email);
     488           37 :     mi->settings.phone = is.phone;
     489           37 :     is.phone = NULL;
     490           37 :     if (NULL != is.website)
     491            0 :       mi->settings.website = GNUNET_strdup (is.website);
     492           37 :     if (NULL != is.logo)
     493            0 :       mi->settings.logo = GNUNET_strdup (is.logo);
     494           37 :     mi->auth = ias;
     495           37 :     GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv);
     496           37 :     GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv,
     497              :                                         &mi->merchant_pub.eddsa_pub);
     498              : 
     499           37 :     for (unsigned int i = 0; i<MAX_RETRIES; i++)
     500              :     {
     501           37 :       if (GNUNET_OK !=
     502           37 :           TALER_MERCHANTDB_start (TMH_db,
     503              :                                   "post /instances"))
     504              :       {
     505            0 :         GNUNET_break (0);
     506            0 :         mi->rc = 1;
     507            0 :         TMH_instance_decref (mi);
     508            0 :         GNUNET_JSON_parse_free (spec);
     509            0 :         return TALER_MHD_reply_with_error (
     510              :           connection,
     511              :           MHD_HTTP_INTERNAL_SERVER_ERROR,
     512              :           TALER_EC_GENERIC_DB_START_FAILED,
     513              :           NULL);
     514              :       }
     515           37 :       qs = TALER_MERCHANTDB_insert_instance (TMH_db,
     516           37 :                                              &mi->merchant_pub,
     517           37 :                                              &mi->merchant_priv,
     518           37 :                                              &mi->settings,
     519           37 :                                              &mi->auth);
     520           37 :       switch (qs)
     521              :       {
     522            0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     523              :         {
     524              :           enum MHD_Result ret;
     525              : 
     526            0 :           TALER_MERCHANTDB_rollback (TMH_db);
     527            0 :           GNUNET_break (0);
     528            0 :           ret = TALER_MHD_reply_with_error (
     529              :             connection,
     530              :             MHD_HTTP_INTERNAL_SERVER_ERROR,
     531              :             TALER_EC_GENERIC_DB_STORE_FAILED,
     532              :             "insert_instance");
     533            0 :           mi->rc = 1;
     534            0 :           TMH_instance_decref (mi);
     535            0 :           GNUNET_JSON_parse_free (spec);
     536            0 :           return ret;
     537              :         }
     538            0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
     539            0 :         goto retry;
     540            0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     541              :         {
     542              :           enum MHD_Result ret;
     543              : 
     544            0 :           TALER_MERCHANTDB_rollback (TMH_db);
     545            0 :           GNUNET_break (0);
     546            0 :           ret = TALER_MHD_reply_with_error (
     547              :             connection,
     548              :             MHD_HTTP_CONFLICT,
     549              :             TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
     550              :             id_lower);
     551            0 :           mi->rc = 1;
     552            0 :           TMH_instance_decref (mi);
     553            0 :           GNUNET_JSON_parse_free (spec);
     554            0 :           return ret;
     555              :         }
     556           37 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     557              :         /* handled below */
     558           37 :         break;
     559              :       }
     560           37 :       qs = TALER_MERCHANTDB_commit (TMH_db);
     561           37 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     562           37 :         qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     563            0 : retry:
     564           37 :       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     565           37 :         break; /* success! -- or hard failure */
     566              :     } /* for .. MAX_RETRIES */
     567           37 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     568              :     {
     569            0 :       GNUNET_break (0);
     570            0 :       mi->rc = 1;
     571            0 :       TMH_instance_decref (mi);
     572            0 :       GNUNET_JSON_parse_free (spec);
     573            0 :       return TALER_MHD_reply_with_error (connection,
     574              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     575              :                                          TALER_EC_GENERIC_DB_COMMIT_FAILED,
     576              :                                          NULL);
     577              :     }
     578              :     /* Finally, also update our running process */
     579           37 :     GNUNET_assert (GNUNET_OK ==
     580              :                    TMH_add_instance (mi));
     581           37 :     TMH_reload_instances (mi->settings.id);
     582              :   }
     583           37 :   GNUNET_JSON_parse_free (spec);
     584           37 :   if (GNUNET_TIME_relative_is_zero (login_token_expiration))
     585              :   {
     586           37 :     return TALER_MHD_reply_static (connection,
     587              :                                    MHD_HTTP_NO_CONTENT,
     588              :                                    NULL,
     589              :                                    NULL,
     590              :                                    0);
     591              :   }
     592              : 
     593              :   {
     594              :     /* Narrow DB interaction to new instance */
     595              :     enum GNUNET_DB_QueryStatus qs;
     596              : 
     597            0 :     qs = TALER_MERCHANTDB_set_instance (TMH_db,
     598              :                                         id_lower);
     599            0 :     switch (qs)
     600              :     {
     601            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     602            0 :       GNUNET_break (0);
     603            0 :       return TALER_MHD_reply_with_error (
     604              :         connection,
     605              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     606              :         TALER_EC_GENERIC_DB_SETUP_FAILED,
     607              :         "set_instance");
     608            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     609            0 :       GNUNET_break (0);
     610            0 :       return TALER_MHD_reply_with_error (
     611              :         connection,
     612              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     613              :         TALER_EC_GENERIC_DB_SETUP_FAILED,
     614              :         "set_instance");
     615            0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     616            0 :       return TALER_MHD_reply_with_error (
     617              :         connection,
     618              :         MHD_HTTP_NOT_FOUND,
     619              :         TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
     620              :         hc->url);
     621            0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     622            0 :       break;
     623              :     }
     624              :   }
     625              : 
     626              :   {
     627              :     struct TALER_MERCHANTDB_LoginTokenP btoken;
     628            0 :     enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA;
     629              :     enum GNUNET_DB_QueryStatus qs;
     630              :     struct GNUNET_TIME_Timestamp expiration_time;
     631            0 :     bool refreshable = true;
     632              : 
     633            0 :     GNUNET_CRYPTO_random_block (&btoken,
     634              :                                 sizeof (btoken));
     635              :     expiration_time
     636            0 :       = GNUNET_TIME_relative_to_timestamp (login_token_expiration);
     637            0 :     qs = TALER_MERCHANTDB_insert_login_token (TMH_db,
     638              :                                               id_lower,
     639              :                                               &btoken,
     640              :                                               GNUNET_TIME_timestamp_get (),
     641              :                                               expiration_time,
     642              :                                               iscope,
     643              :                                               "login token from instance creation");
     644            0 :     switch (qs)
     645              :     {
     646            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     647              :     case GNUNET_DB_STATUS_SOFT_ERROR:
     648              :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     649            0 :       GNUNET_break (0);
     650            0 :       return TALER_MHD_reply_with_error (
     651              :         connection,
     652              :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     653              :         TALER_EC_GENERIC_DB_STORE_FAILED,
     654              :         "insert_login_token");
     655            0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     656            0 :       break;
     657              :     }
     658              : 
     659              :     {
     660              :       char *tok;
     661              :       enum MHD_Result ret;
     662              :       char *val;
     663              : 
     664            0 :       val = GNUNET_STRINGS_data_to_string_alloc (&btoken,
     665              :                                                  sizeof (btoken));
     666            0 :       GNUNET_asprintf (&tok,
     667              :                        RFC_8959_PREFIX "%s",
     668              :                        val);
     669            0 :       GNUNET_free (val);
     670            0 :       ret = TALER_MHD_REPLY_JSON_PACK (
     671              :         connection,
     672              :         MHD_HTTP_OK,
     673              :         GNUNET_JSON_pack_string ("access_token",
     674              :                                  tok),
     675              :         GNUNET_JSON_pack_string ("token",
     676              :                                  tok),
     677              :         GNUNET_JSON_pack_string ("scope",
     678              :                                  TMH_get_name_by_scope (iscope,
     679              :                                                         &refreshable)),
     680              :         GNUNET_JSON_pack_bool ("refreshable",
     681              :                                refreshable),
     682              :         GNUNET_JSON_pack_timestamp ("expiration",
     683              :                                     expiration_time));
     684            0 :       GNUNET_free (tok);
     685            0 :       return ret;
     686              :     }
     687              :   }
     688              : }
     689              : 
     690              : 
     691              : /**
     692              :  * Generate an instance, given its configuration.
     693              :  *
     694              :  * @param rh context of the handler
     695              :  * @param connection the MHD connection to handle
     696              :  * @param[in,out] hc context with further information about the request
     697              :  * @return MHD result code
     698              :  */
     699              : enum MHD_Result
     700           37 : TMH_private_post_instances (const struct TMH_RequestHandler *rh,
     701              :                             struct MHD_Connection *connection,
     702              :                             struct TMH_HandlerContext *hc)
     703              : {
     704           74 :   return post_instances (rh,
     705              :                          connection,
     706              :                          hc,
     707           37 :                          GNUNET_TIME_UNIT_ZERO,
     708              :                          false);
     709              : }
     710              : 
     711              : 
     712              : /**
     713              :  * Generate an instance, given its configuration.
     714              :  * Public handler to be used when self-provisioning.
     715              :  *
     716              :  * @param rh context of the handler
     717              :  * @param connection the MHD connection to handle
     718              :  * @param[in,out] hc context with further information about the request
     719              :  * @return MHD result code
     720              :  */
     721              : enum MHD_Result
     722            5 : TMH_public_post_instances (const struct TMH_RequestHandler *rh,
     723              :                            struct MHD_Connection *connection,
     724              :                            struct TMH_HandlerContext *hc)
     725              : {
     726              :   struct GNUNET_TIME_Relative expiration;
     727              : 
     728            5 :   TALER_MHD_parse_request_rel_time (connection,
     729              :                                     "token_validity_ms",
     730              :                                     &expiration);
     731            5 :   if (GNUNET_YES !=
     732              :       TMH_have_self_provisioning)
     733              :   {
     734            0 :     GNUNET_break_op (0);
     735            0 :     return TALER_MHD_reply_with_error (connection,
     736              :                                        MHD_HTTP_FORBIDDEN,
     737              :                                        TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
     738              :                                        "Self-provisioning is not enabled");
     739              :   }
     740              : 
     741            5 :   return post_instances (rh,
     742              :                          connection,
     743              :                          hc,
     744              :                          expiration,
     745              :                          TMH_TCS_NONE !=
     746              :                          TEH_mandatory_tan_channels);
     747              : }
     748              : 
     749              : 
     750              : /* end of taler-merchant-httpd_post-management-instances.c */
        

Generated by: LCOV version 2.0-1