gnunet-svn
[Top][All Lists]
Advanced

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

[taler-anastasis] branch master updated: first rough pass over backend


From: gnunet
Subject: [taler-anastasis] branch master updated: first rough pass over backend
Date: Mon, 08 Feb 2021 14:32:48 +0100

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

grothoff pushed a commit to branch master
in repository anastasis.

The following commit(s) were added to refs/heads/master by this push:
     new 5464120  first rough pass over backend
5464120 is described below

commit 5464120715ccfe37b3252ff0f0de8788f4a01227
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Feb 8 14:32:46 2021 +0100

    first rough pass over backend
---
 contrib/gana                                      |   2 +-
 src/backend/Makefile.am                           |  25 +-
 src/backend/anastasis-httpd.c                     |  17 +
 src/backend/anastasis-httpd.h                     |   5 +
 src/backend/anastasis-httpd_config.c              |  18 +-
 src/backend/anastasis-httpd_policy.c              |   6 +-
 src/backend/anastasis-httpd_policy_upload.c       |  14 +-
 src/backend/anastasis-httpd_truth.c               | 984 +++++++++++-----------
 src/backend/anastasis-httpd_truth_upload.c        |   4 +-
 src/backend/anastasis_authorization_plugin.c      |  38 +-
 src/backend/anastasis_authorization_plugin_file.c |   3 -
 src/backend/anastasis_authorization_plugin_sms.c  | 350 ++++++--
 src/include/anastasis_authorization_lib.h         |  21 +-
 src/include/anastasis_authorization_plugin.h      |   7 -
 src/include/anastasis_util_lib.h                  |   7 +-
 15 files changed, 842 insertions(+), 659 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index df16026..f5b0263 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit df1602616af682c4960d08784b7acba5896dcc81
+Subproject commit f5b02632094606010ed1c28c21931c81fa852d2b
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 7d4ab65..6b170f9 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -22,8 +22,7 @@ libanastasisauthorization_la_LDFLAGS = \
 
 plugin_LTLIBRARIES = \
   libanastasis_plugin_authorization_file.la \
-       libanastasis_plugin_authorization_email.la \
-       libanastasis_plugin_authorization_sms.la
+  libanastasis_plugin_authorization_sms.la
 libanastasis_plugin_authorization_file_la_SOURCES = \
   anastasis_authorization_plugin_file.c
 libanastasis_plugin_authorization_file_la_LIBADD = \
@@ -34,16 +33,18 @@ libanastasis_plugin_authorization_file_la_LDFLAGS = \
   -ltalerutil \
   -lgnunetutil \
   $(XLIB)
-libanastasis_plugin_authorization_email_la_SOURCES = \
-  anastasis_authorization_plugin_email.c
-libanastasis_plugin_authorization_email_la_LIBADD = \
-  $(LTLIBINTL)
-libanastasis_plugin_authorization_email_la_LDFLAGS = \
-  $(ANASTASIS_PLUGIN_LDFLAGS) \
-  -ljansson \
-  -ltalerutil \
-  -lgnunetutil \
-  $(XLIB)
+
+#libanastasis_plugin_authorization_email_la_SOURCES = \
+#  anastasis_authorization_plugin_email.c
+#libanastasis_plugin_authorization_email_la_LIBADD = \
+#  $(LTLIBINTL)
+#libanastasis_plugin_authorization_email_la_LDFLAGS = \
+#  $(ANASTASIS_PLUGIN_LDFLAGS) \
+#  -ljansson \
+#  -ltalerutil \
+#  -lgnunetutil \
+#  $(XLIB)
+
 libanastasis_plugin_authorization_sms_la_SOURCES = \
   anastasis_authorization_plugin_sms.c
 libanastasis_plugin_authorization_sms_la_LIBADD = \
diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c
index e5ac332..0dcecf1 100644
--- a/src/backend/anastasis-httpd.c
+++ b/src/backend/anastasis-httpd.c
@@ -52,6 +52,11 @@ struct TALER_Amount AH_annual_fee;
  */
 struct TALER_Amount AH_insurance;
 
+/**
+ * Cost for secure question truth download.
+ */
+struct TALER_Amount AH_question_cost;
+
 /**
  * Our configuration.
  */
@@ -590,6 +595,18 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  if (GNUNET_OK !=
+      TALER_config_get_amount (config,
+                               "authorization-question",
+                               "COST",
+                               &AH_question_cost))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "authorization-question",
+                               "COST");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
   if (GNUNET_OK !=
       TALER_config_get_amount (config,
                                "anastasis",
diff --git a/src/backend/anastasis-httpd.h b/src/backend/anastasis-httpd.h
index 96282e2..7f50aff 100644
--- a/src/backend/anastasis-httpd.h
+++ b/src/backend/anastasis-httpd.h
@@ -149,6 +149,11 @@ extern struct TALER_Amount AH_annual_fee;
  */
 extern struct TALER_Amount AH_insurance;
 
+/**
+ * Cost for secure question truth download.
+ */
+extern struct TALER_Amount AH_question_cost;
+
 /**
  * Our Taler backend to process payments.
  */
diff --git a/src/backend/anastasis-httpd_config.c 
b/src/backend/anastasis-httpd_config.c
index e7e24bb..bc5cff3 100644
--- a/src/backend/anastasis-httpd_config.c
+++ b/src/backend/anastasis-httpd_config.c
@@ -23,8 +23,9 @@
 #include "platform.h"
 #include <jansson.h>
 #include "anastasis-httpd_config.h"
+#include "anastasis-httpd.h"
 #include <taler/taler_json_lib.h>
-
+#include "anastasis_authorization_lib.h"
 
 /**
  * Add enabled methods and their fees to the ``/config`` response.
@@ -65,7 +66,7 @@ add_methods (void *cls,
                       "method",
                       section,
                       "cost",
-                      TALER_JSON_from_amount (&AH_question_cost));
+                      TALER_JSON_from_amount (&cost));
   GNUNET_assert (
     0 ==
     json_array_append_new (method_arr,
@@ -87,6 +88,19 @@ AH_handler_config (struct TMH_RequestHandler *rh,
   json_t *method_arr = json_array ();
 
   GNUNET_assert (NULL != method_arr);
+  {
+    json_t *method;
+
+    method = json_pack ("{s:s, s:o}",
+                        "method",
+                        "question",
+                        "cost",
+                        TALER_JSON_from_amount (&AH_question_cost));
+    GNUNET_assert (
+      0 ==
+      json_array_append_new (method_arr,
+                             method));
+  }
   GNUNET_CONFIGURATION_iterate_sections (AH_cfg,
                                          &add_methods,
                                          method_arr);
diff --git a/src/backend/anastasis-httpd_policy.c 
b/src/backend/anastasis-httpd_policy.c
index 23873c0..0a1396a 100644
--- a/src/backend/anastasis-httpd_policy.c
+++ b/src/backend/anastasis-httpd_policy.c
@@ -158,10 +158,12 @@ AH_policy_get (struct MHD_Connection *connection,
   struct GNUNET_HashCode recovery_data_hash;
   enum ANASTASIS_DB_AccountStatus as;
   MHD_RESULT ret;
+  uint32_t version;
 
   as = db->lookup_account (db->cls,
                            account_pub,
-                           &recovery_data_hash);
+                           &recovery_data_hash,
+                           &version);
   switch (as)
   {
   case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED:
@@ -209,7 +211,7 @@ AH_policy_get (struct MHD_Connection *connection,
           GNUNET_break_op (0);
           return TALER_MHD_reply_with_error (connection,
                                              MHD_HTTP_BAD_REQUEST,
-                                             
TALER_EC_ANASTASIS_BAD_IF_NONE_MATCH,
+                                             
TALER_EC_ANASTASIS_POLICY_BAD_IF_NONE_MATCH,
                                              "Etag must be a base32-encoded 
SHA-512 hash");
         }
         if (0 == GNUNET_memcmp (&inm_h,
diff --git a/src/backend/anastasis-httpd_policy_upload.c 
b/src/backend/anastasis-httpd_policy_upload.c
index 69b3ee5..f37cb39 100644
--- a/src/backend/anastasis-httpd_policy_upload.c
+++ b/src/backend/anastasis-httpd_policy_upload.c
@@ -731,8 +731,8 @@ AH_handler_policy_post (
           connection,
           MHD_HTTP_BAD_REQUEST,
           (NULL == lens)
-          ? TALER_EC_ANASTASIS_MISSING_CONTENT_LENGTH
-          : TALER_EC_ANASTASIS_MALFORMED_CONTENT_LENGTH,
+          ? TALER_EC_ANASTASIS_POLICY_MISSING_CONTENT_LENGTH
+          : TALER_EC_ANASTASIS_POLICY_MALFORMED_CONTENT_LENGTH,
           NULL);
       }
       if (len / 1024 / 1024 >= AH_upload_limit_mb)
@@ -750,7 +750,7 @@ AH_handler_policy_post (
                              "malloc");
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_PAYLOAD_TOO_LARGE,
-                                           
TALER_EC_ANASTASIS_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
+                                           
TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
                                            NULL);
       }
       puc->upload_size = (size_t) len;
@@ -772,7 +772,7 @@ AH_handler_policy_post (
         GNUNET_break_op (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_BAD_REQUEST,
-                                           
TALER_EC_ANASTASIS_BAD_POLICY_SIGNATURE,
+                                           
TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE,
                                            "Anastasis-Policy-Signature does 
not include a base32-encoded EdDSA signature");
       }
     }
@@ -793,7 +793,7 @@ AH_handler_policy_post (
         GNUNET_break_op (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_BAD_REQUEST,
-                                           TALER_EC_ANASTASIS_BAD_IF_MATCH,
+                                           
TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH,
                                            "Etag must include a base32-encoded 
SHA-512 hash");
       }
     }
@@ -815,7 +815,7 @@ AH_handler_policy_post (
         GNUNET_break_op (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_FORBIDDEN,
-                                           
TALER_EC_ANASTASIS_GENERIC_BAD_SIGNATURE,
+                                           
TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE,
                                            "Anastasis-Policy-Signature");
       }
     }
@@ -1015,7 +1015,7 @@ AH_handler_policy_post (
       GNUNET_break_op (0);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
-                                         
TALER_EC_ANASTASIS_INVALID_POLICY_UPLOAD,
+                                         
TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD,
                                          "Data uploaded does not match Etag 
promise");
     }
   }
diff --git a/src/backend/anastasis-httpd_truth.c 
b/src/backend/anastasis-httpd_truth.c
index 0b7cdd3..fd101ac 100644
--- a/src/backend/anastasis-httpd_truth.c
+++ b/src/backend/anastasis-httpd_truth.c
@@ -40,6 +40,27 @@
 
 struct GetContext
 {
+
+  /**
+   * Payment Identifier
+   */
+  struct ANASTASIS_PaymentSecretP payment_identifier;
+
+  /**
+   * Public key of the challenge which is solved.
+   */
+  struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
+
+  /**
+   * true if client provided a payment secret / order ID?
+   */
+  struct TALER_Amount challenge_cost;
+
+  /**
+   * Our handler context.
+   */
+  struct TM_HandlerContext *hc;
+
   /**
    * Kept in DLL for shutdown handling while suspended.
    */
@@ -65,11 +86,6 @@ struct GetContext
    */
   struct ANASTASIS_AUTHORIZATION_State *as;
 
-  /**
-   * Public key of the challenge which is solved.
-   */
-  struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
-
   /**
    * Used while we are awaiting proposal creation.
    */
@@ -85,48 +101,27 @@ struct GetContext
    */
   struct MHD_Response *resp;
 
-  /**
-   * Timestamp of the order in @e existing_order_id. Used to
-   * select the most recent unpaid offer.
-   */
-  struct GNUNET_TIME_Absolute existing_pi_timestamp;
-
-  /**
-   * Payment Identifier
-   */
-  struct ANASTASIS_PaymentSecretP payment_identifier;
-
   /**
    * HTTP response code to use on resume, if resp is set.
    */
   unsigned int response_code;
 
-  /**
-   * Whether to generate a claim token.
-   */
-  bool make_claim_token;
-
-  /**
-   * The claim token
-   */
-  struct TALER_ClaimTokenP claim_token;
-
   /**
    * true if client provided a payment secret / order ID?
    */
   bool payment_identifier_provided;
 
-  /**
-   * true if client provided a payment secret / order ID?
-   */
-  struct TALER_Amount challenge_cost;
 };
 
+
 /**
- * Linked list over all authorization processes
+ * Head of linked list over all authorization processes
  */
-
 static struct GetContext *gc_head;
+
+/**
+ * Tail of linked list over all authorization processes
+ */
 static struct GetContext *gc_tail;
 
 
@@ -151,6 +146,7 @@ AH_truth_shutdown (void)
 
 /**
  * Callback used to notify the application about completed requests.
+ * Cleans up the requests data structures.
  *
  * @param hc
  */
@@ -161,6 +157,7 @@ request_done (struct TM_HandlerContext *hc)
 
   if (NULL == gc)
     return;
+  hc->cc = NULL;
   GNUNET_CONTAINER_DLL_remove (gc_head,
                                gc_tail,
                                gc);
@@ -179,20 +176,16 @@ request_done (struct TM_HandlerContext *hc)
  * Transmit a payment request for @a order_id on @a connection
  *
  * @param gc context to make payment request for
- * @return MHD response to use
  */
-// FIXME: share logic with anastasis-http_policy_upload.c!
-static int
+static void
 make_payment_request (struct GetContext *gc)
 {
   struct MHD_Response *resp;
 
-  /* request payment via Taler */
   resp = MHD_create_response_from_buffer (0,
                                           NULL,
                                           MHD_RESPMEM_PERSISTENT);
-  if (NULL == resp)
-    return GNUNET_SYSERR;
+  GNUNET_assert (NULL != resp);
   TALER_MHD_add_global_headers (resp);
   {
     char *hdr;
@@ -201,7 +194,6 @@ make_payment_request (struct GetContext *gc)
     order_id = GNUNET_STRINGS_data_to_string_alloc (
       &gc->payment_identifier,
       sizeof (gc->payment_identifier));
-
     GNUNET_asprintf (&hdr,
                      "taler://pay/%s/%s",
                      AH_backend_url,
@@ -215,7 +207,6 @@ make_payment_request (struct GetContext *gc)
   }
   gc->resp = resp;
   gc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
-  return GNUNET_OK;
 }
 
 
@@ -223,7 +214,7 @@ make_payment_request (struct GetContext *gc)
  * Callbacks of this type are used to serve the result of submitting a
  * /contract request to a merchant.
  *
- * @param cls our `struct PolicyUploadContext`
+ * @param cls our `struct GetContext`
  * @param por response details
  */
 static void
@@ -233,6 +224,7 @@ proposal_cb (void *cls,
   struct GetContext *gc = cls;
   enum GNUNET_DB_QueryStatus qs;
 
+  gc->hc->cc = NULL;
   GNUNET_CONTAINER_DLL_remove (gc_head,
                                gc_tail,
                                gc);
@@ -248,7 +240,7 @@ proposal_cb (void *cls,
     gc->resp = TALER_MHD_make_json_pack (
       "{s:I, s:s, s:I, s:I, s:O?}",
       "code",
-      (json_int_t) TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR,
+      (json_int_t) TALER_EC_ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR,
       "hint",
       "Failed to setup order with merchant backend",
       "backend-ec",
@@ -258,12 +250,9 @@ proposal_cb (void *cls,
       "backend-reply",
       por->hr.reply);
     GNUNET_assert (NULL != gc->resp);
-    gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+    gc->response_code = MHD_HTTP_BAD_GATEWAY;
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Storing payment request for order `%s'\n",
-              por->details.ok.order_id);
   qs = db->record_challenge_payment (db->cls,
                                      &gc->truth_public_key,
                                      &gc->payment_identifier,
@@ -276,83 +265,14 @@ proposal_cb (void *cls,
     gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Obtained fresh order `%s'\n",
-              por->details.ok.order_id);
-  if (GNUNET_OK !=
-      make_payment_request (por->details.ok.order_id))
-  {
-    // FIXME: generate error!
-  }
-}
-
-
-static MHD_RESULT
-return_key_share (const struct
-                  ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
-                  struct MHD_Connection *connection)
-{
-  // load encrypted keyshare from db
-  enum GNUNET_DB_QueryStatus qs;
-  void *encrypted_keyshare;
-  size_t encrypted_keyshare_size;
-  struct MHD_Response *resp;
-  MHD_RESULT ret;
-
-  qs = db->get_key_share (db->cls,
-                          truth_public_key,
-                          &encrypted_keyshare,
-                          &encrypted_keyshare_size);
-
-  if (qs != GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)
-  {
-    return MHD_HTTP_NOT_FOUND;
-  }
-
-  resp = MHD_create_response_from_buffer (encrypted_keyshare_size,
-                                          encrypted_keyshare,
-                                          MHD_RESPMEM_MUST_FREE);
-  TALER_MHD_add_global_headers (resp);
-  ret = MHD_queue_response (connection,
-                            MHD_HTTP_OK,
-                            resp);
-  MHD_destroy_response (resp);
-  return ret;
-}
-
-
-/**
- * Function called on all pending payments for the right
- * account.
- *
- * @param cls closure, our `struct BackupContext`
- * @param timestamp for how long have we been waiting
- * @param ps order id in the backend
- * @param amount how much is the order for
- */
-static void
-ongoing_payment_cb (void *cls,
-                    struct GNUNET_TIME_Absolute timestamp,
-                    const struct ANASTASIS_PaymentSecretP *ps,
-                    const struct TALER_Amount *amount)
-{
-  struct GetContext *gc = cls;
-
-  if (0 != TALER_amount_cmp (amount,
-                             &gc->challenge_cost))
-    return; /* can't re-use, fees changed */
-  if (gc->existing_pi_timestamp.abs_value_us < timestamp.abs_value_us)
-  {
-    gc->payment_identifier = *ps;
-    gc->existing_pi_timestamp = timestamp;
-  }
+  make_payment_request (gc);
 }
 
 
 /**
  * Callback to process a GET /check-payment request
  *
- * @param cls our `struct PolicyUploadContext`
+ * @param cls our `struct GetContext`
  * @param hr HTTP response details
  * @param osr order status
  */
@@ -364,225 +284,218 @@ check_payment_cb (void *cls,
 {
   struct GetContext *gc = cls;
 
-  /* refunds are not supported, verify */
   gc->cpo = NULL;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Payment status checked: %s\n",
-              osr->status ? "paid" : "unpaid");
+  gc->hc->cc = NULL;
   GNUNET_CONTAINER_DLL_remove (gc_head,
                                gc_tail,
                                gc);
   MHD_resume_connection (gc->connection);
   AH_trigger_daemon ();
 
-  if (osr->status)
+  switch (hr->http_status)
   {
-    enum GNUNET_DB_QueryStatus qs;
-
-    qs = db->update_challenge_payment (db->cls,
-                                       &gc->truth_public_key,
-                                       &gc->payment_identifier);
-    if (0 <= qs)
-      return; /* continue as planned */
+  case MHD_HTTP_OK:
+    GNUNET_assert (NULL != osr);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* We created this order before, how can it be not found now? */
     GNUNET_break (0);
-    gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
-                                     "challenge payment");
-    gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
-    return; /* continue as planned */
-  }
-  if (0 != gc->existing_pi_timestamp.abs_value_us)
-  {
-    /* repeat payment request */
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Repeating payment request\n");
-    if (GNUNET_OK !=
-        make_payment_request (gc))
+    gc->resp = TALER_MHD_make_error 
(TALER_EC_ANASTASIS_TRUTH_ORDER_DISAPPEARED,
+                                     NULL);
+    gc->response_code = MHD_HTTP_BAD_GATEWAY;
+    return;
+  case MHD_HTTP_BAD_GATEWAY:
+    gc->resp = TALER_MHD_make_error (
+      TALER_EC_ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD,
+      NULL);
+    gc->response_code = MHD_HTTP_BAD_GATEWAY;
+    return;
+  case MHD_HTTP_GATEWAY_TIMEOUT:
+    gc->resp = TALER_MHD_make_error 
(TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT,
+                                     "Timeout check payment status");
+    GNUNET_assert (NULL != gc->resp);
+    gc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
+    return;
+  default:
     {
-      // FIXME: generate error
+      char status[14];
+
+      GNUNET_snprintf (status,
+                       sizeof (status),
+                       "%u",
+                       hr->http_status);
+      gc->resp = TALER_MHD_make_error (
+        TALER_EC_ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS,
+        status);
+      GNUNET_assert (NULL != gc->resp);
+      gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      return;
     }
-    return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Timeout waiting for payment\n");
-  gc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
-                                   "Timeout awaiting promised payment");
-  GNUNET_assert (NULL != gc->resp);
-  gc->response_code = MHD_HTTP_REQUEST_TIMEOUT;
-}
 
-
-/**
- * Helper function used to ask our backend to await
- * a payment for the user's account.
- *
- * @param puc context to begin payment for.
- * @param timeout when to give up trying
- */
-static void
-await_payment (struct GetContext *gc,
-               struct GNUNET_TIME_Relative timeout)
-{
-  GNUNET_CONTAINER_DLL_insert (gc_tail,
-                               gc_head,
-                               gc);
-  MHD_suspend_connection (gc->connection);
+  switch (osr->status)
   {
-    char *order_id;
+  case TALER_MERCHANT_OSC_PAID:
+    {
+      enum GNUNET_DB_QueryStatus qs;
 
-    order_id = GNUNET_STRINGS_data_to_string_alloc (
-      &gc->payment_identifier,
-      sizeof (gc->payment_identifier));
-    gc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx,
-                                                 AH_backend_url,
-                                                 order_id,
-                                                 NULL /* our payments are NOT 
session-bound */,
-                                                 false,
-                                                 timeout,
-                                                 &check_payment_cb,
-                                                 gc);
-    GNUNET_free (order_id);
+      qs = db->update_challenge_payment (db->cls,
+                                         &gc->truth_public_key,
+                                         &gc->payment_identifier);
+      if (0 <= qs)
+        return; /* continue as planned */
+      GNUNET_break (0);
+      gc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
+                                       "update challenge payment");
+      gc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      return; /* continue as planned */
+    }
+  case TALER_MERCHANT_OSC_CLAIMED:
+  case TALER_MERCHANT_OSC_UNPAID:
+    /* repeat payment request */
+    make_payment_request (gc);
+    return;
   }
-  AH_trigger_curl ();
+  /* should never get here */
+  GNUNET_break (0);
 }
 
 
 /**
- * Helper function used to ask our backend to begin
- * processing a payment for the user's account.
- * May perform asynchronous operations by suspending the connection
- * if required.
+ * Helper function used to ask our backend to begin processing a
+ * payment for the user's account.  May perform asynchronous
+ * operations by suspending the connection if required.
  *
- * @param puc context to begin payment for.
- * @param pay_req #GNUNET_YES if payment was explicitly requested,
- *                #GNUNET_NO if payment is needed
+ * @param gc context to begin payment for.
  * @return MHD status code
  */
 static MHD_RESULT
-begin_payment (struct GetContext *gc,
-               int pay_req)
+begin_payment (struct GetContext *gc)
 {
-  json_t *order;
   enum GNUNET_DB_QueryStatus qs;
+  char *order_id;
 
   qs = db->lookup_challenge_payment (db->cls,
                                      &gc->truth_public_key,
-                                     &ongoing_payment_cb,
-                                     gc);
+                                     &gc->payment_identifier);
   if (qs < 0)
   {
-    struct MHD_Response *resp;
-    MHD_RESULT ret;
-
-    resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                 "challenge payment");
-    ret = MHD_queue_response (gc->connection,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              resp);
-    MHD_destroy_response (resp);
-    return ret;
-  }
-
-  if (0 != gc->existing_pi_timestamp.abs_value_us)
-  {
-    await_payment (gc,
-                   GNUNET_TIME_UNIT_ZERO);
-    return MHD_YES;
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (gc->connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                       "lookup challenge payment");
   }
-
-  GNUNET_CONTAINER_DLL_insert (gc_head,
-                               gc_tail,
+  gc->hc->cc = &request_done;
+  GNUNET_CONTAINER_DLL_insert (gc_tail,
+                               gc_head,
                                gc);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Suspending connection while creating order at `%s'\n",
-              AH_backend_url);
   MHD_suspend_connection (gc->connection);
+  order_id = GNUNET_STRINGS_data_to_string_alloc (
+    &gc->payment_identifier,
+    sizeof (gc->payment_identifier));
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
-    char *order_id;
+    /* We already created the order, check if it was paid */
+    gc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx,
+                                                 AH_backend_url,
+                                                 order_id,
+                                                 NULL /* NOT session-bound */,
+                                                 false,
+                                                 GNUNET_TIME_UNIT_ZERO,
+                                                 &check_payment_cb,
+                                                 gc);
+  }
+  else
+  {
+    /* Create a fresh order */
+    json_t *order;
 
-    order_id = GNUNET_STRINGS_data_to_string_alloc (
-      &gc->payment_identifier,
-      sizeof(struct ANASTASIS_PaymentSecretP));
     order = json_pack ("{s:o, s:s, s:s, s:s}",
                        "amount", TALER_JSON_from_amount (&gc->challenge_cost),
                        "summary", "annual fee for anastasis service",
                        "fulfillment_url", AH_fulfillment_url,
                        "order_id", order_id);
-    GNUNET_free (order_id);
+    gc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
+                                          AH_backend_url,
+                                          order,
+                                          GNUNET_TIME_UNIT_ZERO,
+                                          NULL, /* no payment target */
+                                          0,
+                                          NULL, /* no inventory products */
+                                          0,
+                                          NULL, /* no uuids */
+                                          false, /* do NOT require claim token 
*/
+                                          &proposal_cb,
+                                          gc);
+    json_decref (order);
   }
-  gc->po = TALER_MERCHANT_orders_post2 (AH_ctx,
-                                        AH_backend_url,
-                                        order,
-                                        GNUNET_TIME_UNIT_ZERO,
-                                        NULL, /* no payment target */
-                                        0,
-                                        NULL, /* no inventory products */
-                                        0,
-                                        NULL, /* no uuids */
-                                        false, /* do NOT require claim token */
-                                        &proposal_cb,
-                                        gc);
-
+  GNUNET_free (order_id);
   AH_trigger_curl ();
-  json_decref (order);
   return MHD_YES;
 }
 
 
 /**
- * We got some query status from the DB.  Handle the error cases.
- * May perform asynchronous operations by suspending the connection
- * if required.
+ * Load encrypted keyshare from db and return it to the client.
  *
- * @param gc connection to handle status for
- * @param qs query status to handle
- * @return #MHD_YES or #MHD_NO
+ * @param truth_public key the key to the truth for the looup
+ * @param connection the connection to respond upon
+ * @return MHD status code
  */
 static MHD_RESULT
-handle_database_error (struct GetContext *gc,
-                       enum ANASTASIS_DB_QueryStatus qs)
+return_key_share (
+  const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
+  struct MHD_Connection *connection)
 {
-  switch (qs)
+  void *encrypted_keyshare;
+  size_t encrypted_keyshare_size;
+
   {
-  case ANASTASIS_DB_STATUS_PAYMENT_REQUIRED:
+    enum GNUNET_DB_QueryStatus qs;
+
+    qs = db->get_key_share (db->cls,
+                            truth_public_key,
+                            &encrypted_keyshare,
+                            &encrypted_keyshare_size);
+    switch (qs)
     {
-      if (! gc->payment_identifier_provided)
-      {
-        GNUNET_CRYPTO_random_block (
-          GNUNET_CRYPTO_QUALITY_NONCE,
-          &gc->payment_identifier,
-          sizeof (struct ANASTASIS_PaymentSecretP));
-        return begin_payment (gc,
-                              GNUNET_NO);
-      }
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "get key share");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         
TALER_EC_ANASTASIS_TRUTH_KEY_SHARE_GONE,
+                                         NULL);
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break;
+    }
+  }
+
+  {
+    struct MHD_Response *resp;
+
+    resp = MHD_create_response_from_buffer (encrypted_keyshare_size,
+                                            encrypted_keyshare,
+                                            MHD_RESPMEM_MUST_COPY);
+    GNUNET_free (encrypted_keyshare);
+    TALER_MHD_add_global_headers (resp);
+
+    {
+      MHD_RESULT ret;
 
-      await_payment (gc,
-                     CHECK_PAYMENT_GENERIC_TIMEOUT);
+      ret = MHD_queue_response (connection,
+                                MHD_HTTP_OK,
+                                resp);
+      MHD_destroy_response (resp);
+      return ret;
     }
-    return MHD_YES;
-  case ANASTASIS_DB_STATUS_HARD_ERROR:
-  case ANASTASIS_DB_STATUS_SOFT_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (gc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       NULL);
-  case ANASTASIS_DB_STATUS_NO_RESULTS:
-    GNUNET_assert (0);
-    return MHD_NO;
-  /* intentional fall-through! */
-  case ANASTASIS_DB_STATUS_SUCCESS_ONE_RESULT:
-    GNUNET_assert (0);
-    return MHD_NO;
-  /*Should never happen*/
-  default:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (gc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       NULL);
   }
-  GNUNET_break (0);
-  return MHD_NO;
 }
 
 
@@ -598,19 +511,16 @@ AH_handler_truth_get (struct MHD_Connection *connection,
                       struct TM_HandlerContext *hc)
 {
   struct GetContext *gc = hc->ctx;
-  struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
   struct ANASTASIS_CRYPTO_TruthKeyP truth_key;
   struct GNUNET_HashCode challenge_response;
-  const char *challenge_response_s;
+  struct ANASTASIS_AuthorizationPlugin *p;
+  bool have_response;
   void *encrypted_truth;
   size_t encrypted_truth_size;
   void *decrypted_truth;
   size_t decrypted_truth_size;
   char *truth_mime;
-  char *method;
-  struct TALER_Amount zero_amount;
-  TALER_amount_get_zero (AH_currency, &zero_amount);
-  bool zero_cost = false;
+  bool is_question;
 
   if (NULL != gc)
   {
@@ -619,9 +529,6 @@ AH_handler_truth_get (struct MHD_Connection *connection,
       MHD_RESULT ret;
 
       /* We generated a response asynchronously, queue that */
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Returning asynchronously generated response with HTTP 
status %u\n",
-                  gc->response_code);
       ret = MHD_queue_response (connection,
                                 gc->response_code,
                                 gc->resp);
@@ -632,6 +539,7 @@ AH_handler_truth_get (struct MHD_Connection *connection,
     }
     if (NULL != gc->as)
     {
+      /* Authorization process is "running", check what is going on */
       enum ANASTASIS_AUTHORIZATION_Result ret;
 
       ret = gc->authorization->process (gc->as,
@@ -666,13 +574,17 @@ AH_handler_truth_get (struct MHD_Connection *connection,
         return MHD_NO;
       }
     }
+    /* How can we get here? */
+    GNUNET_break (0);
+    return MHD_NO;
   }
-  else
-  {
-    gc = GNUNET_new (struct GetContext);
-    hc->ctx = gc;
-    gc->connection = connection;
-  }
+
+  /* Fresh request, do initial setup */
+  gc = GNUNET_new (struct GetContext);
+  gc->hc = hc;
+  hc->ctx = gc;
+  gc->connection = connection;
+
   {
     const char *pay_id;
 
@@ -682,18 +594,17 @@ AH_handler_truth_get (struct MHD_Connection *connection,
     if (NULL != pay_id)
     {
       if (GNUNET_OK !=
-          GNUNET_STRINGS_string_to_data (pay_id,
-                                         strlen (pay_id),
-                                         &gc->payment_identifier,
-                                         sizeof (struct
-                                                 ANASTASIS_PaymentSecretP)))
+          GNUNET_STRINGS_string_to_data (
+            pay_id,
+            strlen (pay_id),
+            &gc->payment_identifier,
+            sizeof (struct ANASTASIS_PaymentSecretP)))
       {
         GNUNET_break_op (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_BAD_REQUEST,
-                                           // FIXME: find error code
-                                           TALER_EC_SYNC_BAD_IF_MATCH,
-                                           "Payment-Identifier does not 
include a base32-encoded Payment-Secret");
+                                           
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                           "Payment-Identifier");
       }
       gc->payment_identifier_provided = true;
     }
@@ -702,223 +613,262 @@ AH_handler_truth_get (struct MHD_Connection *connection,
     const char *pub_key_str;
 
     pub_key_str = &url[strlen ("/truth/")];
-    GNUNET_assert (NULL != pub_key_str);
-
-    GNUNET_STRINGS_string_to_data (pub_key_str,
-                                   strlen (pub_key_str),
-                                   &truth_public_key,
-                                   sizeof(struct
-                                          ANASTASIS_CRYPTO_TruthPublicKeyP));
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "public key from Url (keyshare lookup): %s\n",
-                TALER_B2S (&truth_public_key));
-
-    challenge_response_s = MHD_lookup_connection_value (connection,
-                                                        MHD_GET_ARGUMENT_KIND,
-                                                        "response");
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Challenge response from url (keyshare lookup): %s\n",
-                challenge_response_s);
+    if (GNUNET_OK !=
+        GNUNET_STRINGS_string_to_data (
+          pub_key_str,
+          strlen (pub_key_str),
+          &gc->truth_public_key,
+          sizeof(struct ANASTASIS_CRYPTO_TruthPublicKeyP)))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                         "truth public key");
+    }
   }
+
   {
-    // check if header contains Truth-Decryption-Key
+    /* check if header contains Truth-Decryption-Key */
     const char *tdk;
 
     tdk = MHD_lookup_connection_value (connection,
                                        MHD_HEADER_KIND,
                                        "Truth-Decryption-Key");
-    if (NULL == tdk)
-      return MHD_HTTP_PRECONDITION_FAILED;
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_GENERIC_PARAMETER_MISSING,
+                                         "Truth-Decryption-Key");
+    }
 
-    if ( (NULL != tdk) &&
-         (GNUNET_OK !=
-          GNUNET_STRINGS_string_to_data (tdk,
-                                         strlen (tdk),
-                                         &truth_key,
-                                         sizeof (struct
-                                                 ANASTASIS_CRYPTO_TruthKeyP))))
+    if (GNUNET_OK !=
+        GNUNET_STRINGS_string_to_data (
+          tdk,
+          strlen (tdk),
+          &truth_key,
+          sizeof (struct ANASTASIS_CRYPTO_TruthKeyP)))
     {
       GNUNET_break_op (0);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
-                                         // FIXME: find error code
-                                         TALER_EC_SYNC_BAD_IF_MATCH, // use 
description in gana: "Truth-Decryption-Key does not include a base32-encoded 
decryption key"
-                                         tdk);
+                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                         "Truth-Decryption-Key");
     }
+  }
 
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "TruthKey from header: %s\n",
-                TALER_B2S (&truth_key));
+  {
+    const char *challenge_response_s;
+
+    challenge_response_s = MHD_lookup_connection_value (connection,
+                                                        MHD_GET_ARGUMENT_KIND,
+                                                        "response");
+    if ( (NULL != challenge_response_s) &&
+         (GNUNET_OK !=
+          GNUNET_CRYPTO_hash_from_string (challenge_response_s,
+                                          &challenge_response)) )
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                         "response");
+    }
+    have_response = (NULL != challenge_response_s);
   }
+
   {
-    // load encrypted truth from db
+    /* load encrypted truth from DB */
     enum GNUNET_DB_QueryStatus qs;
+    char *method;
 
     qs = db->get_escrow_challenge (db->cls,
-                                   &truth_public_key,
+                                   &gc->truth_public_key,
                                    &encrypted_truth,
                                    &encrypted_truth_size,
                                    &truth_mime,
                                    &method);
-    if (qs != GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)
+    switch (qs)
     {
-      return MHD_HTTP_NOT_FOUND;
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (gc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "get escrow challenge");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         TALER_EC_ANASTASIS_TRUTH_UNKNOWN,
+                                         NULL);
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break;
     }
-  }
-  {
-    // decrypt encrypted_truth
-    ANASTASIS_CRYPTO_truth_decrypt (&truth_key,
-                                    encrypted_truth,
-                                    encrypted_truth_size,
-                                    &decrypted_truth,
-                                    &decrypted_truth_size);
-  }
-
-  /* Check if the cost is zero to skip the payment */
-  // FIXME: lookup costs properly!
-  if (0 == strcmp ("question",
-                   method))
-  {
-    if (0 == TALER_amount_cmp (&AH_question_cost, &zero_amount))
-      zero_cost = true;
-  }
-  if (0 == strcmp ("file",
-                   method))
-  {
-    if (0 == TALER_amount_cmp (&AH_file_cost, &zero_amount))
-      zero_cost = true;
-  }
-
-  /* Check database to see if the transaction is permissible */
-
-  if (! zero_cost)
-  {
-    enum GNUNET_DB_QueryStatus qs;
-
-    if (0 == strcmp ("question",
-                     method))
+    is_question = strcmp ("question",
+                          method);
+    if (! is_question)
     {
-      gc->challenge_cost = AH_question_cost;
+      gc->authorization
+        = ANASTASIS_authorization_plugin_load (method,
+                                               AH_cfg,
+                                               &gc->challenge_cost);
+      if (NULL == gc->authorization)
+      {
+        MHD_RESULT ret;
+
+        ret = TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_INTERNAL_SERVER_ERROR,
+          TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED,
+          method);
+        GNUNET_free (encrypted_truth);
+        GNUNET_free (method);
+        return ret;
+      }
+      GNUNET_free (method);
     }
-    if (0 == strcmp ("file",
-                     method))
+    else
     {
-      gc->challenge_cost = AH_file_cost;
+      gc->challenge_cost = AH_question_cost;
     }
+  }
+
+  {
+    struct TALER_Amount zero_amount;
 
-    if (gc->payment_identifier_provided)
+    TALER_amount_get_zero (AH_currency,
+                           &zero_amount);
+    if (0 != TALER_amount_cmp (&gc->challenge_cost,
+                               &zero_amount))
     {
-      bool paid = false;
+      /* Check database to see if the transaction is paid for */
+      enum GNUNET_DB_QueryStatus qs;
+      bool paid;
 
+      if (! gc->payment_identifier_provided)
+      {
+        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                    &gc->payment_identifier,
+                                    sizeof (struct ANASTASIS_PaymentSecretP));
+        GNUNET_free (encrypted_truth);
+        return begin_payment (gc);
+      }
       qs = db->check_challenge_payment (db->cls,
                                         &gc->payment_identifier,
                                         &paid);
-      if (qs < 0)
-        return handle_database_error (gc,
-                                      qs);
-
-      if ((qs >= 0) && (! paid))
+      switch (qs)
       {
-        return begin_payment (gc,
-                              GNUNET_YES);
+      case GNUNET_DB_STATUS_HARD_ERROR:
+      case GNUNET_DB_STATUS_SOFT_ERROR:
+        GNUNET_break (0);
+        GNUNET_free (encrypted_truth);
+        return TALER_MHD_reply_with_error (gc->connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                           "check challenge payment");
+      case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+        /* Re-use existing payment identifier */
+        GNUNET_free (encrypted_truth);
+        return begin_payment (gc);
+      case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+        if (! paid)
+        {
+          GNUNET_free (encrypted_truth);
+          return begin_payment (gc);
+        }
+        break;
       }
     }
-
-    if (! gc->payment_identifier_provided)
-    {
-      GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                  &gc->payment_identifier,
-                                  sizeof (struct ANASTASIS_PaymentSecretP));
-      return begin_payment (gc,
-                            GNUNET_YES);
-    }
   }
-  if (NULL == challenge_response_s)
+
+  /* We've been paid, now validate response */
   {
-    // FIXME: queue PROPER reply...
-    GNUNET_free (decrypted_truth);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_FORBIDDEN,
-                                       // FIXME: find error code
-                                       TALER_EC_SYNC_BAD_IF_MATCH,
-                                       NULL);
+    /* decrypt encrypted_truth */
+    ANASTASIS_CRYPTO_truth_decrypt (&truth_key,
+                                    encrypted_truth,
+                                    encrypted_truth_size,
+                                    &decrypted_truth,
+                                    &decrypted_truth_size);
+    GNUNET_free (encrypted_truth);
   }
-  GNUNET_CRYPTO_hash_from_string (challenge_response_s,
-                                  &challenge_response);
-  if (0 == strcmp ("question",
-                   method))
+
+  /* Special case for secure question: we do not generate a numeric challenge,
+     but check that the hash matches */
+  if (is_question)
   {
-    if (0 != GNUNET_memcmp (&challenge_response,
-                            decrypted_truth))
+    if (! have_response)
     {
-      GNUNET_break (0);
+      GNUNET_free (decrypted_truth);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_FORBIDDEN,
-                                         // FIXME: find error code
-                                         TALER_EC_SYNC_BAD_IF_MATCH,
-                                         challenge_response_s);
+                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED,
+                                         NULL);
     }
-    else
+    if ( (decrypted_truth_size != sizeof (challenge_response)) ||
+         (0 != memcmp (&challenge_response,
+                       decrypted_truth,
+                       decrypted_truth_size)) )
     {
-      return return_key_share (&truth_public_key,
-                               connection);
+      GNUNET_break_op (0);
+      GNUNET_free (decrypted_truth);
+      // FIXME: mark in DB that we did it (now, for 
code_retransmission_frequency!)
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_FORBIDDEN,
+                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
+                                         NULL);
     }
+    return return_key_share (&gc->truth_public_key,
+                             connection);
   }
 
-  GNUNET_free (decrypted_truth);
   /* Not security question, check for answer in DB */
-  if (NULL != challenge_response_s)
+  if (have_response)
   {
     enum ANASTASIS_DB_CodeStatus cs;
 
+    GNUNET_free (decrypted_truth);
     cs = db->verify_challenge_code (db->cls,
-                                    &truth_public_key,
+                                    &gc->truth_public_key,
                                     &challenge_response);
     switch (cs)
     {
-    case ANASTASIS_DB_STATUS_CHALLENGE_CODE_MISMATCH:
-
-    case ANASTASIS_DB_STATUS_HARD_ERROR:
-      // FIXME: proper reply!
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_SOFT_ERROR:
-      // FIXME: proper reply!
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_NO_RESULTS:
-      // FIXME: proper reply!
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_VALID_CODE_STORED:
-      return return_key_share (&truth_public_key,
+    case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH:
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_FORBIDDEN,
+                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_FAILED,
+                                         NULL);
+    case ANASTASIS_DB_CODE_STATUS_HARD_ERROR:
+    case ANASTASIS_DB_CODE_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (gc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "verify_challenge_code");
+    case ANASTASIS_DB_CODE_STATUS_NO_RESULTS:
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_GONE,
+                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_UNKNOWN,
+                                         NULL);
+    case ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED:
+      return return_key_share (&gc->truth_public_key,
                                connection);
     }
     GNUNET_break (0);
     return MHD_NO;
   }
 
-  /* Not security question and no answer: use plugin to generate challenge! */
+  /* Not security question and no answer: use plugin to check if
+     decrypted truth is a valid challenge! */
   {
-    struct ANASTASIS_AuthorizationPlugin *authorization;
     enum GNUNET_GenericReturnValue ret;
-    struct ANASTASIS_AUTHORIZATION_State *as;
-    enum ANASTASIS_AUTHORIZATION_Result aret;
-    enum ANASTASIS_DB_QueryStatus qs;
-    struct GNUNET_TIME_Relative challenge_expiration;
-    challenge_expiration = GNUNET_TIME_UNIT_HOURS;
-    authorization = ANASTASIS_authorization_plugin_load (method);
-    if (NULL == authorization)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Plugin not found: %s",method);
-      GNUNET_free (decrypted_truth);
-      return MHD_NO;
-    }
-
-    ret = authorization->validate (authorization->cls,
-                                   connection,
-                                   decrypted_truth,
-                                   decrypted_truth_size);
 
+    ret = gc->authorization->validate (gc->authorization->cls,
+                                       connection,
+                                       decrypted_truth,
+                                       decrypted_truth_size);
     switch (ret)
     {
     case GNUNET_OK:
@@ -933,86 +883,96 @@ AH_handler_truth_get (struct MHD_Connection *connection,
       GNUNET_free (decrypted_truth);
       return MHD_NO;
     }
+  }
 
-    uint64_t code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
-                                              999999);
+  /* Setup challenge and begin authorization process */
+  {
+    uint64_t code;
+    enum GNUNET_DB_QueryStatus qs;
 
+    code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+                                     999999);
+    // FIXME: this is more complex:
+    // - p->code_rotation_period
+    // - p->code_validity_period
+    // - p->code_retransmission_frequency
+    // all MUST be considered here!
+    // -> DB API change needed!
     qs = db->store_challenge_code (db->cls,
-                                   &truth_public_key,
+                                   &gc->truth_public_key,
                                    code,
-                                   challenge_expiration,
+                                   p->code_validity_period,
                                    3);
-
     switch (qs)
     {
-    case ANASTASIS_DB_STATUS_HARD_ERROR:
-      /* data invalid, reply was NOT queued */
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
       GNUNET_free (decrypted_truth);
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_SOFT_ERROR:
-      /* data invalid, reply was NOT queued */
+      return TALER_MHD_reply_with_error (gc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "store_challenge_code");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
       GNUNET_free (decrypted_truth);
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_VALID_CODE_STORED:
-      /*FIXME already code stored message */
-      return MHD_NO;
-    case ANASTASIS_DB_STATUS_SUCCESS_ONE_RESULT:
-      /*challengecode was stored successfully*/
+      return TALER_MHD_reply_with_error (gc->connection,
+                                         MHD_HTTP_ALREADY_REPORTED,
+                                         
TALER_EC_ANASTASIS_TRUTH_CHALLENGE_ACTIVE,
+                                         NULL);
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      /* challenge code was stored successfully*/
       break;
-    default:
-      GNUNET_free (decrypted_truth);
-      return MHD_NO;
     }
 
-    as = authorization->start (authorization->cls,
-                               &truth_public_key,
-                               code,
-                               authorization->auth_command,
-                               decrypted_truth,
-                               decrypted_truth_size);
-
-    if (NULL == as)
+    gc->as = gc->authorization->start (gc->authorization->cls,
+                                       &gc->truth_public_key,
+                                       code,
+                                       decrypted_truth,
+                                       decrypted_truth_size);
+    GNUNET_free (decrypted_truth);
+    if (NULL == gc->as)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  ("AUTHORIZATION START FAILED"));
-      // FIXME: queue PROPER reply...
-      return MHD_NO;
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (gc->connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED,
+                                         NULL);
     }
-    gc->connection = connection;
-    gc->authorization = authorization;
-    gc->as = as;
-    hc->ctx = gc;
     hc->cc = &request_done;
     GNUNET_CONTAINER_DLL_insert (gc_head,
                                  gc_tail,
                                  gc);
-    aret = authorization->process (as,
-                                   connection);
-    switch (aret)
     {
-    case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
-      /* all good, challenge sent! */
-      // FIXME: mark in DB that we did it (now, for 
code_retransmission_frequency!)
-      break;
-    case ANASTASIS_AUTHORIZATION_RES_FAILED:
-      /* sending challenge failed */
-      // FIXME: give at least a refund!?
-      break;
-    case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
-      /* we have been suspended, see you later */
+      enum ANASTASIS_AUTHORIZATION_Result aret;
+
+      aret = gc->authorization->process (gc->as,
+                                         connection);
+      switch (aret)
+      {
+      case ANASTASIS_AUTHORIZATION_RES_SUCCESS:
+        /* all good, challenge sent! */
+        // FIXME: mark in DB that we did it (now, for 
code_retransmission_frequency!)
+        break;
+      case ANASTASIS_AUTHORIZATION_RES_FAILED:
+        /* sending challenge failed */
+        // FIXME: give at least a refund!?
+        break;
+      case ANASTASIS_AUTHORIZATION_RES_SUSPENDED:
+        /* we have been suspended, see you later */
+        return MHD_YES;
+      case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
+        /* Challenge sent successfully, but HTTP reply failed */
+        // FIXME: mark in DB that we did it (now, for 
code_retransmission_frequency!)
+        gc->authorization->cleanup (gc->as);
+        return MHD_NO;
+      case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
+        /* failure to send challenge AND to queue reply, kill connection */
+        gc->authorization->cleanup (gc->as);
+        return MHD_NO;
+      }
+      gc->authorization->cleanup (gc->as);
+      gc->as = NULL;
       return MHD_YES;
-    case ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED:
-      /* Challenge sent successfully */
-      // FIXME: mark in DB that we did it (now, for 
code_retransmission_frequency!)
-      authorization->cleanup (gc->as);
-      return MHD_NO;
-    case ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED:
-      /* failure to queue reply, kill connection */
-      authorization->cleanup (gc->as);
-      return MHD_NO;
     }
-    authorization->cleanup (gc->as);
-    gc->as = NULL;
-    return MHD_YES;
   }
 }
diff --git a/src/backend/anastasis-httpd_truth_upload.c 
b/src/backend/anastasis-httpd_truth_upload.c
index 7228d99..d231aac 100644
--- a/src/backend/anastasis-httpd_truth_upload.c
+++ b/src/backend/anastasis-httpd_truth_upload.c
@@ -58,7 +58,7 @@ AH_handler_truth_post (struct MHD_Connection *connection,
   struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
   int res;
   struct ANASTASIS_DB_Truth truth;
-  enum ANASTASIS_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* extract public key from url */
   {
@@ -113,7 +113,7 @@ AH_handler_truth_post (struct MHD_Connection *connection,
                 qs);
     json_decref (json);
     GNUNET_JSON_parse_free (spec);
-    if (ANASTASIS_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     {
       struct MHD_Response *resp;
 
diff --git a/src/backend/anastasis_authorization_plugin.c 
b/src/backend/anastasis_authorization_plugin.c
index b79fb84..b1824ee 100644
--- a/src/backend/anastasis_authorization_plugin.c
+++ b/src/backend/anastasis_authorization_plugin.c
@@ -21,6 +21,7 @@
  */
 #include "platform.h"
 #include "anastasis_authorization_plugin.h"
+#include "anastasis-httpd.h"
 #include <ltdl.h>
 
 
@@ -73,14 +74,6 @@ struct AuthPlugin
 };
 
 
-/**
- * Load authorization plugin.
- *
- * @param method name of the method to load
- * @param AH_cfg configuration to use
- * @param[out] set to the cost for using the plugin during recovery
- * @return #GNUNET_OK on success
- */
 struct ANASTASIS_AuthorizationPlugin *
 ANASTASIS_authorization_plugin_load (
   const char *method,
@@ -117,14 +110,15 @@ ANASTASIS_authorization_plugin_load (
     GNUNET_free (ap);
     return NULL;
   }
+  // FIXME: AH_annual_fee is not a symbol of this library!!!
   if (GNUNET_OK !=
       TALER_amount_cmp_currency (&ap->cost,
                                  &AH_annual_fee))
   {
-    GNUNET_log_config_malformed (GNUNET_ERROR_TYPE_ERROR,
-                                 sec_name,
-                                 "COST",
-                                 "currency mismatch");
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               sec_name,
+                               "COST",
+                               "currency mismatch");
     GNUNET_free (sec_name);
     GNUNET_free (ap);
     return NULL;
@@ -155,26 +149,6 @@ ANASTASIS_authorization_plugin_load (
 }
 
 
-/**
- * Shutdown the plugin.
- *
- * @param plugin the plugin to unload
- */
-void
-ANASTASIS_authorization_plugin_unload (
-  struct ANASTASIS_AuthorizationPlugin *plugin)
-{
-  char *lib_name;
-
-  if (NULL == plugin)
-    return;
-  lib_name = plugin->library_name;
-  GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
-                                               plugin));
-  GNUNET_free (lib_name);
-}
-
-
 void
 ANASTASIS_authorization_plugin_shutdown (void)
 {
diff --git a/src/backend/anastasis_authorization_plugin_file.c 
b/src/backend/anastasis_authorization_plugin_file.c
index e23a21b..691aaef 100644
--- a/src/backend/anastasis_authorization_plugin_file.c
+++ b/src/backend/anastasis_authorization_plugin_file.c
@@ -113,7 +113,6 @@ static struct ANASTASIS_AUTHORIZATION_State *
 file_start (void *cls,
             const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
             uint64_t code,
-            char *auth_command,
             const void *data,
             size_t data_length)
 {
@@ -183,8 +182,6 @@ file_process (struct ANASTASIS_AUTHORIZATION_State *as,
                                  strlen (as->filename),
                                  response,
                                  response_size);
-  char *test = GNUNET_STRINGS_data_to_string_alloc (response,
-                                                    response_size);
   resp = MHD_create_response_from_buffer (response_size,
                                           response,
                                           MHD_RESPMEM_MUST_FREE);
diff --git a/src/backend/anastasis_authorization_plugin_sms.c 
b/src/backend/anastasis_authorization_plugin_sms.c
index f3d1ea8..579992c 100644
--- a/src/backend/anastasis_authorization_plugin_sms.c
+++ b/src/backend/anastasis_authorization_plugin_sms.c
@@ -1,6 +1,6 @@
 /*
   This file is part of Anastasis
-  Copyright (C) 2019 Taler Systems SA
+  Copyright (C) 2019, 2021 Taler Systems SA
 
   Anastasis is free software; you can redistribute it and/or modify it under 
the
   terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -29,29 +29,83 @@
 /**
  * Saves the State of a authorization process
  */
+struct SMS_Context
+{
+
+  /**
+   * Command which is executed to run the plugin (some bash script or a
+   * command line argument)
+   */
+  char *auth_command;
+
+  /**
+   * Regex for phone number validation.
+   */
+  regex_t regex;
+
+};
+
 
+/**
+ * Saves the State of a authorization process
+ */
 struct ANASTASIS_AUTHORIZATION_State
 {
   /**
    * Public key of the challenge which is authorised
    */
-  const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key;
+  struct ANASTASIS_CRYPTO_TruthPublicKeyP truth_public_key;
+
   /**
    * Code which is sent to the user (here sent via SMS)
    */
   uint64_t code;
+
+  /**
+   * Our plugin context.
+   */
+  struct SMS_Context *ctx;
+
   /**
    * holds the truth information
    */
   char *phone_number;
+
   /**
-   * closure
+   * Handle to the helper process.
    */
-  void *cls;
+  struct GNUNET_OS_Process *child;
+
   /**
-   * Command which is executed to run the sms authentication
+   * Handle to wait for @e child
    */
-  char *auth_command;
+  struct ANASTASIS_ChildWaitHandle *cwh;
+
+  /**
+   * Our client connection, set if suspended.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Message to send.
+   */
+  char *msg;
+
+  /**
+   * Offset of transmission in msg.
+   */
+  size_t msg_off;
+
+  /**
+   * Exit code from helper.
+   */
+  long unsigned int exit_code;
+
+  /**
+   * How did the helper die?
+   */
+  enum GNUNET_OS_ProcessStatusType pst;
+
 };
 
 
@@ -63,7 +117,7 @@ struct ANASTASIS_AUTHORIZATION_State
  *
  * To be possibly used before issuing a 402 payment required to the client.
  *
- * @param cls closure
+ * @param cls closure with a `struct SMS_Context`
  * @param connection HTTP client request (for queuing response)
  * @param data input to validate (i.e. is it a valid phone number, etc.)
  * @param data_length number of bytes in @a data
@@ -77,46 +131,37 @@ sms_validate (void *cls,
               const char *data,
               size_t data_length)
 {
-  regex_t regex;
+  struct SMS_Context *ctx = cls;
   int regex_result;
-  /*FIXME very basic check */
-  const char *regexp = "^[0-9]+$";
-  char *phone_number = GNUNET_STRINGS_data_to_string_alloc (data,
-                                                            data_length);
-
-  regex_result = regcomp (&regex,
-                          regexp,
-                          REG_EXTENDED);
-  if (0 < regex_result)
-  {
-    GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to compile regular expression.");
-    regfree (&regex);
-    return GNUNET_NO;
-  }
+  char *phone_number;
 
-  regex_result = regexec (&regex,
+  phone_number = GNUNET_strndup (data,
+                                 data_length);
+  regex_result = regexec (&ctx->regex,
                           phone_number,
                           0,
                           NULL,
                           0);
+  GNUNET_free (phone_number);
   if (0 != regex_result)
   {
-    regfree (&regex);
+    if (MHD_NO ==
+        TALER_MHD_reply_with_error (connection,
+                                    MHD_HTTP_EXPECTATION_FAILED,
+                                    TALER_EC_ANASTASIS_SMS_PHONE_INVALID,
+                                    NULL))
+      return GNUNET_SYSERR;
     return GNUNET_NO;
   }
-  regfree (&regex);
-  GNUNET_free (phone_number);
   return GNUNET_OK;
 }
 
 
 /**
  * Begin issuing authentication challenge to user based on @a data.
- * I.e. start to send SMS or e-mail or launch video identification.
+ * Sends SMS.
  *
- * @param cls closure
+ * @param cls closure with a `struct SMS_Context`
  * @param truth_public_key Identifier of the challenge, to be (if possible) 
included in the
  *             interaction with the user
  * @param code secret code that the user has to provide back to satisfy the 
challenge in
@@ -128,23 +173,45 @@ static struct ANASTASIS_AUTHORIZATION_State *
 sms_start (void *cls,
            const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
            uint64_t code,
-           char *auth_command,
            const void *data,
            size_t data_length)
 {
+  struct SMS_Context *ctx = cls;
   struct ANASTASIS_AUTHORIZATION_State *as;
 
   as = GNUNET_new (struct ANASTASIS_AUTHORIZATION_State);
-  as->cls = cls;
-  as->truth_public_key = truth_public_key;
+  as->ctx = ctx;
+  as->truth_public_key = *truth_public_key;
   as->code = code;
-  as->auth_command = auth_command;
-  as->phone_number = GNUNET_STRINGS_data_to_string_alloc (data,
-                                                          data_length);
+  as->phone_number = GNUNET_strndup (data,
+                                     data_length);
   return as;
 }
 
 
+/**
+ * Function called when our SMS helper has terminated.
+ *
+ * @param cls our `struct ANASTASIS_AUHTORIZATION_State`
+ * @param type type of the process
+ * @param exit_code status code of the process
+ */
+static void
+sms_done_cb (void *cls,
+             enum GNUNET_OS_ProcessStatusType type,
+             long unsigned int exit_code)
+{
+  struct ANASTASIS_AUTHORIZATION_State *as = cls;
+
+  as->child = NULL;
+  as->cwh = NULL;
+  as->pst = type;
+  as->exit_code = exit_code;
+  MHD_resume_connection (as->connection);
+  AH_trigger_daemon (); // FIXME: unclean, linker issue => change API to get a 
callback?
+}
+
+
 /**
  * Begin issuing authentication challenge to user based on @a data.
  * I.e. start to send SMS or e-mail or launch video identification.
@@ -158,40 +225,153 @@ sms_process (struct ANASTASIS_AUTHORIZATION_State *as,
              struct MHD_Connection *connection)
 {
   MHD_RESULT mres;
-  struct MHD_Response *resp;
-  int p[2];
-  /*FIXME ERROR HANDLING*/
-  int ret = pipe (p);
-  pid_t pid = fork ();
-  switch (pid)
+
+  if (NULL == as->msg)
   {
-  case -1:
-    close (p[0]);
-    close (p[1]);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Error while trying to send email");
-    resp = TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                 "Failed to fork process");
-    mres = MHD_queue_response (connection,
-                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                               resp);
-    MHD_destroy_response (resp);
+    /* First time, start child process and feed pipe */
+    struct GNUNET_DISK_PipeHandle *p;
+    struct GNUNET_DISK_FileHandle *pipe_stdin;
+
+    p = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
+    if (NULL == p)
+    {
+      mres = TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_ANASTASIS_SMS_HELPER_EXEC_FAILED,
+                                         "pipe");
+      if (MHD_YES != mres)
+        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+      return ANASTASIS_AUTHORIZATION_RES_FAILED;
+    }
+    as->child = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
+                                         p,
+                                         NULL,
+                                         NULL,
+                                         as->ctx->auth_command,
+                                         as->ctx->auth_command,
+                                         as->phone_number,
+                                         NULL);
+    if (NULL == as->child)
+    {
+      GNUNET_DISK_pipe_close (p);
+      mres = TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         
TALER_EC_ANASTASIS_SMS_HELPER_EXEC_FAILED,
+                                         "exec");
+      if (MHD_YES != mres)
+        return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+      return ANASTASIS_AUTHORIZATION_RES_FAILED;
+    }
+    pipe_stdin = GNUNET_DISK_pipe_detach_end (p,
+                                              GNUNET_DISK_PIPE_END_WRITE);
+    GNUNET_assert (NULL != pipe_stdin);
+    GNUNET_DISK_pipe_close (p);
+    {
+      char *tpk;
+
+      tpk = GNUNET_STRINGS_data_to_string_alloc (
+        &as->truth_public_key,
+        sizeof (as->truth_public_key));
+      GNUNET_asprintf (&as->msg,
+                       "#%llu\nAnastasis\n%s",
+                       (unsigned long long) as->code,
+                       tpk);
+      GNUNET_free (tpk);
+    }
+
+    {
+      const char *off = as->msg;
+      size_t left = strlen (off);
+
+      while (0 != left)
+      {
+        ssize_t ret;
+
+        if (0 == left)
+          break;
+        ret = GNUNET_DISK_file_write (pipe_stdin,
+                                      off,
+                                      left);
+        if (ret <= 0)
+        {
+          mres = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             
TALER_EC_ANASTASIS_SMS_HELPER_EXEC_FAILED,
+                                             "write");
+          if (MHD_YES != mres)
+            return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
+          return ANASTASIS_AUTHORIZATION_RES_FAILED;
+        }
+        as->msg_off += ret;
+        off += ret;
+        left -= ret;
+      }
+      GNUNET_DISK_file_close (pipe_stdin);
+    }
+    as->cwh = ANASTASIS_wait_child (as->child,
+                                    &sms_done_cb,
+                                    as);
+    as->connection = connection;
+    MHD_suspend_connection (connection);
+    return ANASTASIS_AUTHORIZATION_RES_SUSPENDED;
+  }
+  if (NULL != as->cwh)
+  {
+    /* Spurious call, why are we here? */
+    GNUNET_break (0);
+    MHD_suspend_connection (connection);
+    return ANASTASIS_AUTHORIZATION_RES_SUSPENDED;
+  }
+  if ( (GNUNET_OS_PROCESS_EXITED != as->pst) ||
+       (0 != as->exit_code) )
+  {
+    char es[32];
+
+    GNUNET_snprintf (es,
+                     sizeof (es),
+                     "%u/%d",
+                     (unsigned int) as->exit_code,
+                     as->pst);
+    mres = TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_ANASTASIS_SMS_HELPER_COMMAND_FAILED,
+                                       es);
     if (MHD_YES != mres)
       return ANASTASIS_AUTHORIZATION_RES_FAILED_REPLY_FAILED;
     return ANASTASIS_AUTHORIZATION_RES_FAILED;
-  case 0:
-    dup2 (p[0],0);
-    close (p[1]);
-    char buff[21];
-    sprintf (buff, "%lu", as->code);
-    execlp (as->auth_command, buff, as->phone_number, NULL);
-    close (p[0]);
-    close (p[1]);
-    break;
-  default:
-    /*FIXME */
-    break;
   }
+
+  /* Build HTTP response */
+  {
+    struct MHD_Response *resp;
+    size_t reply_len;
+    char *reply;
+    const char *end;
+    size_t slen;
+
+    /* FIXME: i18n based on language preferences of the client */
+    /* FIXME: Reply in JSON if requested by client */
+    slen = strlen (as->phone_number);
+    if (slen > 4)
+      end = &as->phone_number[slen - 4];
+    else
+      end = &as->phone_number[slen / 2];
+    reply_len = GNUNET_asprintf (&reply,
+                                 "Recovery TAN send to phone number ending 
with %s",
+                                 end);
+    resp = MHD_create_response_from_buffer (reply_len,
+                                            reply,
+                                            MHD_RESPMEM_MUST_COPY);
+    GNUNET_free (reply);
+    TALER_MHD_add_global_headers (resp);
+    mres = MHD_queue_response (connection,
+                               MHD_HTTP_OK,
+                               resp);
+    MHD_destroy_response (resp);
+  }
+
+  if (MHD_YES != mres)
+    return ANASTASIS_AUTHORIZATION_RES_SUCCESS_REPLY_FAILED;
   return ANASTASIS_AUTHORIZATION_RES_SUCCESS;
 }
 
@@ -204,6 +384,20 @@ sms_process (struct ANASTASIS_AUTHORIZATION_State *as,
 static void
 sms_cleanup (struct ANASTASIS_AUTHORIZATION_State *as)
 {
+  if (NULL != as->cwh)
+  {
+    ANASTASIS_wait_child_cancel (as->cwh);
+    as->cwh = NULL;
+  }
+  if (NULL != as->child)
+  {
+    (void) GNUNET_OS_process_kill (as->child,
+                                   SIGKILL);
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_OS_process_wait (as->child));
+    as->child = NULL;
+  }
+  GNUNET_free (as->msg);
   GNUNET_free (as->phone_number);
   GNUNET_free (as);
 }
@@ -220,7 +414,25 @@ libanastasis_plugin_authorization_sms_init (void *cls)
 {
   struct ANASTASIS_AuthorizationPlugin *plugin;
   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct SMS_Context *ctx;
+
+  ctx = GNUNET_new (struct SMS_Context);
+  {
+    int regex_result;
+    const char *regexp = "^+?[0-9]+$";
+
+    regex_result = regcomp (&ctx->regex,
+                            regexp,
+                            REG_EXTENDED);
+    if (0 < regex_result)
+    {
+      GNUNET_break (0);
+      GNUNET_free (ctx);
+      return NULL;
+    }
+  }
   plugin = GNUNET_new (struct ANASTASIS_AuthorizationPlugin);
+  plugin->cls = ctx;
   plugin->validate = &sms_validate;
   plugin->start = &sms_start;
   plugin->process = &sms_process;
@@ -230,11 +442,12 @@ libanastasis_plugin_authorization_sms_init (void *cls)
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              "authorization-sms",
                                              "SMSAUTH_COMMAND",
-                                             &plugin->auth_command))
+                                             &ctx->auth_command))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "authorization-sms",
                                "SMSAUTH_COMMAND");
+    GNUNET_free (ctx);
     GNUNET_free (plugin);
     return NULL;
   }
@@ -252,6 +465,11 @@ void *
 libanastasis_plugin_authorization_email_done (void *cls)
 {
   struct ANASTASIS_AuthorizationPlugin *plugin = cls;
+  struct SMS_Context *ctx = plugin->cls;
+
+  GNUNET_free (ctx->auth_command);
+  regfree (&ctx->regex);
+  GNUNET_free (ctx);
   GNUNET_free (plugin);
   return NULL;
 }
diff --git a/src/include/anastasis_authorization_lib.h 
b/src/include/anastasis_authorization_lib.h
index efb6b92..80740be 100644
--- a/src/include/anastasis_authorization_lib.h
+++ b/src/include/anastasis_authorization_lib.h
@@ -26,23 +26,20 @@
 #include "anastasis_authorization_plugin.h"
 
 /**
- * Initialize the plugin.
+ * Load authorization plugin.
  *
- * @param cfg configuration to use
- * @return NULL on failure
+ * @param method name of the method to load
+ * @param AH_cfg configuration to use
+ * @param[out] set to the cost for using the plugin during recovery
+ * @return #GNUNET_OK on success
  */
 struct ANASTASIS_AuthorizationPlugin *
-ANASTASIS_authorization_plugin_load (const char *method);
+ANASTASIS_authorization_plugin_load (
+  const char *method,
+  const struct GNUNET_CONFIGURATION_Handle *AH_cfg,
+  struct TALER_Amount *cost);
 
 
-/**
- * unload a plugin.
- *
- * @param plugin plugin to unload
- */
-void
-ANASTASIS_authorization_plugin_unload (struct ANASTASIS_DatabasePlugin 
*plugin);
-
 /**
  * shutdown all loaded plugins.
  *
diff --git a/src/include/anastasis_authorization_plugin.h 
b/src/include/anastasis_authorization_plugin.h
index c0dffbd..7a69dc1 100644
--- a/src/include/anastasis_authorization_plugin.h
+++ b/src/include/anastasis_authorization_plugin.h
@@ -110,12 +110,6 @@ struct ANASTASIS_AuthorizationPlugin
    */
   struct GNUNET_TIME_Relative code_retransmission_frequency;
 
-  /**
-   * Command which is executed to run the plugin (some bash script or a
-   * command line argument)
-   */
-  char *auth_command;
-
   /**
    * Validate @a data is a well-formed input into the challenge method,
    * i.e. @a data is a well-formed phone number for sending an SMS, or
@@ -158,7 +152,6 @@ struct ANASTASIS_AuthorizationPlugin
   (*start)(void *cls,
            const struct ANASTASIS_CRYPTO_TruthPublicKeyP *truth_public_key,
            uint64_t code,
-           char *auth_command,
            const void *data,
            size_t data_length);
 
diff --git a/src/include/anastasis_util_lib.h b/src/include/anastasis_util_lib.h
index 35ad8e3..3cfb7a7 100644
--- a/src/include/anastasis_util_lib.h
+++ b/src/include/anastasis_util_lib.h
@@ -43,7 +43,7 @@ struct ANASTASIS_ChildWaitHandle;
 
 /**
  * Defines a ANASTASIS_ChildCompletedCallback which is sent back
- * upon death or complettion of a child process. Used to trigger
+ * upon death or completion of a child process. Used to trigger
  * authentication commands.
  *
  * @param cls handle for the callback
@@ -55,6 +55,8 @@ typedef void
 (*ANASTASIS_ChildCompletedCallback)(void *cls,
                                     enum GNUNET_OS_ProcessStatusType type,
                                     long unsigned int exit_code);
+
+
 /**
  * Starts the handling of the child processes.
  * Function checks the status of the child process and sends back a
@@ -70,6 +72,9 @@ ANASTASIS_wait_child (struct GNUNET_OS_Process *proc,
                       ANASTASIS_ChildCompletedCallback cb,
                       void *cb_cls);
 
+/**
+ * Stop waiting on this child.
+ */
 void
 ANASTASIS_wait_child_cancel (struct ANASTASIS_ChildWaitHandle *cwh);
 

-- 
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]