gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: more skeleton logic for merchant


From: gnunet
Subject: [taler-merchant] branch master updated: more skeleton logic for merchant's new /kyc handler
Date: Sun, 10 Oct 2021 14:39:26 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new e668bcde more skeleton logic for merchant's new /kyc handler
e668bcde is described below

commit e668bcdeb5317f99f39957425ce735139c01810d
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Oct 10 14:39:24 2021 +0200

    more skeleton logic for merchant's new /kyc handler
---
 src/backend/taler-merchant-httpd.c                 |   1 +
 src/backend/taler-merchant-httpd.h                 |   2 +-
 .../taler-merchant-httpd_post-orders-ID-pay.c      |  36 +-
 ...r-merchant-httpd_private-get-instances-ID-kyc.c | 573 ++++++++++++++++++++-
 ...r-merchant-httpd_private-get-instances-ID-kyc.h |   8 +
 5 files changed, 591 insertions(+), 29 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index feb90b28..1023c08c 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -269,6 +269,7 @@ do_shutdown (void *cls)
   (void) cls;
   TMH_force_ac_resume ();
   TMH_force_pc_resume ();
+  TMH_force_kyc_resume ();
   TMH_force_rc_resume ();
   TMH_force_post_transfers_resume ();
   TMH_force_tip_pickup_resume ();
diff --git a/src/backend/taler-merchant-httpd.h 
b/src/backend/taler-merchant-httpd.h
index 60bd9ce3..bdb99704 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -663,7 +663,7 @@ TMH_reload_instances (const char *id);
  * @param hash the hash to check against
  * @return #GNUNET_OK if the @a token matches
  */
-int
+enum GNUNET_GenericReturnValue
 TMH_check_auth (const char *token,
                 const struct GNUNET_ShortHashCode *salt,
                 const struct GNUNET_HashCode *hash);
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index 7ec76fc5..72abde23 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -416,10 +416,6 @@ abort_active_deposits (struct PayContext *pc)
 }
 
 
-/**
- * Force all pay contexts to be resumed as we are about
- * to shut down MHD.
- */
 void
 TMH_force_pc_resume ()
 {
@@ -1611,10 +1607,11 @@ parse_pay (struct MHD_Connection *connection,
     {
       GNUNET_break_op (0);
       GNUNET_JSON_parse_free (spec);
-      return (MHD_YES == TALER_MHD_reply_with_error (connection,
-                                                     MHD_HTTP_BAD_REQUEST,
-                                                     
TALER_EC_GENERIC_PARAMETER_MISSING,
-                                                     "'coins' must be an 
array"))
+      return (MHD_YES ==
+              TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_BAD_REQUEST,
+                                          TALER_EC_GENERIC_PARAMETER_MISSING,
+                                          "'coins' must be an array"))
               ? GNUNET_NO
               : GNUNET_SYSERR;
     }
@@ -1924,24 +1921,15 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler 
*rh,
   GNUNET_assert (GNUNET_NO == pc->suspended);
   if (0 != pc->response_code)
   {
-    MHD_RESULT res;
-
     /* We are *done* processing the request, just queue the response (!) */
     if (UINT_MAX == pc->response_code)
     {
       GNUNET_break (0);
       return MHD_NO; /* hard error */
     }
-    res = MHD_queue_response (connection,
-                              pc->response_code,
-                              pc->response);
-    MHD_destroy_response (pc->response);
-    pc->response = NULL;
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Queueing response (%u) for POST /orders/$ID/pay (%s).\n",
-                (unsigned int) pc->response_code,
-                res ? "OK" : "FAILED");
-    return res;
+    return MHD_queue_response (connection,
+                               pc->response_code,
+                               pc->response);
   }
   {
     enum GNUNET_GenericReturnValue ret;
@@ -1961,10 +1949,10 @@ TMH_post_orders_ID_pay (const struct TMH_RequestHandler 
*rh,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suspending pay handling while working with the exchange\n");
   GNUNET_assert (NULL == pc->timeout_task);
-  pc->timeout_task =
-    GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
-                                  &handle_pay_timeout,
-                                  pc);
+  pc->timeout_task
+    = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
+                                    &handle_pay_timeout,
+                                    pc);
   execute_pay_transaction (pc);
   return MHD_YES;
 }
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c 
b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
index b3422f3f..bc9aa76f 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
@@ -28,6 +28,426 @@
 #include <taler/taler_json_lib.h>
 
 
+/**
+ * We do not re-check an acceptable KYC status for
+ * a month, as usually a KYC never expires.
+ */
+#define STALE_KYC_TIMEOUT GNUNET_TIME_UNIT_MONTHS
+
+
+/**
+ * Information we keep per /kyc request.
+ */
+struct KycContext;
+
+
+/**
+ * Structure for tracking requests to the exchange's
+ * ``/kyc-check`` API.
+ */
+struct ExchangeKycRequest
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct ExchangeKycRequest *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct ExchangeKycRequest *prev;
+
+  /**
+   * KYC request this exchange request is made for.
+   */
+  struct KycContext *kc;
+
+  /**
+   * Hash of the wire account (with salt) we are checking.
+   */
+  struct GNUNET_HashCode h_wire;
+
+  /**
+   * Handle for the actual HTTP request to the exchange.
+   */
+  // struct TALER_EXCHANGE_KycHandle *kyc;
+
+  /**
+   * KYC number used by the exchange.
+   */
+  uint64_t exchange_kyc_serial;
+
+  /**
+   * Our account's payto URI.
+   */
+  char *payto_uri;
+
+  /**
+   * Base URL of the exchange.
+   */
+  char *exchange_url;
+
+  /**
+   * Timestamp when we last got a reply from the exchange.
+   */
+  struct GNUNET_TIME_Absolute last_check;
+
+  /**
+   * Last KYC status returned by the exchange.
+   */
+  bool kyc_ok;
+};
+
+
+/**
+ * Information we keep per /kyc request.
+ */
+struct KycContext
+{
+  /**
+   * Stored in a DLL.
+   */
+  struct KycContext *next;
+
+  /**
+   * Stored in a DLL.
+   */
+  struct KycContext *prev;
+
+  /**
+   * Connection we are handling.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Instance we are serving.
+   */
+  struct TMH_MerchantInstance *mi;
+
+  /**
+   * Our handler context.
+   */
+  struct TMH_HandlerContext *hc;
+
+  /**
+   * Task to trigger on request timeout, or NULL.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * Response to return, NULL if we don't have one yet.
+   */
+  struct MHD_Response *response;
+
+  /**
+   * JSON array where we are building up the array with
+   * pending KYC operations.
+   */
+  json_t *pending_kycs;
+
+  /**
+   * JSON array where we are building up the array with
+   * troubled KYC operations.
+   */
+  json_t *timeout_kycs;
+
+  /**
+   * Head of DLL of requests we are making to an
+   * exchange to inquire about the latest KYC status.
+   */
+  struct ExchangeKycRequest *exchange_pending_head;
+
+  /**
+   * Tail of DLL of requests we are making to an
+   * exchange to inquire about the latest KYC status.
+   */
+  struct ExchangeKycRequest *exchange_pending_tail;
+
+  /**
+   * Set to the exchange URL, or NULL to not filter by
+   * exchange.
+   */
+  const char *exchange_url;
+
+  /**
+   * Set to the h_wire of the merchant account if
+   * @a have_h_wire is true, used to filter by account.
+   */
+  struct GNUNET_HashCode h_wire;
+
+  /**
+   * How long are we willing to wait for the exchange(s)?
+   */
+  struct GNUNET_TIME_Relative timeout;
+
+  /**
+   * HTTP status code to use for the reply, i.e 200 for "OK".
+   * Special value UINT_MAX is used to indicate hard errors
+   * (no reply, return #MHD_NO).
+   */
+  unsigned int response_code;
+
+  /**
+   * #GNUNET_NO if the @e connection was not suspended,
+   * #GNUNET_YES if the @e connection was suspended,
+   * #GNUNET_SYSERR if @e connection was resumed to as
+   * part of #MH_force_pc_resume during shutdown.
+   */
+  enum GNUNET_GenericReturnValue suspended;
+
+  /**
+   * True if @e h_wire was given.
+   */
+  bool have_h_wire;
+};
+
+
+/**
+ * Head of DLL.
+ */
+static struct KycContext *kc_head;
+
+/**
+ * Tail of DLL.
+ */
+static struct KycContext *kc_tail;
+
+
+void
+TMH_force_kyc_resume ()
+{
+  for (struct KycContext *kc = kc_head;
+       NULL != kc;
+       kc = kc->next)
+  {
+    if (NULL != kc->timeout_task)
+    {
+      GNUNET_SCHEDULER_cancel (kc->timeout_task);
+      kc->timeout_task = NULL;
+    }
+    if (GNUNET_YES == kc->suspended)
+    {
+      kc->suspended = GNUNET_SYSERR;
+      MHD_resume_connection (kc->connection);
+    }
+  }
+}
+
+
+/**
+ * Custom cleanup routine for a `struct KycContext`.
+ *
+ * @param cls the `struct KycContext` to clean up.
+ */
+static void
+kyc_context_cleanup (void *cls)
+{
+  struct KycContext *kc = cls;
+  struct ExchangeKycRequest *ekr;
+
+  while (NULL != (ekr = kc->exchange_pending_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
+                                 kc->exchange_pending_tail,
+                                 ekr);
+    // FIXME: TALER_EXCHANGE_kyc_check_cancel (ekr->kyc);
+    GNUNET_free (ekr->exchange_url);
+    GNUNET_free (ekr->payto_uri);
+    GNUNET_free (ekr);
+  }
+  if (NULL != kc->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (kc->timeout_task);
+    kc->timeout_task = NULL;
+  }
+  if (NULL != kc->response)
+  {
+    MHD_destroy_response (kc->response);
+    kc->response = NULL;
+  }
+  GNUNET_CONTAINER_DLL_remove (kc_head,
+                               kc_tail,
+                               kc);
+  json_decref (kc->pending_kycs);
+  json_decref (kc->timeout_kycs);
+  GNUNET_free (kc);
+}
+
+
+/**
+ * Resume the given KYC context and send the given response.
+ * Stores the response in the @a kc and signals MHD to resume
+ * the connection.  Also ensures MHD runs immediately.
+ *
+ * @param kc KYC context
+ * @param response_code response code to use
+ * @param response response data to send back
+ */
+static void
+resume_kyc_with_response (struct KycContext *kc,
+                          unsigned int response_code,
+                          struct MHD_Response *response)
+{
+  kc->response_code = response_code;
+  kc->response = response;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Resuming /kyc handling as exchange interaction is done (%u)\n",
+              response_code);
+  if (NULL != kc->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (kc->timeout_task);
+    kc->timeout_task = NULL;
+  }
+  GNUNET_assert (GNUNET_YES == kc->suspended);
+  kc->suspended = GNUNET_NO;
+  MHD_resume_connection (kc->connection);
+  TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
+}
+
+
+/**
+ * Resume KYC processing with an error.
+ *
+ * @param kc operation to resume
+ * @param http_status http status code to return
+ * @param ec taler error code to return
+ * @param msg human readable error message
+ */
+static void
+resume_kyc_with_error (struct KycContext *kc,
+                       unsigned int http_status,
+                       enum TALER_ErrorCode ec,
+                       const char *msg)
+{
+  resume_kyc_with_response (kc,
+                            http_status,
+                            TALER_MHD_make_error (ec,
+                                                  msg));
+}
+
+
+/**
+ * Handle a timeout for the processing of the kyc request.
+ *
+ * @param cls our `struct KycContext`
+ */
+static void
+handle_kyc_timeout (void *cls)
+{
+  struct KycContext *kc = cls;
+
+  kc->timeout_task = NULL;
+  GNUNET_assert (GNUNET_YES == kc->suspended);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Resuming KYC with error after timeout\n");
+  // FIXME: we _still_ need to produce a valid
+  // JSON reply, not just an error according to the spec!
+  resume_kyc_with_error (kc,
+                         MHD_HTTP_GATEWAY_TIMEOUT,
+                         TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
+                         NULL);
+}
+
+
+#if FIXME
+static void
+exchange_check_cb (void *cls,
+                   ...)
+{
+  struct ExchangeKycRequest *ekr = cls;
+  struct KycContext *kc = ekr->kc;
+
+  ekr->kyc = NULL;
+  // build up reply in 'kc'
+  if (error_reply)
+  {
+    // logging ...
+    kc->response_code = MHD_HTTP_BAD_GATEWAY;
+  }
+  else
+  {
+    // ...
+  }
+
+  GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
+                               kc->exchange_pending_tail,
+                               ekr);
+  GNUNET_free (ekr->exchange_url);
+  GNUNET_free (ekr->payto_uri);
+  GNUNET_free (ekr);
+  if (NULL != kc->exchange_pending_head)
+    return; /* wait for more */
+  /* All exchange requests done, create final
+     big respose from cummulated replies */
+  if ( (0 == json_array_size (kc->pending_kycs)) &&
+       (0 == json_array_size (kc->timeout_kycs)) )
+  {
+    /* special case: all KYC operations did succeed
+       after we asked at the exchanges => 204 */
+    response = empty;
+    resume_kyc_with_response (kc,
+                              MHD_HTTP_NO_CONTENT,
+                              response);
+    return;
+  }
+  response = make_response (kc);
+  resume_kyc_with_response (kc,
+                            kc->response_code,
+                            response);
+}
+
+
+#endif
+
+
+/**
+ * Function called from ``account_kyc_get_status``
+ * with KYC status information for this merchant.
+ *
+ * @param cls our `struct KycContext *`
+ * @param h_wire hash of the wire account
+ * @param exchange_kyc_serial serial number for the KYC process at the 
exchange, 0 if unknown
+ * @param payto_uri payto:// URI of the merchant's bank account
+ * @param exchange_url base URL of the exchange for which this is a status
+ * @param last_check when did we last get an update on our KYC status from the 
exchange
+ * @param kyc_ok true if we satisfied the KYC requirements
+ */
+static void
+kyc_status_cb (void *cls,
+               const struct GNUNET_HashCode *h_wire,
+               uint64_t exchange_kyc_serial,
+               const char *payto_uri,
+               const char *exchange_url,
+               struct GNUNET_TIME_Absolute last_check,
+               bool kyc_ok)
+{
+  struct KycContext *kc = cls;
+  struct ExchangeKycRequest *ekr;
+
+  if (kyc_ok &&
+      (GNUNET_TIME_absolute_get_duration (last_check).rel_value_us <
+       STALE_KYC_TIMEOUT.rel_value_us) )
+    return; /* KYC ok, ignore! */
+  kc->response_code = MHD_HTTP_ACCEPTED;
+  ekr = GNUNET_new (struct ExchangeKycRequest);
+  GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
+                               kc->exchange_pending_tail,
+                               ekr);
+  ekr->h_wire = *h_wire;
+  ekr->exchange_kyc_serial = exchange_kyc_serial;
+  ekr->exchange_url = GNUNET_strdup (exchange_url);
+  ekr->payto_uri = GNUNET_strdup (payto_uri);
+  ekr->last_check = last_check;
+  ekr->kyc_ok = kyc_ok;
+  ekr->kc = kc;
+#if FIXME
+  ekr->kyc = TALER_EXCHANGE_kyc_check (exchange_url,
+                                       &exchange_check_cb,
+                                       ekr);
+#endif
+}
+
+
 /**
  * Check the KYC status of an instance.
  *
@@ -41,6 +461,151 @@ get_instances_ID_kyc (struct TMH_MerchantInstance *mi,
                       struct MHD_Connection *connection,
                       struct TMH_HandlerContext *hc)
 {
+  struct KycContext *kc = hc->ctx;
+
+  if (NULL == kc)
+  {
+    kc = GNUNET_new (struct KycContext);
+    hc->ctx = kc;
+    hc->cc = &kyc_context_cleanup;
+    GNUNET_CONTAINER_DLL_insert (kc_head,
+                                 kc_tail,
+                                 kc);
+    kc->connection = connection;
+    kc->hc = hc;
+    kc->pending_kycs = json_array ();
+    GNUNET_assert (NULL != kc->pending_kycs);
+    kc->timeout_kycs = json_array ();
+    GNUNET_assert (NULL != kc->timeout_kycs);
+
+    /* process 'timeout_ms' argument */
+    {
+      const char *long_poll_timeout_s;
+
+      long_poll_timeout_s
+        = MHD_lookup_connection_value (connection,
+                                       MHD_GET_ARGUMENT_KIND,
+                                       "timeout_ms");
+      if (NULL != long_poll_timeout_s)
+      {
+        unsigned int timeout_ms;
+        char dummy;
+
+        if (1 != sscanf (long_poll_timeout_s,
+                         "%u%c",
+                         &timeout_ms,
+                         &dummy))
+        {
+          GNUNET_break_op (0);
+          return TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             "timeout_ms must be non-negative 
number");
+        }
+        kc->timeout = GNUNET_TIME_relative_multiply (
+          GNUNET_TIME_UNIT_MILLISECONDS,
+          timeout_ms);
+        kc->timeout_task
+          = GNUNET_SCHEDULER_add_delayed (kc->timeout,
+                                          &handle_kyc_timeout,
+                                          kc);
+      }
+    } /* end timeout processing */
+
+    /* process 'exchange_url' argument */
+    kc->exchange_url = MHD_lookup_connection_value (connection,
+                                                    MHD_GET_ARGUMENT_KIND,
+                                                    "exchange_url");
+    if ( (NULL != kc->exchange_url) &&
+         (! TALER_url_valid_charset (kc->exchange_url) ||
+          ( (0 != strncasecmp (kc->exchange_url,
+                               "http://";,
+                               strlen ("http://";))) &&
+            (0 != strncasecmp (kc->exchange_url,
+                               "https://";,
+                               strlen ("https://";))) ) ) )
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                         "exchange_url must be a valid HTTP(s) 
URL");
+    }
+
+    /* process 'h_wire' argument */
+    {
+      const char *h_wire_s;
+
+      h_wire_s
+        = MHD_lookup_connection_value (connection,
+                                       MHD_GET_ARGUMENT_KIND,
+                                       "h_wire");
+      if (NULL != h_wire_s)
+      {
+        if (GNUNET_OK !=
+            GNUNET_STRINGS_string_to_data (h_wire_s,
+                                           strlen (h_wire_s),
+                                           &kc->h_wire,
+                                           sizeof (kc->h_wire)))
+        {
+          GNUNET_break_op (0);
+          return TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                             "h_wire must be Crockford base32 
encoded hash");
+        }
+        kc->have_h_wire = true;
+      }
+    } /* end of h_wire processing */
+
+    /* Check our database */
+    {
+      enum GNUNET_DB_QueryStatus qs;
+
+      qs = TMH_db->account_kyc_get_status (TMH_db->cls,
+                                           mi->settings.id,
+                                           kc->have_h_wire
+                                           ? &kc->h_wire
+                                           : NULL,
+                                           kc->exchange_url,
+                                           &kyc_status_cb,
+                                           kc);
+      if (qs < 0)
+      {
+        GNUNET_break (0);
+        return TALER_MHD_reply_with_ec (connection,
+                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                        "account_kyc_get_status");
+      }
+    }
+    if (NULL == kc->exchange_pending_head)
+      return TALER_MHD_reply_static (connection,
+                                     MHD_HTTP_NO_CONTENT,
+                                     NULL,
+                                     NULL,
+                                     0);
+    MHD_suspend_connection (connection);
+    kc->suspended = GNUNET_YES;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Suspending KYC request handling while checking with the 
exchange(s)\n");
+    return MHD_YES;
+  }
+  if (GNUNET_SYSERR == kc->suspended)
+    return MHD_NO; /* during shutdown, we don't generate any more replies */
+  GNUNET_assert (GNUNET_NO == kc->suspended);
+  if (0 != kc->response_code)
+  {
+    /* We are *done* processing the request, just queue the response (!) */
+    if (UINT_MAX == kc->response_code)
+    {
+      GNUNET_break (0);
+      return MHD_NO; /* hard error */
+    }
+    return MHD_queue_response (connection,
+                               kc->response_code,
+                               kc->response);
+  }
+  /* we should never get here */
   GNUNET_break (0);
   return MHD_NO;
 }
@@ -48,8 +613,8 @@ get_instances_ID_kyc (struct TMH_MerchantInstance *mi,
 
 MHD_RESULT
 TMH_private_get_instances_ID_kyc (const struct TMH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    struct TMH_HandlerContext *hc)
+                                  struct MHD_Connection *connection,
+                                  struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi = hc->instance;
 
@@ -61,8 +626,8 @@ TMH_private_get_instances_ID_kyc (const struct 
TMH_RequestHandler *rh,
 
 MHD_RESULT
 TMH_private_get_instances_default_ID_kyc (const struct TMH_RequestHandler *rh,
-                                            struct MHD_Connection *connection,
-                                            struct TMH_HandlerContext *hc)
+                                          struct MHD_Connection *connection,
+                                          struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi;
 
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.h 
b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.h
index 58762c86..682f65a6 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.h
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.h
@@ -27,6 +27,14 @@
 #include "taler-merchant-httpd.h"
 
 
+/**
+ * Force all KYC contexts to be resumed as we are about
+ * to shut down MHD.
+ */
+void
+TMH_force_kyc_resume (void);
+
+
 /**
  * Change the instance's kyc settings.
  * This is the handler called using the instance's own kycentication.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]