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: 135 257 52.5 %
Date: 2021-08-30 06:54:17 Functions: 6 8 75.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          62 : TMH_force_get_orders_resume (struct TMH_MerchantInstance *mi)
     117             : {
     118             :   struct TMH_PendingOrder *po;
     119             : 
     120          62 :   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          62 :   if (NULL != mi->po_eh)
     132             :   {
     133           0 :     TMH_db->event_listen_cancel (mi->po_eh);
     134           0 :     mi->po_eh = NULL;
     135             :   }
     136          62 :   if (NULL != order_timeout_task)
     137             :   {
     138           0 :     GNUNET_SCHEDULER_cancel (order_timeout_task);
     139           0 :     order_timeout_task = NULL;
     140             :   }
     141          62 :   if (NULL != order_timeout_heap)
     142             :   {
     143           0 :     GNUNET_CONTAINER_heap_destroy (order_timeout_heap);
     144           0 :     order_timeout_heap = NULL;
     145             :   }
     146          62 : }
     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           1 : order_timeout (void *cls)
     156             : {
     157             :   struct TMH_PendingOrder *po;
     158             :   struct TMH_MerchantInstance *mi;
     159             : 
     160             :   (void) cls;
     161           1 :   order_timeout_task = NULL;
     162             :   while (1)
     163             :   {
     164           2 :     po = GNUNET_CONTAINER_heap_peek (order_timeout_heap);
     165           2 :     if (NULL == po)
     166             :     {
     167             :       /* release data structure, we don't need it right now */
     168           1 :       GNUNET_CONTAINER_heap_destroy (order_timeout_heap);
     169           1 :       order_timeout_heap = NULL;
     170           1 :       return;
     171             :     }
     172           1 :     if (GNUNET_TIME_absolute_is_future (po->long_poll_timeout))
     173           0 :       break;
     174           1 :     GNUNET_assert (po ==
     175             :                    GNUNET_CONTAINER_heap_remove_root (order_timeout_heap));
     176           1 :     po->hn = NULL;
     177           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     178             :                 "Resuming long polled job due to timeout\n");
     179           1 :     mi = po->mi;
     180           1 :     GNUNET_assert (po->in_dll);
     181           1 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     182             :                                  mi->po_tail,
     183             :                                  po);
     184           1 :     if ( (NULL == mi->po_head) &&
     185           1 :          (NULL != mi->po_eh) )
     186             :     {
     187           1 :       TMH_db->event_listen_cancel (mi->po_eh);
     188           1 :       mi->po_eh = NULL;
     189             :     }
     190           1 :     po->in_dll = false;
     191           1 :     MHD_resume_connection (po->con);
     192           1 :     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           5 : cleanup (void *ctx)
     208             : {
     209           5 :   struct TMH_PendingOrder *po = ctx;
     210             : 
     211           5 :   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           5 :   if (NULL != po->hn)
     220           0 :     GNUNET_assert (po ==
     221             :                    GNUNET_CONTAINER_heap_remove_node (po->hn));
     222           5 :   json_decref (po->pa);
     223           5 :   GNUNET_free (po);
     224           5 : }
     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_Absolute 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           4 : add_order (void *cls,
     271             :            const char *orig_order_id,
     272             :            uint64_t order_serial,
     273             :            struct GNUNET_TIME_Absolute creation_time)
     274             : {
     275           4 :   struct TMH_PendingOrder *po = cls;
     276             :   json_t *contract_terms;
     277             :   struct GNUNET_HashCode h_contract_terms;
     278             :   enum GNUNET_DB_QueryStatus qs;
     279             :   const char *summary;
     280           4 :   char *order_id = NULL;
     281           4 :   bool refundable = false;
     282             :   bool paid;
     283             :   struct TALER_Amount order_amount;
     284             : 
     285           4 :   qs = TMH_db->lookup_order_status_by_serial (TMH_db->cls,
     286             :                                               po->instance_id,
     287             :                                               order_serial,
     288             :                                               &order_id,
     289             :                                               &h_contract_terms,
     290             :                                               &paid);
     291           4 :   if (qs < 0)
     292             :   {
     293           0 :     GNUNET_break (0);
     294           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     295           0 :     return;
     296             :   }
     297             :   /* qs == 0: contract terms don't exist, so the order cannot be paid. */
     298           4 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     299             :   {
     300           0 :     paid = false;
     301           0 :     if (NULL == orig_order_id)
     302             :     {
     303             :       /* cannot be via DB trigger, and the other code
     304             :          path should have passed an orig_order_id */
     305           0 :       GNUNET_break (0);
     306           0 :       po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     307           0 :       return;
     308             :     }
     309           0 :     order_id = GNUNET_strdup (orig_order_id);
     310             :   }
     311             : 
     312           4 :   if (paid)
     313             :   {
     314             :     /* if the order was paid, it must have been claimed, so use
     315             :        lookup_contract_terms to avoid the order being deleted in the db. */
     316             :     uint64_t os;
     317             : 
     318           1 :     qs = TMH_db->lookup_contract_terms (TMH_db->cls,
     319             :                                         po->instance_id,
     320             :                                         order_id,
     321             :                                         &contract_terms,
     322             :                                         &os,
     323             :                                         NULL);
     324             :   }
     325             :   else
     326             :   {
     327             :     struct GNUNET_HashCode unused;
     328             : 
     329           3 :     qs = TMH_db->lookup_order (TMH_db->cls,
     330             :                                po->instance_id,
     331             :                                order_id,
     332             :                                NULL,
     333             :                                &unused,
     334             :                                &contract_terms);
     335             :   }
     336             : 
     337           4 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     338             :   {
     339           0 :     GNUNET_break (0);
     340           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     341           0 :     json_decref (contract_terms);
     342           0 :     GNUNET_free (order_id);
     343           0 :     return;
     344             :   }
     345             : 
     346             :   {
     347             :     struct GNUNET_TIME_Absolute rd;
     348             :     struct GNUNET_JSON_Specification spec[] = {
     349           4 :       TALER_JSON_spec_amount ("amount",
     350             :                               TMH_currency,
     351             :                               &order_amount),
     352           4 :       GNUNET_JSON_spec_absolute_time ("refund_deadline",
     353             :                                       &rd),
     354           4 :       GNUNET_JSON_spec_string ("summary",
     355             :                                &summary),
     356           4 :       GNUNET_JSON_spec_end ()
     357             :     };
     358             : 
     359           4 :     if (GNUNET_OK !=
     360           4 :         GNUNET_JSON_parse (contract_terms,
     361             :                            spec,
     362             :                            NULL, NULL))
     363             :     {
     364           0 :       GNUNET_break (0);
     365           0 :       po->result = TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID;
     366           0 :       json_decref (contract_terms);
     367           0 :       GNUNET_free (order_id);
     368           0 :       return;
     369             :     }
     370             : 
     371           4 :     if (GNUNET_TIME_absolute_is_future (rd) &&
     372             :         paid)
     373             :     {
     374             :       struct TALER_Amount refund_amount;
     375             : 
     376           0 :       GNUNET_assert (GNUNET_OK ==
     377             :                      TALER_amount_set_zero (TMH_currency,
     378             :                                             &refund_amount));
     379           0 :       qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
     380             :                                             po->instance_id,
     381             :                                             &h_contract_terms,
     382             :                                             &process_refunds_cb,
     383             :                                             &refund_amount);
     384           0 :       if (0 > qs)
     385             :       {
     386           0 :         GNUNET_break (0);
     387           0 :         po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     388           0 :         json_decref (contract_terms);
     389           0 :         GNUNET_free (order_id);
     390           0 :         return;
     391             :       }
     392           0 :       if (0 > TALER_amount_cmp (&refund_amount,
     393             :                                 &order_amount))
     394           0 :         refundable = true;
     395             :     }
     396             :   }
     397             : 
     398           4 :   GNUNET_assert (0 ==
     399             :                  json_array_append_new (
     400             :                    po->pa,
     401             :                    GNUNET_JSON_PACK (
     402             :                      GNUNET_JSON_pack_string ("order_id",
     403             :                                               order_id),
     404             :                      GNUNET_JSON_pack_uint64 ("row_id",
     405             :                                               order_serial),
     406             :                      GNUNET_JSON_pack_time_abs ("timestamp",
     407             :                                                 creation_time),
     408             :                      TALER_JSON_pack_amount ("amount",
     409             :                                              &order_amount),
     410             :                      GNUNET_JSON_pack_string ("summary",
     411             :                                               summary),
     412             :                      GNUNET_JSON_pack_bool ("refundable",
     413             :                                             refundable),
     414             :                      GNUNET_JSON_pack_bool ("paid",
     415             :                                             paid))));
     416           4 :   json_decref (contract_terms);
     417           4 :   GNUNET_free (order_id);
     418             : }
     419             : 
     420             : 
     421             : /**
     422             :  * We have received a trigger from the database
     423             :  * that we should (possibly) resume some requests.
     424             :  *
     425             :  * @param cls a `struct TMH_MerchantInstance`
     426             :  * @param extra a `struct TMH_OrderChangeEventP`
     427             :  * @param extra_size number of bytes in @a extra
     428             :  */
     429             : static void
     430           0 : resume_by_event (void *cls,
     431             :                  const void *extra,
     432             :                  size_t extra_size)
     433             : {
     434           0 :   struct TMH_MerchantInstance *mi = cls;
     435           0 :   const struct TMH_OrderChangeEventDetailsP *oce = extra;
     436             :   struct TMH_PendingOrder *pn;
     437             :   enum TMH_OrderStateFlags osf;
     438             :   uint64_t order_serial_id;
     439             :   struct GNUNET_TIME_Absolute date;
     440             : 
     441           0 :   if (sizeof (*oce) != extra_size)
     442             :   {
     443           0 :     GNUNET_break (0);
     444           0 :     return;
     445             :   }
     446           0 :   osf = (enum TMH_OrderStateFlags) ntohl (oce->order_state);
     447           0 :   order_serial_id = GNUNET_ntohll (oce->order_serial_id);
     448           0 :   date = GNUNET_TIME_absolute_ntoh (oce->execution_date);
     449           0 :   for (struct TMH_PendingOrder *po = mi->po_head;
     450             :        NULL != po;
     451           0 :        po = pn)
     452             :   {
     453           0 :     pn = po->next;
     454           0 :     if (! ( ( ((TALER_EXCHANGE_YNA_YES == po->of.paid) ==
     455           0 :                (0 != (osf & TMH_OSF_PAID))) ||
     456           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.paid) ) &&
     457           0 :             ( ((TALER_EXCHANGE_YNA_YES == po->of.refunded) ==
     458           0 :                (0 != (osf & TMH_OSF_REFUNDED))) ||
     459           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.refunded) ) &&
     460           0 :             ( ((TALER_EXCHANGE_YNA_YES == po->of.wired) ==
     461           0 :                (0 != (osf & TMH_OSF_WIRED))) ||
     462           0 :               (TALER_EXCHANGE_YNA_ALL == po->of.wired) ) ) )
     463           0 :       continue;
     464           0 :     if (po->of.delta > 0)
     465             :     {
     466           0 :       if (order_serial_id < po->of.start_row)
     467           0 :         continue;
     468           0 :       if (date.abs_value_us < po->of.date.abs_value_us)
     469           0 :         continue;
     470           0 :       po->of.delta--;
     471             :     }
     472             :     else
     473             :     {
     474           0 :       if (order_serial_id > po->of.start_row)
     475           0 :         continue;
     476           0 :       if (date.abs_value_us > po->of.date.abs_value_us)
     477           0 :         continue;
     478           0 :       po->of.delta++;
     479             :     }
     480           0 :     add_order (po,
     481             :                NULL,
     482             :                order_serial_id,
     483             :                date);
     484           0 :     GNUNET_assert (po->in_dll);
     485           0 :     GNUNET_CONTAINER_DLL_remove (mi->po_head,
     486             :                                  mi->po_tail,
     487             :                                  po);
     488           0 :     po->in_dll = false;
     489           0 :     GNUNET_assert (po ==
     490             :                    GNUNET_CONTAINER_heap_remove_node (po->hn));
     491           0 :     MHD_resume_connection (po->con);
     492           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     493             :   }
     494           0 :   if (NULL == mi->po_head)
     495             :   {
     496           0 :     TMH_db->event_listen_cancel (mi->po_eh);
     497           0 :     mi->po_eh = NULL;
     498             :   }
     499             : }
     500             : 
     501             : 
     502             : /**
     503             :  * There has been a change or addition of a new @a order_id.  Wake up
     504             :  * long-polling clients that may have been waiting for this event.
     505             :  *
     506             :  * @param mi the instance where the order changed
     507             :  * @param osf order state flags
     508             :  * @param date execution date of the order
     509             :  * @param order_serial_id serial ID of the order in the database
     510             :  */
     511             : void
     512           7 : TMH_notify_order_change (struct TMH_MerchantInstance *mi,
     513             :                          enum TMH_OrderStateFlags osf,
     514             :                          struct GNUNET_TIME_Absolute date,
     515             :                          uint64_t order_serial_id)
     516             : {
     517          14 :   struct TMH_OrderChangeEventDetailsP oce = {
     518           7 :     .order_serial_id = GNUNET_htonll (order_serial_id),
     519           7 :     .execution_date = GNUNET_TIME_absolute_hton (date),
     520           7 :     .order_state = htonl (osf)
     521             :   };
     522           7 :   struct TMH_OrderChangeEventP eh = {
     523           7 :     .header.type = htons (TALER_DBEVENT_MERCHANT_ORDERS_CHANGE),
     524           7 :     .header.size = htons (sizeof (eh)),
     525             :     .merchant_pub = mi->merchant_pub
     526             :   };
     527             : 
     528           7 :   TMH_db->event_notify (TMH_db->cls,
     529             :                         &eh.header,
     530             :                         &oce,
     531             :                         sizeof (oce));
     532           7 : }
     533             : 
     534             : 
     535             : /**
     536             :  * Handle a GET "/orders" request.
     537             :  *
     538             :  * @param rh context of the handler
     539             :  * @param connection the MHD connection to handle
     540             :  * @param[in,out] hc context with further information about the request
     541             :  * @return MHD result code
     542             :  */
     543             : MHD_RESULT
     544           6 : TMH_private_get_orders (const struct TMH_RequestHandler *rh,
     545             :                         struct MHD_Connection *connection,
     546             :                         struct TMH_HandlerContext *hc)
     547             : {
     548           6 :   struct TMH_PendingOrder *po = hc->ctx;
     549             :   enum GNUNET_DB_QueryStatus qs;
     550             :   struct TALER_MERCHANTDB_OrderFilter of;
     551             : 
     552           6 :   if (NULL != po)
     553             :   {
     554             :     /* resumed from long-polling, return answer we already have
     555             :        in 'hc->ctx' */
     556           1 :     if (TALER_EC_NONE != po->result)
     557             :     {
     558           0 :       GNUNET_break (0);
     559           0 :       return TALER_MHD_reply_with_error (connection,
     560             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     561             :                                          po->result,
     562             :                                          NULL);
     563             :     }
     564           1 :     return TALER_MHD_REPLY_JSON_PACK (
     565             :       connection,
     566             :       MHD_HTTP_OK,
     567             :       GNUNET_JSON_pack_array_incref ("orders",
     568             :                                      po->pa));
     569             :   }
     570             : 
     571           5 :   if (! (TALER_arg_to_yna (connection,
     572             :                            "paid",
     573             :                            TALER_EXCHANGE_YNA_ALL,
     574             :                            &of.paid)) )
     575           0 :     return TALER_MHD_reply_with_error (connection,
     576             :                                        MHD_HTTP_BAD_REQUEST,
     577             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     578             :                                        "paid");
     579           5 :   if (! (TALER_arg_to_yna (connection,
     580             :                            "refunded",
     581             :                            TALER_EXCHANGE_YNA_ALL,
     582             :                            &of.refunded)) )
     583           0 :     return TALER_MHD_reply_with_error (connection,
     584             :                                        MHD_HTTP_BAD_REQUEST,
     585             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     586             :                                        "refunded");
     587           5 :   if (! (TALER_arg_to_yna (connection,
     588             :                            "wired",
     589             :                            TALER_EXCHANGE_YNA_ALL,
     590             :                            &of.wired)) )
     591           0 :     return TALER_MHD_reply_with_error (connection,
     592             :                                        MHD_HTTP_BAD_REQUEST,
     593             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     594             :                                        "wired");
     595             :   {
     596             :     const char *delta_str;
     597             : 
     598           5 :     delta_str = MHD_lookup_connection_value (connection,
     599             :                                              MHD_GET_ARGUMENT_KIND,
     600             :                                              "delta");
     601           5 :     if (NULL == delta_str)
     602             :     {
     603           4 :       of.delta = -20;
     604             :     }
     605             :     else
     606             :     {
     607             :       char dummy;
     608             :       long long ll;
     609             : 
     610           1 :       if (1 !=
     611           1 :           sscanf (delta_str,
     612             :                   "%lld%c",
     613             :                   &ll,
     614             :                   &dummy))
     615             :       {
     616           0 :         GNUNET_break_op (0);
     617           0 :         return TALER_MHD_reply_with_error (connection,
     618             :                                            MHD_HTTP_BAD_REQUEST,
     619             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     620             :                                            "delta");
     621             :       }
     622           1 :       of.delta = (int64_t) ll;
     623           1 :       if ( (-MAX_DELTA > of.delta) ||
     624           1 :            (of.delta > MAX_DELTA) )
     625             :       {
     626           0 :         GNUNET_break_op (0);
     627           0 :         return TALER_MHD_reply_with_error (connection,
     628             :                                            MHD_HTTP_BAD_REQUEST,
     629             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     630             :                                            "delta");
     631             :       }
     632             :     }
     633             :   }
     634             :   {
     635             :     const char *date_ms_str;
     636             : 
     637           5 :     date_ms_str = MHD_lookup_connection_value (connection,
     638             :                                                MHD_GET_ARGUMENT_KIND,
     639             :                                                "date_ms");
     640           5 :     if (NULL == date_ms_str)
     641             :     {
     642           5 :       if (of.delta > 0)
     643           1 :         of.date = GNUNET_TIME_UNIT_ZERO_ABS;
     644             :       else
     645           4 :         of.date = GNUNET_TIME_UNIT_FOREVER_ABS;
     646             :     }
     647             :     else
     648             :     {
     649             :       char dummy;
     650             :       unsigned long long ll;
     651             : 
     652           0 :       if (1 !=
     653           0 :           sscanf (date_ms_str,
     654             :                   "%llu%c",
     655             :                   &ll,
     656             :                   &dummy))
     657             :       {
     658           0 :         GNUNET_break_op (0);
     659           0 :         return TALER_MHD_reply_with_error (connection,
     660             :                                            MHD_HTTP_BAD_REQUEST,
     661             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     662             :                                            "date_ms");
     663             :       }
     664             : 
     665           0 :       of.date = GNUNET_TIME_absolute_from_ms (ll);
     666           0 :       if (GNUNET_TIME_absolute_is_never (of.date))
     667             :       {
     668           0 :         GNUNET_break_op (0);
     669           0 :         return TALER_MHD_reply_with_error (connection,
     670             :                                            MHD_HTTP_BAD_REQUEST,
     671             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     672             :                                            "date_ms");
     673             :       }
     674             :     }
     675             :   }
     676             :   {
     677             :     const char *start_row_str;
     678             : 
     679           5 :     start_row_str = MHD_lookup_connection_value (connection,
     680             :                                                  MHD_GET_ARGUMENT_KIND,
     681             :                                                  "start");
     682           5 :     if (NULL == start_row_str)
     683             :     {
     684           4 :       if (of.delta > 0)
     685           0 :         of.start_row = 0;
     686             :       else
     687           4 :         of.start_row = INT64_MAX;
     688             :     }
     689             :     else
     690             :     {
     691             :       char dummy;
     692             :       unsigned long long ull;
     693             : 
     694           1 :       if (1 !=
     695           1 :           sscanf (start_row_str,
     696             :                   "%llu%c",
     697             :                   &ull,
     698             :                   &dummy))
     699           0 :         return TALER_MHD_reply_with_error (connection,
     700             :                                            MHD_HTTP_BAD_REQUEST,
     701             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     702             :                                            "start");
     703           1 :       of.start_row = (uint64_t) ull;
     704           1 :       if (INT64_MAX < of.start_row)
     705             :       {
     706           0 :         GNUNET_break_op (0);
     707           0 :         return TALER_MHD_reply_with_error (connection,
     708             :                                            MHD_HTTP_BAD_REQUEST,
     709             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     710             :                                            "start");
     711             :       }
     712             :     }
     713             :   }
     714             :   {
     715             :     const char *timeout_ms_str;
     716             : 
     717           5 :     timeout_ms_str = MHD_lookup_connection_value (connection,
     718             :                                                   MHD_GET_ARGUMENT_KIND,
     719             :                                                   "timeout_ms");
     720           5 :     if (NULL == timeout_ms_str)
     721             :     {
     722           4 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     723             :     }
     724             :     else
     725             :     {
     726             :       char dummy;
     727             :       unsigned long long ull;
     728             : 
     729           1 :       if (1 !=
     730           1 :           sscanf (timeout_ms_str,
     731             :                   "%lld%c",
     732             :                   &ull,
     733             :                   &dummy))
     734           0 :         return TALER_MHD_reply_with_error (connection,
     735             :                                            MHD_HTTP_BAD_REQUEST,
     736             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     737             :                                            "timeout_ms");
     738           1 :       of.timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
     739             :                                                   ull);
     740           1 :       if (GNUNET_TIME_relative_is_forever (of.timeout))
     741             :       {
     742           0 :         GNUNET_break_op (0);
     743           0 :         return TALER_MHD_reply_with_error (connection,
     744             :                                            MHD_HTTP_BAD_REQUEST,
     745             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     746             :                                            "timeout_ms");
     747             :       }
     748             :     }
     749             : 
     750           5 :     if ( (0 >= of.delta) &&
     751           4 :          (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     752             :     {
     753           0 :       GNUNET_break_op (0);
     754           0 :       of.timeout = GNUNET_TIME_UNIT_ZERO;
     755             :     }
     756             :   }
     757             : 
     758           5 :   po = GNUNET_new (struct TMH_PendingOrder);
     759           5 :   hc->ctx = po;
     760           5 :   hc->cc = &cleanup;
     761           5 :   po->con = connection;
     762           5 :   po->pa = json_array ();
     763           5 :   GNUNET_assert (NULL != po->pa);
     764           5 :   po->instance_id = hc->instance->settings.id;
     765           5 :   po->mi = hc->instance;
     766           5 :   qs = TMH_db->lookup_orders (TMH_db->cls,
     767             :                               po->instance_id,
     768             :                               &of,
     769             :                               &add_order,
     770             :                               po);
     771           5 :   if (0 > qs)
     772             :   {
     773           0 :     po->result = TALER_EC_GENERIC_DB_FETCH_FAILED;
     774             :   }
     775           5 :   if (TALER_EC_NONE != po->result)
     776             :   {
     777           0 :     GNUNET_break (0);
     778           0 :     return TALER_MHD_reply_with_error (connection,
     779             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     780             :                                        po->result,
     781             :                                        NULL);
     782             :   }
     783           5 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) &&
     784           3 :        (! GNUNET_TIME_relative_is_zero (of.timeout)) )
     785             :   {
     786           1 :     struct TMH_MerchantInstance *mi = hc->instance;
     787             : 
     788             :     /* setup timeout heap (if not yet exists) */
     789           1 :     if (NULL == order_timeout_heap)
     790             :       order_timeout_heap
     791           1 :         = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
     792           1 :     po->hn = GNUNET_CONTAINER_heap_insert (order_timeout_heap,
     793             :                                            po,
     794             :                                            po->long_poll_timeout.abs_value_us);
     795           1 :     po->long_poll_timeout = GNUNET_TIME_relative_to_absolute (of.timeout);
     796           1 :     po->of = of;
     797           1 :     GNUNET_CONTAINER_DLL_insert (mi->po_head,
     798             :                                  mi->po_tail,
     799             :                                  po);
     800           1 :     po->in_dll = true;
     801           1 :     if (NULL == mi->po_eh)
     802             :     {
     803           1 :       struct GNUNET_DB_EventHeaderP change_eh = {
     804           1 :         .type = htons (TALER_DBEVENT_MERCHANT_ORDERS_CHANGE),
     805           1 :         .size = htons (sizeof (change_eh))
     806             :       };
     807             : 
     808           1 :       mi->po_eh = TMH_db->event_listen (TMH_db->cls,
     809             :                                         &change_eh,
     810             :                                         GNUNET_TIME_UNIT_FOREVER_REL,
     811             :                                         &resume_by_event,
     812             :                                         mi);
     813             :     }
     814           1 :     MHD_suspend_connection (connection);
     815             :     {
     816             :       struct TMH_PendingOrder *pot;
     817             : 
     818             :       /* start timeout task */
     819           1 :       pot = GNUNET_CONTAINER_heap_peek (order_timeout_heap);
     820           1 :       if (NULL != order_timeout_task)
     821           0 :         GNUNET_SCHEDULER_cancel (order_timeout_task);
     822           1 :       order_timeout_task = GNUNET_SCHEDULER_add_at (pot->long_poll_timeout,
     823             :                                                     &order_timeout,
     824             :                                                     NULL);
     825             :     }
     826           1 :     return MHD_YES;
     827             :   }
     828           4 :   return TALER_MHD_REPLY_JSON_PACK (
     829             :     connection,
     830             :     MHD_HTTP_OK,
     831             :     GNUNET_JSON_pack_array_incref ("orders",
     832             :                                    po->pa));
     833             : }
     834             : 
     835             : 
     836             : /* end of taler-merchant-httpd_private-get-orders.c */

Generated by: LCOV version 1.14