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: 49 278 17.6 %
Date: 2022-06-30 06:15:34 Functions: 3 8 37.5 %
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          14 : TMH_force_get_orders_resume (struct TMH_MerchantInstance *mi)
     117             : {
     118             :   struct TMH_PendingOrder *po;
     119             : 
     120          14 :   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          14 :   if (NULL != mi->po_eh)
     132             :   {
     133           0 :     TMH_db->event_listen_cancel (mi->po_eh);
     134           0 :     mi->po_eh = NULL;
     135             :   }
     136          14 :   if (NULL != order_timeout_task)
     137             :   {
     138           0 :     GNUNET_SCHEDULER_cancel (order_timeout_task);
     139           0 :     order_timeout_task = NULL;
     140             :   }
     141          14 :   if (NULL != order_timeout_heap)
     142             :   {
     143           0 :     GNUNET_CONTAINER_heap_destroy (order_timeout_heap);
     144           0 :     order_timeout_heap = NULL;
     145             :   }
     146          14 : }
     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           1 : cleanup (void *ctx)
     208             : {
     209           1 :   struct TMH_PendingOrder *po = ctx;
     210             : 
     211           1 :   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           1 :   if (NULL != po->hn)
     220           0 :     GNUNET_assert (po ==
     221             :                    GNUNET_CONTAINER_heap_remove_node (po->hn));
     222           1 :   json_decref (po->pa);
     223           1 :   GNUNET_free (po);
     224           1 : }
     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           1 : TMH_private_get_orders (const struct TMH_RequestHandler *rh,
     600             :                         struct MHD_Connection *connection,
     601             :                         struct TMH_HandlerContext *hc)
     602             : {
     603           1 :   struct TMH_PendingOrder *po = hc->ctx;
     604             :   enum GNUNET_DB_QueryStatus qs;
     605             :   struct TALER_MERCHANTDB_OrderFilter of;
     606             : 
     607           1 :   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           1 :   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           1 :   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           1 :   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           1 :     delta_str = MHD_lookup_connection_value (connection,
     654             :                                              MHD_GET_ARGUMENT_KIND,
     655             :                                              "delta");
     656           1 :     if (NULL == delta_str)
     657             :     {
     658           1 :       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             :   // FIXME: use date_s, as we round to seconds anyway!
     690             :   {
     691             :     const char *date_ms_str;
     692             : 
     693           1 :     date_ms_str = MHD_lookup_connection_value (connection,
     694             :                                                MHD_GET_ARGUMENT_KIND,
     695             :                                                "date_ms");
     696           1 :     if (NULL == date_ms_str)
     697             :     {
     698           1 :       if (of.delta > 0)
     699           0 :         of.date = GNUNET_TIME_UNIT_ZERO_TS;
     700             :       else
     701           1 :         of.date = GNUNET_TIME_UNIT_FOREVER_TS;
     702             :     }
     703             :     else
     704             :     {
     705             :       char dummy;
     706             :       unsigned long long ll;
     707             : 
     708           0 :       if (1 !=
     709           0 :           sscanf (date_ms_str,
     710             :                   "%llu%c",
     711             :                   &ll,
     712             :                   &dummy))
     713             :       {
     714           0 :         GNUNET_break_op (0);
     715           0 :         return TALER_MHD_reply_with_error (connection,
     716             :                                            MHD_HTTP_BAD_REQUEST,
     717             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     718             :                                            "date_ms");
     719             :       }
     720             : 
     721           0 :       of.date = GNUNET_TIME_absolute_to_timestamp (
     722             :         GNUNET_TIME_absolute_from_ms (ll));
     723           0 :       if (GNUNET_TIME_absolute_is_never (of.date.abs_time))
     724             :       {
     725           0 :         GNUNET_break_op (0);
     726           0 :         return TALER_MHD_reply_with_error (connection,
     727             :                                            MHD_HTTP_BAD_REQUEST,
     728             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     729             :                                            "date_ms");
     730             :       }
     731             :     }
     732             :   }
     733             :   {
     734             :     const char *start_row_str;
     735             : 
     736           1 :     start_row_str = MHD_lookup_connection_value (connection,
     737             :                                                  MHD_GET_ARGUMENT_KIND,
     738             :                                                  "start");
     739           1 :     if (NULL == start_row_str)
     740             :     {
     741           1 :       if (of.delta > 0)
     742           0 :         of.start_row = 0;
     743             :       else
     744           1 :         of.start_row = INT64_MAX;
     745             :     }
     746             :     else
     747             :     {
     748             :       char dummy;
     749             :       unsigned long long ull;
     750             : 
     751           0 :       if (1 !=
     752           0 :           sscanf (start_row_str,
     753             :                   "%llu%c",
     754             :                   &ull,
     755             :                   &dummy))
     756           0 :         return TALER_MHD_reply_with_error (connection,
     757             :                                            MHD_HTTP_BAD_REQUEST,
     758             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     759             :                                            "start");
     760           0 :       of.start_row = (uint64_t) ull;
     761           0 :       if (INT64_MAX < of.start_row)
     762             :       {
     763           0 :         GNUNET_break_op (0);
     764           0 :         return TALER_MHD_reply_with_error (connection,
     765             :                                            MHD_HTTP_BAD_REQUEST,
     766             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     767             :                                            "start");
     768             :       }
     769             :     }
     770             :   }
     771             :   {
     772             :     const char *timeout_ms_str;
     773             : 
     774           1 :     timeout_ms_str = MHD_lookup_connection_value (connection,
     775             :                                                   MHD_GET_ARGUMENT_KIND,
     776             :                                                   "timeout_ms");
     777           1 :     if (NULL == timeout_ms_str)
     778             :     {
     779           1 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     780             :     }
     781             :     else
     782             :     {
     783             :       char dummy;
     784             :       unsigned long long ull;
     785             : 
     786           0 :       if (1 !=
     787           0 :           sscanf (timeout_ms_str,
     788             :                   "%lld%c",
     789             :                   &ull,
     790             :                   &dummy))
     791           0 :         return TALER_MHD_reply_with_error (connection,
     792             :                                            MHD_HTTP_BAD_REQUEST,
     793             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     794             :                                            "timeout_ms");
     795           0 :       of.timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
     796             :                                                   ull);
     797           0 :       if (GNUNET_TIME_relative_is_forever (of.timeout))
     798             :       {
     799           0 :         GNUNET_break_op (0);
     800           0 :         return TALER_MHD_reply_with_error (connection,
     801             :                                            MHD_HTTP_BAD_REQUEST,
     802             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     803             :                                            "timeout_ms");
     804             :       }
     805             :     }
     806             : 
     807           1 :     if ( (0 >= of.delta) &&
     808           1 :          (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     809             :     {
     810           0 :       GNUNET_break_op (0);
     811           0 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     812             :     }
     813             :   }
     814             : 
     815           1 :   po = GNUNET_new (struct TMH_PendingOrder);
     816           1 :   hc->ctx = po;
     817           1 :   hc->cc = &cleanup;
     818           1 :   po->con = connection;
     819           1 :   po->pa = json_array ();
     820           1 :   GNUNET_assert (NULL != po->pa);
     821           1 :   po->instance_id = hc->instance->settings.id;
     822           1 :   po->mi = hc->instance;
     823           1 :   qs = TMH_db->lookup_orders (TMH_db->cls,
     824             :                               po->instance_id,
     825             :                               &of,
     826             :                               &add_order,
     827             :                               po);
     828           1 :   if (0 > qs)
     829             :   {
     830           0 :     GNUNET_break (0);
     831           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     832             :   }
     833           1 :   if (TALER_EC_NONE != po->result)
     834             :   {
     835           0 :     GNUNET_break (0);
     836           0 :     return TALER_MHD_reply_with_error (connection,
     837             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     838             :                                        po->result,
     839             :                                        NULL);
     840             :   }
     841           1 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) &&
     842           1 :        (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     843             :   {
     844           0 :     struct TMH_MerchantInstance *mi = hc->instance;
     845             : 
     846             :     /* setup timeout heap (if not yet exists) */
     847           0 :     if (NULL == order_timeout_heap)
     848             :       order_timeout_heap
     849           0 :         = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
     850           0 :     po->hn = GNUNET_CONTAINER_heap_insert (order_timeout_heap,
     851             :                                            po,
     852             :                                            po->long_poll_timeout.abs_value_us);
     853           0 :     po->long_poll_timeout = GNUNET_TIME_relative_to_absolute (of.timeout);
     854           0 :     po->of = of;
     855           0 :     GNUNET_CONTAINER_DLL_insert (mi->po_head,
     856             :                                  mi->po_tail,
     857             :                                  po);
     858           0 :     po->in_dll = true;
     859           0 :     if (NULL == mi->po_eh)
     860             :     {
     861           0 :       struct TMH_OrderChangeEventP change_eh = {
     862           0 :         .header.type = htons (TALER_DBEVENT_MERCHANT_ORDERS_CHANGE),
     863           0 :         .header.size = htons (sizeof (change_eh)),
     864             :         .merchant_pub = mi->merchant_pub
     865             :       };
     866             : 
     867           0 :       mi->po_eh = TMH_db->event_listen (TMH_db->cls,
     868             :                                         &change_eh.header,
     869           0 :                                         GNUNET_TIME_UNIT_FOREVER_REL,
     870             :                                         &resume_by_event,
     871             :                                         mi);
     872             :     }
     873           0 :     MHD_suspend_connection (connection);
     874             :     {
     875             :       struct TMH_PendingOrder *pot;
     876             : 
     877             :       /* start timeout task */
     878           0 :       pot = GNUNET_CONTAINER_heap_peek (order_timeout_heap);
     879           0 :       if (NULL != order_timeout_task)
     880           0 :         GNUNET_SCHEDULER_cancel (order_timeout_task);
     881           0 :       order_timeout_task = GNUNET_SCHEDULER_add_at (pot->long_poll_timeout,
     882             :                                                     &order_timeout,
     883             :                                                     NULL);
     884             :     }
     885           0 :     return MHD_YES;
     886             :   }
     887           1 :   return TALER_MHD_REPLY_JSON_PACK (
     888             :     connection,
     889             :     MHD_HTTP_OK,
     890             :     GNUNET_JSON_pack_array_incref ("orders",
     891             :                                    po->pa));
     892             : }
     893             : 
     894             : 
     895             : /* end of taler-merchant-httpd_private-get-orders.c */

Generated by: LCOV version 1.14