LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-orders.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 278 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2019-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-merchant-httpd_private-get-orders.c
      18             :  * @brief implement GET /orders
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include "taler-merchant-httpd_private-get-orders.h"
      23             : #include <taler/taler_json_lib.h>
      24             : #include <taler/taler_dbevents.h>
      25             : 
      26             : 
      27             : /**
      28             :  * Sensible bound on TALER_MERCHANTDB_OrderFilter.delta
      29             :  */
      30             : #define MAX_DELTA 1024
      31             : 
      32             : 
      33             : /**
      34             :  * A pending GET /orders request.
      35             :  */
      36             : struct TMH_PendingOrder
      37             : {
      38             : 
      39             :   /**
      40             :    * Kept in a DLL.
      41             :    */
      42             :   struct TMH_PendingOrder *prev;
      43             : 
      44             :   /**
      45             :    * Kept in a DLL.
      46             :    */
      47             :   struct TMH_PendingOrder *next;
      48             : 
      49             :   /**
      50             :    * Which connection was suspended.
      51             :    */
      52             :   struct MHD_Connection *con;
      53             : 
      54             :   /**
      55             :    * Associated heap node.
      56             :    */
      57             :   struct GNUNET_CONTAINER_HeapNode *hn;
      58             : 
      59             :   /**
      60             :    * Which instance is this client polling? This also defines
      61             :    * which DLL this struct is part of.
      62             :    */
      63             :   struct TMH_MerchantInstance *mi;
      64             : 
      65             :   /**
      66             :    * At what time does this request expire? If set in the future, we
      67             :    * may wait this long for a payment to arrive before responding.
      68             :    */
      69             :   struct GNUNET_TIME_Absolute long_poll_timeout;
      70             : 
      71             :   /**
      72             :    * Filter to apply.
      73             :    */
      74             :   struct TALER_MERCHANTDB_OrderFilter of;
      75             : 
      76             :   /**
      77             :    * The array of orders.
      78             :    */
      79             :   json_t *pa;
      80             : 
      81             :   /**
      82             :    * The name of the instance we are querying for.
      83             :    */
      84             :   const char *instance_id;
      85             : 
      86             :   /**
      87             :    * The result after adding the orders (#TALER_EC_NONE for okay, anything else for an error).
      88             :    */
      89             :   enum TALER_ErrorCode result;
      90             : 
      91             :   /**
      92             :    * Is the structure in the DLL
      93             :    */
      94             :   bool in_dll;
      95             : };
      96             : 
      97             : 
      98             : /**
      99             :  * Task to timeout pending orders.
     100             :  */
     101             : static struct GNUNET_SCHEDULER_Task *order_timeout_task;
     102             : 
     103             : /**
     104             :  * Heap for orders in long polling awaiting timeout.
     105             :  */
     106             : static struct GNUNET_CONTAINER_Heap *order_timeout_heap;
     107             : 
     108             : 
     109             : /**
     110             :  * We are shutting down (or an instance is being deleted), force resume of all
     111             :  * GET /orders requests.
     112             :  *
     113             :  * @param mi instance to force resuming for
     114             :  */
     115             : void
     116           0 : TMH_force_get_orders_resume (struct TMH_MerchantInstance *mi)
     117             : {
     118             :   struct TMH_PendingOrder *po;
     119             : 
     120           0 :   while (NULL != (po = mi->po_head))
     121             :   {
     122           0 :     GNUNET_assert (po->in_dll);
     123           0 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     124             :                                  mi->po_tail,
     125             :                                  po);
     126           0 :     GNUNET_assert (po ==
     127             :                    GNUNET_CONTAINER_heap_remove_root (order_timeout_heap));
     128           0 :     MHD_resume_connection (po->con);
     129           0 :     po->in_dll = false;
     130             :   }
     131           0 :   if (NULL != mi->po_eh)
     132             :   {
     133           0 :     TMH_db->event_listen_cancel (mi->po_eh);
     134           0 :     mi->po_eh = NULL;
     135             :   }
     136           0 :   if (NULL != order_timeout_task)
     137             :   {
     138           0 :     GNUNET_SCHEDULER_cancel (order_timeout_task);
     139           0 :     order_timeout_task = NULL;
     140             :   }
     141           0 :   if (NULL != order_timeout_heap)
     142             :   {
     143           0 :     GNUNET_CONTAINER_heap_destroy (order_timeout_heap);
     144           0 :     order_timeout_heap = NULL;
     145             :   }
     146           0 : }
     147             : 
     148             : 
     149             : /**
     150             :  * Task run to trigger timeouts on GET /orders requests with long polling.
     151             :  *
     152             :  * @param cls unused
     153             :  */
     154             : static void
     155           0 : order_timeout (void *cls)
     156             : {
     157             :   struct TMH_PendingOrder *po;
     158             :   struct TMH_MerchantInstance *mi;
     159             : 
     160             :   (void) cls;
     161           0 :   order_timeout_task = NULL;
     162             :   while (1)
     163             :   {
     164           0 :     po = GNUNET_CONTAINER_heap_peek (order_timeout_heap);
     165           0 :     if (NULL == po)
     166             :     {
     167             :       /* release data structure, we don't need it right now */
     168           0 :       GNUNET_CONTAINER_heap_destroy (order_timeout_heap);
     169           0 :       order_timeout_heap = NULL;
     170           0 :       return;
     171             :     }
     172           0 :     if (GNUNET_TIME_absolute_is_future (po->long_poll_timeout))
     173           0 :       break;
     174           0 :     GNUNET_assert (po ==
     175             :                    GNUNET_CONTAINER_heap_remove_root (order_timeout_heap));
     176           0 :     po->hn = NULL;
     177           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     178             :                 "Resuming long polled job due to timeout\n");
     179           0 :     mi = po->mi;
     180           0 :     GNUNET_assert (po->in_dll);
     181           0 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     182             :                                  mi->po_tail,
     183             :                                  po);
     184           0 :     if ( (NULL == mi->po_head) &&
     185           0 :          (NULL != mi->po_eh) )
     186             :     {
     187           0 :       TMH_db->event_listen_cancel (mi->po_eh);
     188           0 :       mi->po_eh = NULL;
     189             :     }
     190           0 :     po->in_dll = false;
     191           0 :     MHD_resume_connection (po->con);
     192           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     193             :   }
     194           0 :   order_timeout_task = GNUNET_SCHEDULER_add_at (po->long_poll_timeout,
     195             :                                                 &order_timeout,
     196             :                                                 NULL);
     197             : }
     198             : 
     199             : 
     200             : /**
     201             :  * Cleanup our "context", where we stored the JSON array
     202             :  * we are building for the response.
     203             :  *
     204             :  * @param ctx context to clean up, must be a `struct AddOrderState *`
     205             :  */
     206             : static void
     207           0 : cleanup (void *ctx)
     208             : {
     209           0 :   struct TMH_PendingOrder *po = ctx;
     210             : 
     211           0 :   if (po->in_dll)
     212             :   {
     213           0 :     struct TMH_MerchantInstance *mi = po->mi;
     214             : 
     215           0 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     216             :                                  mi->po_tail,
     217             :                                  po);
     218             :   }
     219           0 :   if (NULL != po->hn)
     220           0 :     GNUNET_assert (po ==
     221             :                    GNUNET_CONTAINER_heap_remove_node (po->hn));
     222           0 :   json_decref (po->pa);
     223           0 :   GNUNET_free (po);
     224           0 : }
     225             : 
     226             : 
     227             : /**
     228             :  * Function called with information about a refund.
     229             :  * It is responsible for summing up the refund amount.
     230             :  *
     231             :  * @param cls closure
     232             :  * @param refund_serial unique serial number of the refund
     233             :  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
     234             :  * @param coin_pub public coin from which the refund comes from
     235             :  * @param exchange_url URL of the exchange that issued @a coin_pub
     236             :  * @param rtransaction_id identificator of the refund
     237             :  * @param reason human-readable explanation of the refund
     238             :  * @param refund_amount refund amount which is being taken from @a coin_pub
     239             :  * @param pending true if the this refund was not yet processed by the wallet/exchange
     240             :  */
     241             : static void
     242           0 : process_refunds_cb (void *cls,
     243             :                     uint64_t refund_serial,
     244             :                     struct GNUNET_TIME_Timestamp timestamp,
     245             :                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
     246             :                     const char *exchange_url,
     247             :                     uint64_t rtransaction_id,
     248             :                     const char *reason,
     249             :                     const struct TALER_Amount *refund_amount,
     250             :                     bool pending)
     251             : {
     252           0 :   struct TALER_Amount *total_refund_amount = cls;
     253             : 
     254           0 :   GNUNET_assert (0 <=
     255             :                  TALER_amount_add (total_refund_amount,
     256             :                                    total_refund_amount,
     257             :                                    refund_amount));
     258           0 : }
     259             : 
     260             : 
     261             : /**
     262             :  * Add order details to our JSON array.
     263             :  *
     264             :  * @param cls some closure
     265             :  * @param orig_order_id the order this is about
     266             :  * @param order_serial serial ID of the order
     267             :  * @param creation_time when was the order created
     268             :  */
     269             : static void
     270           0 : add_order (void *cls,
     271             :            const char *orig_order_id,
     272             :            uint64_t order_serial,
     273             :            struct GNUNET_TIME_Timestamp creation_time)
     274             : {
     275           0 :   struct TMH_PendingOrder *po = cls;
     276           0 :   json_t *contract_terms = NULL;
     277             :   struct TALER_PrivateContractHashP h_contract_terms;
     278             :   enum GNUNET_DB_QueryStatus qs;
     279             :   const char *summary;
     280           0 :   char *order_id = NULL;
     281           0 :   bool refundable = false;
     282             :   bool paid;
     283             :   struct TALER_Amount order_amount;
     284             : 
     285           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     286             :               "Adding order `%s' (%llu) to result set\n",
     287             :               orig_order_id,
     288             :               (unsigned long long) order_serial);
     289           0 :   qs = TMH_db->lookup_order_status_by_serial (TMH_db->cls,
     290             :                                               po->instance_id,
     291             :                                               order_serial,
     292             :                                               &order_id,
     293             :                                               &h_contract_terms,
     294             :                                               &paid);
     295           0 :   if (qs < 0)
     296             :   {
     297           0 :     GNUNET_break (0);
     298           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     299           0 :     return;
     300             :   }
     301           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     302             :   {
     303             :     /* Contract terms don't exist, so the order cannot be paid. */
     304           0 :     paid = false;
     305           0 :     if (NULL == orig_order_id)
     306             :     {
     307             :       /* cannot be via DB trigger, and the other code
     308             :          path should have passed an orig_order_id */
     309           0 :       GNUNET_break (0);
     310           0 :       po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     311           0 :       return;
     312             :     }
     313           0 :     order_id = GNUNET_strdup (orig_order_id);
     314             :   }
     315             : 
     316             :   {
     317             :     /* First try to find the order in the contracts */
     318             :     uint64_t os;
     319           0 :     bool paid = false;
     320             : 
     321           0 :     qs = TMH_db->lookup_contract_terms (TMH_db->cls,
     322             :                                         po->instance_id,
     323             :                                         order_id,
     324             :                                         &contract_terms,
     325             :                                         &os,
     326             :                                         &paid,
     327             :                                         NULL);
     328           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     329           0 :       GNUNET_break (os == order_serial);
     330             :   }
     331           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     332             :   {
     333             :     /* Might still be unclaimed, so try order table */
     334             :     struct TALER_MerchantPostDataHashP unused;
     335             : 
     336           0 :     qs = TMH_db->lookup_order (TMH_db->cls,
     337             :                                po->instance_id,
     338             :                                order_id,
     339             :                                NULL,
     340             :                                &unused,
     341             :                                &contract_terms);
     342             :   }
     343           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     344             :   {
     345           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     346             :                 "Order %llu disappeared during iteration. Skipping.\n",
     347             :                 (unsigned long long) order_serial);
     348           0 :     json_decref (contract_terms); /* should still be NULL */
     349           0 :     GNUNET_free (order_id);
     350           0 :     return;
     351             :   }
     352           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     353             :   {
     354           0 :     GNUNET_break (0);
     355           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     356           0 :     json_decref (contract_terms);
     357           0 :     GNUNET_free (order_id);
     358           0 :     return;
     359             :   }
     360             : 
     361             :   {
     362             :     struct GNUNET_TIME_Timestamp rd;
     363             :     struct GNUNET_JSON_Specification spec[] = {
     364           0 :       TALER_JSON_spec_amount ("amount",
     365             :                               TMH_currency,
     366             :                               &order_amount),
     367           0 :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     368             :                                   &rd),
     369           0 :       GNUNET_JSON_spec_string ("summary",
     370             :                                &summary),
     371           0 :       GNUNET_JSON_spec_end ()
     372             :     };
     373             : 
     374           0 :     if (GNUNET_OK !=
     375           0 :         GNUNET_JSON_parse (contract_terms,
     376             :                            spec,
     377             :                            NULL, NULL))
     378             :     {
     379           0 :       GNUNET_break (0);
     380           0 :       po->result = TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID;
     381           0 :       json_decref (contract_terms);
     382           0 :       GNUNET_free (order_id);
     383           0 :       return;
     384             :     }
     385             : 
     386           0 :     if (GNUNET_TIME_absolute_is_future (rd.abs_time) &&
     387             :         paid)
     388             :     {
     389             :       struct TALER_Amount refund_amount;
     390             : 
     391           0 :       GNUNET_assert (GNUNET_OK ==
     392             :                      TALER_amount_set_zero (TMH_currency,
     393             :                                             &refund_amount));
     394           0 :       qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
     395             :                                             po->instance_id,
     396             :                                             &h_contract_terms,
     397             :                                             &process_refunds_cb,
     398             :                                             &refund_amount);
     399           0 :       if (0 > qs)
     400             :       {
     401           0 :         GNUNET_break (0);
     402           0 :         po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     403           0 :         json_decref (contract_terms);
     404           0 :         GNUNET_free (order_id);
     405           0 :         return;
     406             :       }
     407           0 :       if (0 > TALER_amount_cmp (&refund_amount,
     408             :                                 &order_amount))
     409           0 :         refundable = true;
     410             :     }
     411             :   }
     412             : 
     413           0 :   GNUNET_assert (0 ==
     414             :                  json_array_append_new (
     415             :                    po->pa,
     416             :                    GNUNET_JSON_PACK (
     417             :                      GNUNET_JSON_pack_string ("order_id",
     418             :                                               order_id),
     419             :                      GNUNET_JSON_pack_uint64 ("row_id",
     420             :                                               order_serial),
     421             :                      GNUNET_JSON_pack_timestamp ("timestamp",
     422             :                                                  creation_time),
     423             :                      TALER_JSON_pack_amount ("amount",
     424             :                                              &order_amount),
     425             :                      GNUNET_JSON_pack_string ("summary",
     426             :                                               summary),
     427             :                      GNUNET_JSON_pack_bool ("refundable",
     428             :                                             refundable),
     429             :                      GNUNET_JSON_pack_bool ("paid",
     430             :                                             paid))));
     431           0 :   json_decref (contract_terms);
     432           0 :   GNUNET_free (order_id);
     433             : }
     434             : 
     435             : 
     436             : /**
     437             :  * We have received a trigger from the database
     438             :  * that we should (possibly) resume some requests.
     439             :  *
     440             :  * @param cls a `struct TMH_MerchantInstance`
     441             :  * @param extra a `struct TMH_OrderChangeEventP`
     442             :  * @param extra_size number of bytes in @a extra
     443             :  */
     444             : static void
     445           0 : resume_by_event (void *cls,
     446             :                  const void *extra,
     447             :                  size_t extra_size)
     448             : {
     449           0 :   struct TMH_MerchantInstance *mi = cls;
     450           0 :   const struct TMH_OrderChangeEventDetailsP *oce = extra;
     451             :   struct TMH_PendingOrder *pn;
     452             :   enum TMH_OrderStateFlags osf;
     453             :   uint64_t order_serial_id;
     454             :   struct GNUNET_TIME_Timestamp date;
     455             : 
     456           0 :   if (sizeof (*oce) != extra_size)
     457             :   {
     458           0 :     GNUNET_break (0);
     459           0 :     return;
     460             :   }
     461           0 :   osf = (enum TMH_OrderStateFlags) ntohl (oce->order_state);
     462           0 :   order_serial_id = GNUNET_ntohll (oce->order_serial_id);
     463           0 :   date = GNUNET_TIME_timestamp_ntoh (oce->execution_date);
     464           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     465             :               "Received notification about new order %llu\n",
     466             :               (unsigned long long) order_serial_id);
     467           0 :   for (struct TMH_PendingOrder *po = mi->po_head;
     468             :        NULL != po;
     469           0 :        po = pn)
     470             :   {
     471           0 :     pn = po->next;
     472           0 :     if (! ( ( ((TALER_EXCHANGE_YNA_YES == po->of.paid) ==
     473           0 :                (0 != (osf & TMH_OSF_PAID))) ||
     474           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.paid) ) &&
     475           0 :             ( ((TALER_EXCHANGE_YNA_YES == po->of.refunded) ==
     476           0 :                (0 != (osf & TMH_OSF_REFUNDED))) ||
     477           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.refunded) ) &&
     478           0 :             ( ((TALER_EXCHANGE_YNA_YES == po->of.wired) ==
     479           0 :                (0 != (osf & TMH_OSF_WIRED))) ||
     480           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.wired) ) ) )
     481             :     {
     482           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     483             :                   "Client %p waits on different order type\n",
     484             :                   po);
     485           0 :       continue;
     486             :     }
     487           0 :     if (po->of.delta > 0)
     488             :     {
     489           0 :       if (order_serial_id < po->of.start_row)
     490             :       {
     491           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     492             :                     "Client %p waits on different order row\n",
     493             :                     po);
     494           0 :         continue;
     495             :       }
     496           0 :       if (GNUNET_TIME_timestamp_cmp (date,
     497             :                                      <,
     498             :                                      po->of.date))
     499             :       {
     500           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     501             :                     "Client %p waits on different order date\n",
     502             :                     po);
     503           0 :         continue;
     504             :       }
     505           0 :       po->of.delta--;
     506             :     }
     507             :     else
     508             :     {
     509           0 :       if (order_serial_id > po->of.start_row)
     510             :       {
     511           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     512             :                     "Client %p waits on different order row\n",
     513             :                     po);
     514           0 :         continue;
     515             :       }
     516           0 :       if (GNUNET_TIME_timestamp_cmp (date,
     517             :                                      >,
     518             :                                      po->of.date))
     519             :       {
     520           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     521             :                     "Client %p waits on different order date\n",
     522             :                     po);
     523           0 :         continue;
     524             :       }
     525           0 :       po->of.delta++;
     526             :     }
     527           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     528             :                 "Waking up client %p!\n",
     529             :                 po);
     530           0 :     add_order (po,
     531             :                NULL,
     532             :                order_serial_id,
     533             :                date);
     534           0 :     GNUNET_assert (po->in_dll);
     535           0 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     536             :                                  mi->po_tail,
     537             :                                  po);
     538           0 :     po->in_dll = false;
     539           0 :     GNUNET_assert (po ==
     540             :                    GNUNET_CONTAINER_heap_remove_node (po->hn));
     541           0 :     po->hn = NULL;
     542           0 :     MHD_resume_connection (po->con);
     543           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     544             :   }
     545           0 :   if (NULL == mi->po_head)
     546             :   {
     547           0 :     TMH_db->event_listen_cancel (mi->po_eh);
     548           0 :     mi->po_eh = NULL;
     549             :   }
     550             : }
     551             : 
     552             : 
     553             : /**
     554             :  * There has been a change or addition of a new @a order_id.  Wake up
     555             :  * long-polling clients that may have been waiting for this event.
     556             :  *
     557             :  * @param mi the instance where the order changed
     558             :  * @param osf order state flags
     559             :  * @param date execution date of the order
     560             :  * @param order_serial_id serial ID of the order in the database
     561             :  */
     562             : void
     563           0 : TMH_notify_order_change (struct TMH_MerchantInstance *mi,
     564             :                          enum TMH_OrderStateFlags osf,
     565             :                          struct GNUNET_TIME_Timestamp date,
     566             :                          uint64_t order_serial_id)
     567             : {
     568           0 :   struct TMH_OrderChangeEventDetailsP oce = {
     569           0 :     .order_serial_id = GNUNET_htonll (order_serial_id),
     570           0 :     .execution_date = GNUNET_TIME_timestamp_hton (date),
     571           0 :     .order_state = htonl (osf)
     572             :   };
     573           0 :   struct TMH_OrderChangeEventP eh = {
     574           0 :     .header.type = htons (TALER_DBEVENT_MERCHANT_ORDERS_CHANGE),
     575           0 :     .header.size = htons (sizeof (eh)),
     576             :     .merchant_pub = mi->merchant_pub
     577             :   };
     578             : 
     579           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     580             :               "Notifying clients of new order %llu at %s\n",
     581             :               (unsigned long long) order_serial_id,
     582             :               TALER_B2S (&mi->merchant_pub));
     583           0 :   TMH_db->event_notify (TMH_db->cls,
     584             :                         &eh.header,
     585             :                         &oce,
     586             :                         sizeof (oce));
     587           0 : }
     588             : 
     589             : 
     590             : /**
     591             :  * Handle a GET "/orders" request.
     592             :  *
     593             :  * @param rh context of the handler
     594             :  * @param connection the MHD connection to handle
     595             :  * @param[in,out] hc context with further information about the request
     596             :  * @return MHD result code
     597             :  */
     598             : MHD_RESULT
     599           0 : TMH_private_get_orders (const struct TMH_RequestHandler *rh,
     600             :                         struct MHD_Connection *connection,
     601             :                         struct TMH_HandlerContext *hc)
     602             : {
     603           0 :   struct TMH_PendingOrder *po = hc->ctx;
     604             :   enum GNUNET_DB_QueryStatus qs;
     605             :   struct TALER_MERCHANTDB_OrderFilter of;
     606             : 
     607           0 :   if (NULL != po)
     608             :   {
     609             :     /* resumed from long-polling, return answer we already have
     610             :        in 'hc->ctx' */
     611           0 :     if (TALER_EC_NONE != po->result)
     612             :     {
     613           0 :       GNUNET_break (0);
     614           0 :       return TALER_MHD_reply_with_error (connection,
     615             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     616             :                                          po->result,
     617             :                                          NULL);
     618             :     }
     619           0 :     return TALER_MHD_REPLY_JSON_PACK (
     620             :       connection,
     621             :       MHD_HTTP_OK,
     622             :       GNUNET_JSON_pack_array_incref ("orders",
     623             :                                      po->pa));
     624             :   }
     625             : 
     626           0 :   if (! (TALER_arg_to_yna (connection,
     627             :                            "paid",
     628             :                            TALER_EXCHANGE_YNA_ALL,
     629             :                            &of.paid)) )
     630           0 :     return TALER_MHD_reply_with_error (connection,
     631             :                                        MHD_HTTP_BAD_REQUEST,
     632             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     633             :                                        "paid");
     634           0 :   if (! (TALER_arg_to_yna (connection,
     635             :                            "refunded",
     636             :                            TALER_EXCHANGE_YNA_ALL,
     637             :                            &of.refunded)) )
     638           0 :     return TALER_MHD_reply_with_error (connection,
     639             :                                        MHD_HTTP_BAD_REQUEST,
     640             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     641             :                                        "refunded");
     642           0 :   if (! (TALER_arg_to_yna (connection,
     643             :                            "wired",
     644             :                            TALER_EXCHANGE_YNA_ALL,
     645             :                            &of.wired)) )
     646           0 :     return TALER_MHD_reply_with_error (connection,
     647             :                                        MHD_HTTP_BAD_REQUEST,
     648             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     649             :                                        "wired");
     650             :   {
     651             :     const char *delta_str;
     652             : 
     653           0 :     delta_str = MHD_lookup_connection_value (connection,
     654             :                                              MHD_GET_ARGUMENT_KIND,
     655             :                                              "delta");
     656           0 :     if (NULL == delta_str)
     657             :     {
     658           0 :       of.delta = -20;
     659             :     }
     660             :     else
     661             :     {
     662             :       char dummy;
     663             :       long long ll;
     664             : 
     665           0 :       if (1 !=
     666           0 :           sscanf (delta_str,
     667             :                   "%lld%c",
     668             :                   &ll,
     669             :                   &dummy))
     670             :       {
     671           0 :         GNUNET_break_op (0);
     672           0 :         return TALER_MHD_reply_with_error (connection,
     673             :                                            MHD_HTTP_BAD_REQUEST,
     674             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     675             :                                            "delta");
     676             :       }
     677           0 :       of.delta = (int64_t) ll;
     678           0 :       if ( (-MAX_DELTA > of.delta) ||
     679           0 :            (of.delta > MAX_DELTA) )
     680             :       {
     681           0 :         GNUNET_break_op (0);
     682           0 :         return TALER_MHD_reply_with_error (connection,
     683             :                                            MHD_HTTP_BAD_REQUEST,
     684             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     685             :                                            "delta");
     686             :       }
     687             :     }
     688             :   }
     689             :   {
     690             :     const char *date_s_str;
     691             : 
     692           0 :     date_s_str = MHD_lookup_connection_value (connection,
     693             :                                                MHD_GET_ARGUMENT_KIND,
     694             :                                                "date_s");
     695           0 :     if (NULL == date_s_str)
     696             :     {
     697           0 :       if (of.delta > 0)
     698           0 :         of.date = GNUNET_TIME_UNIT_ZERO_TS;
     699             :       else
     700           0 :         of.date = GNUNET_TIME_UNIT_FOREVER_TS;
     701             :     }
     702             :     else
     703             :     {
     704             :       char dummy;
     705             :       unsigned long long ll;
     706             : 
     707           0 :       if (1 !=
     708           0 :           sscanf (date_s_str,
     709             :                   "%llu%c",
     710             :                   &ll,
     711             :                   &dummy))
     712             :       {
     713           0 :         GNUNET_break_op (0);
     714           0 :         return TALER_MHD_reply_with_error (connection,
     715             :                                            MHD_HTTP_BAD_REQUEST,
     716             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     717             :                                            "date_ms");
     718             :       }
     719             : 
     720           0 :       of.date = GNUNET_TIME_absolute_to_timestamp (
     721             :         GNUNET_TIME_absolute_from_s (ll));
     722           0 :       if (GNUNET_TIME_absolute_is_never (of.date.abs_time))
     723             :       {
     724           0 :         GNUNET_break_op (0);
     725           0 :         return TALER_MHD_reply_with_error (connection,
     726             :                                            MHD_HTTP_BAD_REQUEST,
     727             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     728             :                                            "date_s");
     729             :       }
     730             :     }
     731             :   }
     732             :   {
     733             :     const char *start_row_str;
     734             : 
     735           0 :     start_row_str = MHD_lookup_connection_value (connection,
     736             :                                                  MHD_GET_ARGUMENT_KIND,
     737             :                                                  "start");
     738           0 :     if (NULL == start_row_str)
     739             :     {
     740           0 :       if (of.delta > 0)
     741           0 :         of.start_row = 0;
     742             :       else
     743           0 :         of.start_row = INT64_MAX;
     744             :     }
     745             :     else
     746             :     {
     747             :       char dummy;
     748             :       unsigned long long ull;
     749             : 
     750           0 :       if (1 !=
     751           0 :           sscanf (start_row_str,
     752             :                   "%llu%c",
     753             :                   &ull,
     754             :                   &dummy))
     755           0 :         return TALER_MHD_reply_with_error (connection,
     756             :                                            MHD_HTTP_BAD_REQUEST,
     757             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     758             :                                            "start");
     759           0 :       of.start_row = (uint64_t) ull;
     760           0 :       if (INT64_MAX < of.start_row)
     761             :       {
     762           0 :         GNUNET_break_op (0);
     763           0 :         return TALER_MHD_reply_with_error (connection,
     764             :                                            MHD_HTTP_BAD_REQUEST,
     765             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     766             :                                            "start");
     767             :       }
     768             :     }
     769             :   }
     770             :   {
     771             :     const char *timeout_ms_str;
     772             : 
     773           0 :     timeout_ms_str = MHD_lookup_connection_value (connection,
     774             :                                                   MHD_GET_ARGUMENT_KIND,
     775             :                                                   "timeout_ms");
     776           0 :     if (NULL == timeout_ms_str)
     777             :     {
     778           0 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     779             :     }
     780             :     else
     781             :     {
     782             :       char dummy;
     783             :       unsigned long long ull;
     784             : 
     785           0 :       if (1 !=
     786           0 :           sscanf (timeout_ms_str,
     787             :                   "%lld%c",
     788             :                   &ull,
     789             :                   &dummy))
     790           0 :         return TALER_MHD_reply_with_error (connection,
     791             :                                            MHD_HTTP_BAD_REQUEST,
     792             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     793             :                                            "timeout_ms");
     794           0 :       of.timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
     795             :                                                   ull);
     796           0 :       if (GNUNET_TIME_relative_is_forever (of.timeout))
     797             :       {
     798           0 :         GNUNET_break_op (0);
     799           0 :         return TALER_MHD_reply_with_error (connection,
     800             :                                            MHD_HTTP_BAD_REQUEST,
     801             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     802             :                                            "timeout_ms");
     803             :       }
     804             :     }
     805             : 
     806           0 :     if ( (0 >= of.delta) &&
     807           0 :          (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     808             :     {
     809           0 :       GNUNET_break_op (0);
     810           0 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     811             :     }
     812             :   }
     813             : 
     814           0 :   po = GNUNET_new (struct TMH_PendingOrder);
     815           0 :   hc->ctx = po;
     816           0 :   hc->cc = &cleanup;
     817           0 :   po->con = connection;
     818           0 :   po->pa = json_array ();
     819           0 :   GNUNET_assert (NULL != po->pa);
     820           0 :   po->instance_id = hc->instance->settings.id;
     821           0 :   po->mi = hc->instance;
     822           0 :   qs = TMH_db->lookup_orders (TMH_db->cls,
     823             :                               po->instance_id,
     824             :                               &of,
     825             :                               &add_order,
     826             :                               po);
     827           0 :   if (0 > qs)
     828             :   {
     829           0 :     GNUNET_break (0);
     830           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     831             :   }
     832           0 :   if (TALER_EC_NONE != po->result)
     833             :   {
     834           0 :     GNUNET_break (0);
     835           0 :     return TALER_MHD_reply_with_error (connection,
     836             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     837             :                                        po->result,
     838             :                                        NULL);
     839             :   }
     840           0 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) &&
     841           0 :        (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     842             :   {
     843           0 :     struct TMH_MerchantInstance *mi = hc->instance;
     844             : 
     845             :     /* setup timeout heap (if not yet exists) */
     846           0 :     if (NULL == order_timeout_heap)
     847             :       order_timeout_heap
     848           0 :         = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
     849           0 :     po->hn = GNUNET_CONTAINER_heap_insert (order_timeout_heap,
     850             :                                            po,
     851             :                                            po->long_poll_timeout.abs_value_us);
     852           0 :     po->long_poll_timeout = GNUNET_TIME_relative_to_absolute (of.timeout);
     853           0 :     po->of = of;
     854           0 :     GNUNET_CONTAINER_DLL_insert (mi->po_head,
     855             :                                  mi->po_tail,
     856             :                                  po);
     857           0 :     po->in_dll = true;
     858           0 :     if (NULL == mi->po_eh)
     859             :     {
     860           0 :       struct TMH_OrderChangeEventP change_eh = {
     861           0 :         .header.type = htons (TALER_DBEVENT_MERCHANT_ORDERS_CHANGE),
     862           0 :         .header.size = htons (sizeof (change_eh)),
     863             :         .merchant_pub = mi->merchant_pub
     864             :       };
     865             : 
     866           0 :       mi->po_eh = TMH_db->event_listen (TMH_db->cls,
     867             :                                         &change_eh.header,
     868           0 :                                         GNUNET_TIME_UNIT_FOREVER_REL,
     869             :                                         &resume_by_event,
     870             :                                         mi);
     871             :     }
     872           0 :     MHD_suspend_connection (connection);
     873             :     {
     874             :       struct TMH_PendingOrder *pot;
     875             : 
     876             :       /* start timeout task */
     877           0 :       pot = GNUNET_CONTAINER_heap_peek (order_timeout_heap);
     878           0 :       if (NULL != order_timeout_task)
     879           0 :         GNUNET_SCHEDULER_cancel (order_timeout_task);
     880           0 :       order_timeout_task = GNUNET_SCHEDULER_add_at (pot->long_poll_timeout,
     881             :                                                     &order_timeout,
     882             :                                                     NULL);
     883             :     }
     884           0 :     return MHD_YES;
     885             :   }
     886           0 :   return TALER_MHD_REPLY_JSON_PACK (
     887             :     connection,
     888             :     MHD_HTTP_OK,
     889             :     GNUNET_JSON_pack_array_incref ("orders",
     890             :                                    po->pa));
     891             : }
     892             : 
     893             : 
     894             : /* end of taler-merchant-httpd_private-get-orders.c */

Generated by: LCOV version 1.14