LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-patch-instances-ID.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 207 0.0 %
Date: 2022-06-30 06:15:34 Functions: 0 4 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2020-2021 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 taler-merchant-httpd_private-patch-instances-ID.c
      22             :  * @brief implementing PATCH /instances/$ID request handling
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include "taler-merchant-httpd_private-patch-instances-ID.h"
      27             : #include "taler-merchant-httpd_helper.h"
      28             : #include <taler/taler_json_lib.h>
      29             : 
      30             : 
      31             : /**
      32             :  * How often do we retry the simple INSERT database transaction?
      33             :  */
      34             : #define MAX_RETRIES 3
      35             : 
      36             : 
      37             : /**
      38             :  * Free memory used by @a wm
      39             :  *
      40             :  * @param wm wire method to free
      41             :  */
      42             : static void
      43           0 : free_wm (struct TMH_WireMethod *wm)
      44             : {
      45           0 :   GNUNET_free (wm->payto_uri);
      46           0 :   GNUNET_free (wm->wire_method);
      47           0 :   GNUNET_free (wm);
      48           0 : }
      49             : 
      50             : 
      51             : /**
      52             :  * PATCH configuration of an existing instance, given its configuration.
      53             :  *
      54             :  * @param mi instance to patch
      55             :  * @param connection the MHD connection to handle
      56             :  * @param[in,out] hc context with further information about the request
      57             :  * @return MHD result code
      58             :  */
      59             : static MHD_RESULT
      60           0 : patch_instances_ID (struct TMH_MerchantInstance *mi,
      61             :                     struct MHD_Connection *connection,
      62             :                     struct TMH_HandlerContext *hc)
      63             : {
      64             :   struct TALER_MERCHANTDB_InstanceSettings is;
      65             :   json_t *payto_uris;
      66             :   const char *name;
      67           0 :   struct TMH_WireMethod *wm_head = NULL;
      68           0 :   struct TMH_WireMethod *wm_tail = NULL;
      69             :   struct GNUNET_JSON_Specification spec[] = {
      70           0 :     GNUNET_JSON_spec_json ("payto_uris",
      71             :                            &payto_uris),
      72           0 :     GNUNET_JSON_spec_string ("name",
      73             :                              &name),
      74           0 :     GNUNET_JSON_spec_mark_optional(
      75             :       GNUNET_JSON_spec_string ("website",
      76             :                                (const char **) &is.website),
      77             :       NULL),
      78           0 :     GNUNET_JSON_spec_mark_optional(
      79             :       GNUNET_JSON_spec_string ("email",
      80             :                                 (const char **) &is.email),
      81             :       NULL),
      82           0 :     GNUNET_JSON_spec_mark_optional(
      83             :       GNUNET_JSON_spec_string ("logo",
      84             :                                 (const char **) &is.logo),
      85             :       NULL),
      86           0 :     GNUNET_JSON_spec_json ("address",
      87             :                            &is.address),
      88           0 :     GNUNET_JSON_spec_json ("jurisdiction",
      89             :                            &is.jurisdiction),
      90           0 :     TALER_JSON_spec_amount ("default_max_wire_fee",
      91             :                             TMH_currency,
      92             :                             &is.default_max_wire_fee),
      93           0 :     GNUNET_JSON_spec_uint32 ("default_wire_fee_amortization",
      94             :                              &is.default_wire_fee_amortization),
      95           0 :     TALER_JSON_spec_amount ("default_max_deposit_fee",
      96             :                             TMH_currency,
      97             :                             &is.default_max_deposit_fee),
      98           0 :     GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
      99             :                                     &is.default_wire_transfer_delay),
     100           0 :     GNUNET_JSON_spec_relative_time ("default_pay_delay",
     101             :                                     &is.default_pay_delay),
     102           0 :     GNUNET_JSON_spec_end ()
     103             :   };
     104             :   enum GNUNET_DB_QueryStatus qs;
     105           0 :   bool committed = false;
     106             : 
     107           0 :   GNUNET_assert (NULL != mi);
     108           0 :   memset (&is,
     109             :           0,
     110             :           sizeof (is));
     111             :   {
     112             :     enum GNUNET_GenericReturnValue res;
     113             : 
     114           0 :     res = TALER_MHD_parse_json_data (connection,
     115           0 :                                      hc->request_body,
     116             :                                      spec);
     117           0 :     if (GNUNET_OK != res)
     118             :       return (GNUNET_NO == res)
     119             :              ? MHD_YES
     120           0 :              : MHD_NO;
     121             :   }
     122           0 :   if (! TMH_location_object_valid (is.address))
     123             :   {
     124           0 :     GNUNET_break_op (0);
     125           0 :     GNUNET_JSON_parse_free (spec);
     126           0 :     return TALER_MHD_reply_with_error (connection,
     127             :                                        MHD_HTTP_BAD_REQUEST,
     128             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     129             :                                        "address");
     130             :   }
     131           0 :   if ( (NULL != is.logo) &&
     132           0 :        (! TMH_image_data_url_valid (is.logo)) )
     133             :   {
     134           0 :     GNUNET_break_op (0);
     135           0 :     GNUNET_JSON_parse_free (spec);
     136           0 :     return TALER_MHD_reply_with_error (connection,
     137             :                                        MHD_HTTP_BAD_REQUEST,
     138             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     139             :                                        "logo");
     140             :   }
     141             : 
     142           0 :   if (! TMH_location_object_valid (is.jurisdiction))
     143             :   {
     144           0 :     GNUNET_break_op (0);
     145           0 :     GNUNET_JSON_parse_free (spec);
     146           0 :     return TALER_MHD_reply_with_error (connection,
     147             :                                        MHD_HTTP_BAD_REQUEST,
     148             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     149             :                                        "jurisdiction");
     150             :   }
     151             : 
     152           0 :   if (! TMH_payto_uri_array_valid (payto_uris))
     153           0 :     return TALER_MHD_reply_with_error (connection,
     154             :                                        MHD_HTTP_BAD_REQUEST,
     155             :                                        TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
     156             :                                        NULL);
     157           0 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     158             :   {
     159             :     /* Cleanup after earlier loops */
     160             :     {
     161             :       struct TMH_WireMethod *wm;
     162             : 
     163           0 :       while (NULL != (wm = wm_head))
     164             :       {
     165           0 :         GNUNET_CONTAINER_DLL_remove (wm_head,
     166             :                                      wm_tail,
     167             :                                      wm);
     168           0 :         free_wm (wm);
     169             :       }
     170             :     }
     171           0 :     if (GNUNET_OK !=
     172           0 :         TMH_db->start (TMH_db->cls,
     173             :                        "PATCH /instances"))
     174             :     {
     175           0 :       GNUNET_JSON_parse_free (spec);
     176           0 :       return TALER_MHD_reply_with_error (connection,
     177             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     178             :                                          TALER_EC_GENERIC_DB_START_FAILED,
     179             :                                          NULL);
     180             :     }
     181             :     /* Check for equality of settings */
     182           0 :     if (! ( (0 == strcmp (mi->settings.name,
     183           0 :                           name)) &&
     184           0 :             ((mi->settings.email == is.email) ||
     185           0 :              (NULL != is.email && NULL != mi->settings.email &&
     186           0 :               0 == strcmp (mi->settings.email,
     187           0 :                             is.email))) &&
     188           0 :             ((mi->settings.website == is.website) ||
     189           0 :              (NULL != is.website && NULL != mi->settings.website &&
     190           0 :               0 == strcmp (mi->settings.website,
     191           0 :                             is.website))) &&
     192           0 :             ((mi->settings.logo == is.logo) ||
     193           0 :              (NULL != is.logo && NULL != mi->settings.logo &&
     194           0 :               0 == strcmp (mi->settings.logo,
     195           0 :                             is.logo))) &&
     196           0 :             (1 == json_equal (mi->settings.address,
     197           0 :                               is.address)) &&
     198           0 :             (1 == json_equal (mi->settings.jurisdiction,
     199           0 :                               is.jurisdiction)) &&
     200           0 :             (GNUNET_YES == TALER_amount_cmp_currency (
     201           0 :                &mi->settings.default_max_deposit_fee,
     202           0 :                &is.default_max_deposit_fee)) &&
     203           0 :             (0 == TALER_amount_cmp (&mi->settings.default_max_deposit_fee,
     204           0 :                                     &is.default_max_deposit_fee)) &&
     205           0 :             (GNUNET_YES == TALER_amount_cmp_currency (
     206           0 :                &mi->settings.default_max_wire_fee,
     207           0 :                &is.default_max_wire_fee)) &&
     208           0 :             (0 == TALER_amount_cmp (&mi->settings.default_max_wire_fee,
     209           0 :                                     &is.default_max_wire_fee)) &&
     210           0 :             (mi->settings.default_wire_fee_amortization ==
     211           0 :              is.default_wire_fee_amortization) &&
     212           0 :             (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
     213             :                                        ==,
     214             :                                        is.default_wire_transfer_delay)) &&
     215           0 :             (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
     216             :                                        ==,
     217             :                                        is.default_pay_delay)) ) )
     218             :     {
     219           0 :       is.id = mi->settings.id;
     220           0 :       is.name = GNUNET_strdup (name);
     221           0 :       qs = TMH_db->update_instance (TMH_db->cls,
     222             :                                     &is);
     223           0 :       GNUNET_free (is.name);
     224           0 :       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     225             :       {
     226           0 :         TMH_db->rollback (TMH_db->cls);
     227           0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     228           0 :           goto retry;
     229             :         else
     230           0 :           goto giveup;
     231             :       }
     232             :     }
     233             : 
     234             :     /* Check for changes in accounts */
     235           0 :     {
     236           0 :       unsigned int len = json_array_size (payto_uris);
     237           0 :       struct TMH_WireMethod *matches[GNUNET_NZL (len)];
     238             :       bool matched;
     239             : 
     240           0 :       memset (matches,
     241             :               0,
     242             :               sizeof (matches));
     243           0 :       for (struct TMH_WireMethod *wm = mi->wm_head;
     244             :            NULL != wm;
     245           0 :            wm = wm->next)
     246             :       {
     247           0 :         const char *uri = wm->payto_uri;
     248             : 
     249           0 :         GNUNET_assert (NULL != uri);
     250           0 :         matched = false;
     251           0 :         for (unsigned int i = 0; i<len; i++)
     252             :         {
     253           0 :           const char *str = json_string_value (json_array_get (payto_uris,
     254             :                                                                i));
     255           0 :           if (0 == strcasecmp (uri,
     256             :                                str))
     257             :           {
     258             :             /* our own existing payto URIs should be unique, that is no
     259             :                duplicates in the list, so we cannot match twice */
     260           0 :             GNUNET_assert (NULL == matches[i]);
     261           0 :             matches[i] = wm;
     262           0 :             matched = true;
     263           0 :             break;
     264             :           }
     265             :         }
     266             :         /* delete unmatched (= removed) accounts */
     267           0 :         if ( (! matched) &&
     268           0 :              (wm->active) )
     269             :         {
     270             :           /* Account was REMOVED */
     271           0 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     272             :                       "Existing account `%s' not found, inactivating it.\n",
     273             :                       uri);
     274           0 :           wm->deleting = true;
     275           0 :           qs = TMH_db->inactivate_account (TMH_db->cls,
     276           0 :                                            mi->settings.id,
     277           0 :                                            &wm->h_wire);
     278           0 :           if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     279             :           {
     280           0 :             TMH_db->rollback (TMH_db->cls);
     281           0 :             if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     282           0 :               goto retry;
     283             :             else
     284           0 :               goto giveup;
     285             :           }
     286             :         }
     287             :       }
     288             :       /* Find _new_ accounts */
     289           0 :       for (unsigned int i = 0; i<len; i++)
     290             :       {
     291             :         struct TALER_MERCHANTDB_AccountDetails ad;
     292             :         struct TMH_WireMethod *wm;
     293             : 
     294           0 :         if (NULL != matches[i])
     295             :         {
     296           0 :           wm = matches[i];
     297           0 :           if (! wm->active)
     298             :           {
     299           0 :             qs = TMH_db->activate_account (TMH_db->cls,
     300           0 :                                            mi->settings.id,
     301           0 :                                            &wm->h_wire);
     302           0 :             if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     303             :             {
     304           0 :               TMH_db->rollback (TMH_db->cls);
     305           0 :               if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     306           0 :                 goto retry;
     307             :               else
     308           0 :                 goto giveup;
     309             :             }
     310             :           }
     311           0 :           wm->enabling = true;
     312           0 :           continue;
     313             :         }
     314           0 :         ad.payto_uri = json_string_value (json_array_get (payto_uris,
     315             :                                                           i));
     316           0 :         GNUNET_assert (NULL != ad.payto_uri);
     317           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     318             :                     "Adding NEW account `%s'\n",
     319             :                     ad.payto_uri);
     320           0 :         wm = TMH_setup_wire_account (ad.payto_uri);
     321           0 :         GNUNET_CONTAINER_DLL_insert (wm_head,
     322             :                                      wm_tail,
     323             :                                      wm);
     324           0 :         ad.h_wire = wm->h_wire;
     325           0 :         ad.active = true;
     326           0 :         qs = TMH_db->insert_account (TMH_db->cls,
     327           0 :                                      mi->settings.id,
     328             :                                      &ad);
     329           0 :         if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     330             :         {
     331           0 :           TMH_db->rollback (TMH_db->cls);
     332           0 :           if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     333           0 :             goto retry;
     334             :           else
     335           0 :             goto giveup;
     336             :         }
     337             :       }
     338             :     }
     339             : 
     340           0 :     qs = TMH_db->commit (TMH_db->cls);
     341           0 : retry:
     342           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     343           0 :       continue;
     344           0 :     if (qs >= 0)
     345           0 :       committed = true;
     346           0 :     break;
     347             :   } /* for(... MAX_RETRIES) */
     348           0 : giveup:
     349             :   /* Deactivate existing wire methods that were removed above */
     350           0 :   for (struct TMH_WireMethod *wm = mi->wm_head;
     351             :        NULL != wm;
     352           0 :        wm = wm->next)
     353             :   {
     354             :     /* We did not flip the 'active' bits earlier because the
     355             :        DB transaction could still fail. Now it is time to update our
     356             :        runtime state. */
     357           0 :     GNUNET_assert (! (wm->deleting & wm->enabling));
     358           0 :     if (committed)
     359             :     {
     360           0 :       if (wm->deleting)
     361           0 :         wm->active = false;
     362           0 :       if (wm->enabling)
     363           0 :         wm->active = true;
     364             :     }
     365           0 :     wm->deleting = false;
     366           0 :     wm->enabling = false;
     367             :   }
     368           0 :   if (! committed)
     369             :   {
     370             :     struct TMH_WireMethod *wm;
     371             : 
     372           0 :     while (NULL != (wm = wm_head))
     373             :     {
     374           0 :       GNUNET_CONTAINER_DLL_remove (wm_head,
     375             :                                    wm_tail,
     376             :                                    wm);
     377           0 :       free_wm (wm);
     378             :     }
     379           0 :     GNUNET_JSON_parse_free (spec);
     380           0 :     return TALER_MHD_reply_with_error (connection,
     381             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     382             :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     383             :                                        NULL);
     384             :   }
     385             : 
     386             :   /* Update our 'settings' */
     387           0 :   GNUNET_free (mi->settings.name);
     388           0 :   GNUNET_free (mi->settings.email);
     389           0 :   GNUNET_free (mi->settings.website);
     390           0 :   GNUNET_free (mi->settings.logo);
     391           0 :   json_decref (mi->settings.address);
     392           0 :   json_decref (mi->settings.jurisdiction);
     393           0 :   is.id = mi->settings.id;
     394           0 :   mi->settings = is;
     395           0 :   mi->settings.address = json_incref (mi->settings.address);
     396           0 :   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
     397           0 :   mi->settings.name = GNUNET_strdup (name);
     398           0 :   if (NULL != is.email)
     399           0 :     mi->settings.email = GNUNET_strdup (is.email);
     400           0 :   if (NULL != is.website)
     401           0 :     mi->settings.website = GNUNET_strdup (is.website);
     402           0 :   if (NULL != is.logo)
     403           0 :     mi->settings.logo = GNUNET_strdup (is.logo);
     404             : 
     405             :   /* Add 'new' wire methods to our list */
     406             :   {
     407             :     struct TMH_WireMethod *wm;
     408             : 
     409             :     /* Note: this _could_ be done more efficiently if
     410             :        someone wrote a GNUNET_CONTAINER_DLL_merge()... */
     411           0 :     while (NULL != (wm = wm_head))
     412             :     {
     413           0 :       GNUNET_CONTAINER_DLL_remove (wm_head,
     414             :                                    wm_tail,
     415             :                                    wm);
     416           0 :       GNUNET_CONTAINER_DLL_insert (mi->wm_head,
     417             :                                    mi->wm_tail,
     418             :                                    wm);
     419             :     }
     420             :   }
     421             : 
     422           0 :   GNUNET_JSON_parse_free (spec);
     423           0 :   TMH_reload_instances (mi->settings.id);
     424           0 :   return TALER_MHD_reply_static (connection,
     425             :                                  MHD_HTTP_NO_CONTENT,
     426             :                                  NULL,
     427             :                                  NULL,
     428             :                                  0);
     429             : }
     430             : 
     431             : 
     432             : MHD_RESULT
     433           0 : TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh,
     434             :                                 struct MHD_Connection *connection,
     435             :                                 struct TMH_HandlerContext *hc)
     436             : {
     437           0 :   struct TMH_MerchantInstance *mi = hc->instance;
     438             : 
     439           0 :   return patch_instances_ID (mi,
     440             :                              connection,
     441             :                              hc);
     442             : }
     443             : 
     444             : 
     445             : MHD_RESULT
     446           0 : TMH_private_patch_instances_default_ID (const struct TMH_RequestHandler *rh,
     447             :                                         struct MHD_Connection *connection,
     448             :                                         struct TMH_HandlerContext *hc)
     449             : {
     450             :   struct TMH_MerchantInstance *mi;
     451             : 
     452           0 :   mi = TMH_lookup_instance (hc->infix);
     453           0 :   if (NULL == mi)
     454             :   {
     455           0 :     return TALER_MHD_reply_with_error (connection,
     456             :                                        MHD_HTTP_NOT_FOUND,
     457             :                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
     458           0 :                                        hc->infix);
     459             :   }
     460           0 :   if (mi->deleted)
     461             :   {
     462           0 :     return TALER_MHD_reply_with_error (connection,
     463             :                                        MHD_HTTP_CONFLICT,
     464             :                                        TALER_EC_MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED,
     465           0 :                                        hc->infix);
     466             :   }
     467           0 :   return patch_instances_ID (mi,
     468             :                              connection,
     469             :                              hc);
     470             : }
     471             : 
     472             : 
     473             : /* end of taler-merchant-httpd_private-patch-instances-ID.c */

Generated by: LCOV version 1.14