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: 130 180 72.2 %
Date: 2021-08-30 06:54:17 Functions: 2 4 50.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 :   json_decref (wm->j_wire);
      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           4 : 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           4 :   struct TMH_WireMethod *wm_head = NULL;
      68           4 :   struct TMH_WireMethod *wm_tail = NULL;
      69             :   struct GNUNET_JSON_Specification spec[] = {
      70           4 :     GNUNET_JSON_spec_json ("payto_uris",
      71             :                            &payto_uris),
      72           4 :     GNUNET_JSON_spec_string ("name",
      73             :                              &name),
      74           4 :     GNUNET_JSON_spec_json ("address",
      75             :                            &is.address),
      76           4 :     GNUNET_JSON_spec_json ("jurisdiction",
      77             :                            &is.jurisdiction),
      78           4 :     TALER_JSON_spec_amount ("default_max_wire_fee",
      79             :                             TMH_currency,
      80             :                             &is.default_max_wire_fee),
      81           4 :     GNUNET_JSON_spec_uint32 ("default_wire_fee_amortization",
      82             :                              &is.default_wire_fee_amortization),
      83           4 :     TALER_JSON_spec_amount ("default_max_deposit_fee",
      84             :                             TMH_currency,
      85             :                             &is.default_max_deposit_fee),
      86           4 :     TALER_JSON_spec_relative_time ("default_wire_transfer_delay",
      87             :                                    &is.default_wire_transfer_delay),
      88           4 :     TALER_JSON_spec_relative_time ("default_pay_delay",
      89             :                                    &is.default_pay_delay),
      90           4 :     GNUNET_JSON_spec_end ()
      91             :   };
      92             :   enum GNUNET_DB_QueryStatus qs;
      93           4 :   bool committed = false;
      94             : 
      95           4 :   GNUNET_assert (NULL != mi);
      96           4 :   memset (&is,
      97             :           0,
      98             :           sizeof (is));
      99             :   {
     100             :     enum GNUNET_GenericReturnValue res;
     101             : 
     102           4 :     res = TALER_MHD_parse_json_data (connection,
     103           4 :                                      hc->request_body,
     104             :                                      spec);
     105           4 :     if (GNUNET_OK != res)
     106             :       return (GNUNET_NO == res)
     107             :              ? MHD_YES
     108           1 :              : MHD_NO;
     109             :   }
     110           3 :   if (! TMH_location_object_valid (is.address))
     111             :   {
     112           0 :     GNUNET_break_op (0);
     113           0 :     GNUNET_JSON_parse_free (spec);
     114           0 :     return TALER_MHD_reply_with_error (connection,
     115             :                                        MHD_HTTP_BAD_REQUEST,
     116             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     117             :                                        "address");
     118             :   }
     119             : 
     120           3 :   if (! TMH_location_object_valid (is.jurisdiction))
     121             :   {
     122           0 :     GNUNET_break_op (0);
     123           0 :     GNUNET_JSON_parse_free (spec);
     124           0 :     return TALER_MHD_reply_with_error (connection,
     125             :                                        MHD_HTTP_BAD_REQUEST,
     126             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     127             :                                        "jurisdiction");
     128             :   }
     129             : 
     130           3 :   if (! TMH_payto_uri_array_valid (payto_uris))
     131           0 :     return TALER_MHD_reply_with_error (connection,
     132             :                                        MHD_HTTP_BAD_REQUEST,
     133             :                                        TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
     134             :                                        NULL);
     135           3 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     136             :   {
     137             :     /* Cleanup after earlier loops */
     138             :     {
     139             :       struct TMH_WireMethod *wm;
     140             : 
     141           3 :       while (NULL != (wm = wm_head))
     142             :       {
     143           0 :         GNUNET_CONTAINER_DLL_remove (wm_head,
     144             :                                      wm_tail,
     145             :                                      wm);
     146           0 :         free_wm (wm);
     147             :       }
     148             :     }
     149           3 :     if (GNUNET_OK !=
     150           3 :         TMH_db->start (TMH_db->cls,
     151             :                        "PATCH /instances"))
     152             :     {
     153           0 :       GNUNET_JSON_parse_free (spec);
     154           0 :       return TALER_MHD_reply_with_error (connection,
     155             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     156             :                                          TALER_EC_GENERIC_DB_START_FAILED,
     157             :                                          NULL);
     158             :     }
     159             :     /* Check for equality of settings */
     160           3 :     if (! ( (0 == strcmp (mi->settings.name,
     161           2 :                           name)) &&
     162           2 :             (1 == json_equal (mi->settings.address,
     163           3 :                               is.address)) &&
     164           1 :             (1 == json_equal (mi->settings.jurisdiction,
     165           2 :                               is.jurisdiction)) &&
     166           1 :             (GNUNET_YES == TALER_amount_cmp_currency (
     167           1 :                &mi->settings.default_max_deposit_fee,
     168           1 :                &is.default_max_deposit_fee)) &&
     169           1 :             (0 == TALER_amount_cmp (&mi->settings.default_max_deposit_fee,
     170           1 :                                     &is.default_max_deposit_fee)) &&
     171           1 :             (GNUNET_YES == TALER_amount_cmp_currency (
     172           1 :                &mi->settings.default_max_wire_fee,
     173           1 :                &is.default_max_wire_fee)) &&
     174           1 :             (0 == TALER_amount_cmp (&mi->settings.default_max_wire_fee,
     175           1 :                                     &is.default_max_wire_fee)) &&
     176           1 :             (mi->settings.default_wire_fee_amortization ==
     177           1 :              is.default_wire_fee_amortization) &&
     178           1 :             (mi->settings.default_wire_transfer_delay.rel_value_us ==
     179           1 :              is.default_wire_transfer_delay.rel_value_us) &&
     180           1 :             (mi->settings.default_pay_delay.rel_value_us ==
     181           1 :              is.default_pay_delay.rel_value_us) ) )
     182             :     {
     183           2 :       is.id = mi->settings.id;
     184           2 :       is.name = GNUNET_strdup (name);
     185           2 :       qs = TMH_db->update_instance (TMH_db->cls,
     186             :                                     &is);
     187           2 :       GNUNET_free (is.name);
     188           2 :       if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     189             :       {
     190           0 :         TMH_db->rollback (TMH_db->cls);
     191           0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     192           0 :           goto retry;
     193             :         else
     194           0 :           goto giveup;
     195             :       }
     196             :     }
     197             : 
     198             :     /* Check for changes in accounts */
     199           3 :     {
     200           3 :       unsigned int len = json_array_size (payto_uris);
     201           3 :       struct TMH_WireMethod *matches[GNUNET_NZL (len)];
     202             :       bool matched;
     203             : 
     204           3 :       memset (matches,
     205             :               0,
     206             :               sizeof (matches));
     207           6 :       for (struct TMH_WireMethod *wm = mi->wm_head;
     208             :            NULL != wm;
     209           3 :            wm = wm->next)
     210             :       {
     211           3 :         const char *uri = json_string_value (json_object_get (wm->j_wire,
     212             :                                                               "payto_uri"));
     213             : 
     214           3 :         GNUNET_assert (NULL != uri);
     215           3 :         matched = false;
     216           4 :         for (unsigned int i = 0; i<len; i++)
     217             :         {
     218           3 :           const char *str = json_string_value (json_array_get (payto_uris,
     219             :                                                                i));
     220           3 :           if (0 == strcasecmp (uri,
     221             :                                str))
     222             :           {
     223             :             /* our own existing payto URIs should be unique, that is no
     224             :                duplicates in the list, so we cannot match twice */
     225           2 :             GNUNET_assert (NULL == matches[i]);
     226           2 :             matches[i] = wm;
     227           2 :             matched = true;
     228           2 :             break;
     229             :           }
     230             :         }
     231             :         /* delete unmatched (= removed) accounts */
     232           3 :         if ( (! matched) &&
     233           1 :              (wm->active) )
     234             :         {
     235             :           /* Account was REMOVED */
     236           1 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     237             :                       "Existing account `%s' not found, inactivating it.\n",
     238             :                       uri);
     239           1 :           wm->deleting = true;
     240           1 :           qs = TMH_db->inactivate_account (TMH_db->cls,
     241           1 :                                            mi->settings.id,
     242           1 :                                            &wm->h_wire);
     243           1 :           if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     244             :           {
     245           0 :             TMH_db->rollback (TMH_db->cls);
     246           0 :             if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     247           0 :               goto retry;
     248             :             else
     249           0 :               goto giveup;
     250             :           }
     251             :         }
     252             :       }
     253             :       /* Find _new_ accounts */
     254           7 :       for (unsigned int i = 0; i<len; i++)
     255             :       {
     256             :         struct TALER_MERCHANTDB_AccountDetails ad;
     257             :         struct TMH_WireMethod *wm;
     258             : 
     259           4 :         if (NULL != matches[i])
     260             :         {
     261           2 :           wm = matches[i];
     262           2 :           if (! wm->active)
     263             :           {
     264           0 :             qs = TMH_db->activate_account (TMH_db->cls,
     265           0 :                                            mi->settings.id,
     266           0 :                                            &wm->h_wire);
     267           0 :             if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     268             :             {
     269           0 :               TMH_db->rollback (TMH_db->cls);
     270           0 :               if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     271           0 :                 goto retry;
     272             :               else
     273           0 :                 goto giveup;
     274             :             }
     275             :           }
     276           2 :           wm->enabling = true;
     277           2 :           continue;
     278             :         }
     279           2 :         ad.payto_uri = json_string_value (json_array_get (payto_uris,
     280             :                                                           i));
     281           2 :         GNUNET_assert (NULL != ad.payto_uri);
     282           2 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     283             :                     "Adding NEW account `%s'\n",
     284             :                     ad.payto_uri);
     285           2 :         wm = TMH_setup_wire_account (ad.payto_uri);
     286           2 :         GNUNET_CONTAINER_DLL_insert (wm_head,
     287             :                                      wm_tail,
     288             :                                      wm);
     289           2 :         ad.h_wire = wm->h_wire;
     290           2 :         ad.active = true;
     291           2 :         qs = TMH_db->insert_account (TMH_db->cls,
     292           2 :                                      mi->settings.id,
     293             :                                      &ad);
     294           2 :         if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     295             :         {
     296           0 :           TMH_db->rollback (TMH_db->cls);
     297           0 :           if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     298           0 :             goto retry;
     299             :           else
     300           0 :             goto giveup;
     301             :         }
     302             :       }
     303             :     }
     304             : 
     305           3 :     qs = TMH_db->commit (TMH_db->cls);
     306           3 : retry:
     307           3 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     308           0 :       continue;
     309           3 :     if (qs >= 0)
     310           3 :       committed = true;
     311           3 :     break;
     312             :   } /* for(... MAX_RETRIES) */
     313           0 : giveup:
     314             :   /* Deactivate existing wire methods that were removed above */
     315           6 :   for (struct TMH_WireMethod *wm = mi->wm_head;
     316             :        NULL != wm;
     317           3 :        wm = wm->next)
     318             :   {
     319             :     /* We did not flip the 'active' bits earlier because the
     320             :        DB transaction could still fail. Now it is time to update our
     321             :        runtime state. */
     322           3 :     GNUNET_assert (! (wm->deleting & wm->enabling));
     323           3 :     if (committed)
     324             :     {
     325           3 :       if (wm->deleting)
     326           1 :         wm->active = false;
     327           3 :       if (wm->enabling)
     328           2 :         wm->active = true;
     329             :     }
     330           3 :     wm->deleting = false;
     331           3 :     wm->enabling = false;
     332             :   }
     333           3 :   if (! committed)
     334             :   {
     335             :     struct TMH_WireMethod *wm;
     336             : 
     337           0 :     while (NULL != (wm = wm_head))
     338             :     {
     339           0 :       GNUNET_CONTAINER_DLL_remove (wm_head,
     340             :                                    wm_tail,
     341             :                                    wm);
     342           0 :       free_wm (wm);
     343             :     }
     344           0 :     GNUNET_JSON_parse_free (spec);
     345           0 :     return TALER_MHD_reply_with_error (connection,
     346             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     347             :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     348             :                                        NULL);
     349             :   }
     350             : 
     351             :   /* Update our 'settings' */
     352           3 :   GNUNET_free (mi->settings.name);
     353           3 :   json_decref (mi->settings.address);
     354           3 :   json_decref (mi->settings.jurisdiction);
     355           3 :   is.id = mi->settings.id;
     356           3 :   mi->settings = is;
     357           3 :   mi->settings.address = json_incref (mi->settings.address);
     358           3 :   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
     359           3 :   mi->settings.name = GNUNET_strdup (name);
     360             : 
     361             :   /* Add 'new' wire methods to our list */
     362             :   {
     363             :     struct TMH_WireMethod *wm;
     364             : 
     365             :     /* Note: this _could_ be done more efficiently if
     366             :        someone wrote a GNUNET_CONTAINER_DLL_merge()... */
     367           5 :     while (NULL != (wm = wm_head))
     368             :     {
     369           2 :       GNUNET_CONTAINER_DLL_remove (wm_head,
     370             :                                    wm_tail,
     371             :                                    wm);
     372           2 :       GNUNET_CONTAINER_DLL_insert (mi->wm_head,
     373             :                                    mi->wm_tail,
     374             :                                    wm);
     375             :     }
     376             :   }
     377             : 
     378           3 :   GNUNET_JSON_parse_free (spec);
     379           3 :   TMH_reload_instances (mi->settings.id);
     380           3 :   return TALER_MHD_reply_static (connection,
     381             :                                  MHD_HTTP_NO_CONTENT,
     382             :                                  NULL,
     383             :                                  NULL,
     384             :                                  0);
     385             : }
     386             : 
     387             : 
     388             : MHD_RESULT
     389           0 : TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh,
     390             :                                 struct MHD_Connection *connection,
     391             :                                 struct TMH_HandlerContext *hc)
     392             : {
     393           0 :   struct TMH_MerchantInstance *mi = hc->instance;
     394             : 
     395           0 :   return patch_instances_ID (mi,
     396             :                              connection,
     397             :                              hc);
     398             : }
     399             : 
     400             : 
     401             : MHD_RESULT
     402           4 : TMH_private_patch_instances_default_ID (const struct TMH_RequestHandler *rh,
     403             :                                         struct MHD_Connection *connection,
     404             :                                         struct TMH_HandlerContext *hc)
     405             : {
     406             :   struct TMH_MerchantInstance *mi;
     407             : 
     408           4 :   mi = TMH_lookup_instance (hc->infix);
     409           4 :   if (NULL == mi)
     410             :   {
     411           0 :     return TALER_MHD_reply_with_error (connection,
     412             :                                        MHD_HTTP_NOT_FOUND,
     413             :                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
     414           0 :                                        hc->infix);
     415             :   }
     416           4 :   if (mi->deleted)
     417             :   {
     418           0 :     return TALER_MHD_reply_with_error (connection,
     419             :                                        MHD_HTTP_CONFLICT,
     420             :                                        TALER_EC_MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED,
     421           0 :                                        hc->infix);
     422             :   }
     423           4 :   return patch_instances_ID (mi,
     424             :                              connection,
     425             :                              hc);
     426             : }
     427             : 
     428             : 
     429             : /* end of taler-merchant-httpd_private-patch-instances-ID.c */

Generated by: LCOV version 1.14