/*
   This file is part of GNUnet.
   Copyright (C) 2012-2015 GNUnet e.V.

   GNUnet is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 3, or (at your
   option) any later version.

   GNUnet is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNUnet; see the file COPYING.  If not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
   */
/**
 * @author Martin Schanzenbach
 * @file src/identity-provider/gnunet-service-identity-provider.c
 * @brief Identity Token Service
 *
 */
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_constants.h"
#include "gnunet_protocols.h"
#include "gnunet_identity_service.h"
#include "gnunet_gnsrecord_lib.h"
#include "gnunet_namestore_service.h"
#include "gnunet_abe_lib.h"
#include "gnunet_credential_service.h"
#include "gnunet_statistics_service.h"
#include "gnunet_gns_service.h"
#include "gnunet_identity_provider_plugin.h"
#include "gnunet_identity_attribute_lib.h"
#include "gnunet_signatures.h"
#include "identity_provider.h"

/**
 * First pass state
 */
#define STATE_INIT 0

/**
 * Normal operation state
 */
#define STATE_POST_INIT 1

/**
 * Minimum interval between updates
 */
#define MIN_WAIT_TIME GNUNET_TIME_UNIT_MINUTES

/**
 * Standard token expiration time
 */
#define DEFAULT_TOKEN_EXPIRATION_INTERVAL GNUNET_TIME_UNIT_HOURS

/**
 * Identity handle
 */
static struct GNUNET_IDENTITY_Handle *identity_handle;

/**
 * Database handle
 */
static struct GNUNET_IDENTITY_PROVIDER_PluginFunctions *TKT_database;

/**
 * Name of DB plugin
 */
static char *db_lib_name;

/**
 * Token expiration interval
 */
static struct GNUNET_TIME_Relative token_expiration_interval;

/**
 * Namestore handle
 */
static struct GNUNET_NAMESTORE_Handle *ns_handle;

/**
 * GNS handle
 */
static struct GNUNET_GNS_Handle *gns_handle;

/**
 * Credential handle
 */
static struct GNUNET_CREDENTIAL_Handle *credential_handle;

/**
 * Namestore qe
 */
static struct GNUNET_NAMESTORE_QueueEntry *ns_qe;

/**
 * Namestore iterator
 */
static struct GNUNET_NAMESTORE_ZoneIterator *ns_it;

/**
 * Timeout task
 */
static struct GNUNET_SCHEDULER_Task *timeout_task;

/**
 * Update task
 */
static struct GNUNET_SCHEDULER_Task *update_task;


/**
 * Currently processed token
 */
static struct IdentityToken *token;

/**
 * Label for currently processed token
 */
static char* label;

/**
 * Scopes for processed token
 */
static char* scopes;

/**
 * Handle to the statistics service.
 */
static struct GNUNET_STATISTICS_Handle *stats;

/**
 * Our configuration.
 */
static const struct GNUNET_CONFIGURATION_Handle *cfg;

/**
 * An idp client
 */
struct IdpClient;

/**
 * A ticket iteration operation.
 */
struct TicketIteration
{
  /**
   * DLL
   */
  struct TicketIteration *next;

  /**
   * DLL
   */
  struct TicketIteration *prev;

  /**
   * Client which intiated this zone iteration
   */
  struct IdpClient *client;

  /**
   * Key of the identity we are iterating over.
   */
  struct GNUNET_CRYPTO_EcdsaPublicKey identity;

  /**
   * Identity is audience
   */
  uint32_t is_audience;

  /**
   * The operation id fot the iteration in the response for the client
   */
  uint32_t r_id;

  /**
   * Offset of the iteration used to address next result of the 
   * iteration in the store
   *
   * Initialy set to 0 in handle_iteration_start
   * Incremented with by every call to handle_iteration_next
   */
  uint32_t offset;

};



/**
 * Callback after an ABE bootstrap
 *
 * @param cls closure
 * @param abe_key the ABE key that exists or was created
 */
typedef void
(*AbeBootstrapResult) (void *cls,
                       struct GNUNET_ABE_AbeMasterKey *abe_key);


struct AbeBootstrapHandle
{
  /**
   * Function to call when finished
   */
  AbeBootstrapResult proc;

  /**
   * Callback closure
   */
  char *proc_cls;

  /**
   * Key of the zone we are iterating over.
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * Namestore Queue Entry
   */
  struct GNUNET_NAMESTORE_QueueEntry *ns_qe;

  /**
   * The issuer egos ABE master key
   */
  struct GNUNET_ABE_AbeMasterKey *abe_key;
};

/**
 * An attribute iteration operation.
 */
struct AttributeIterator
{
  /**
   * Next element in the DLL
   */
  struct AttributeIterator *next;

  /**
   * Previous element in the DLL
   */
  struct AttributeIterator *prev;

  /**
   * IDP client which intiated this zone iteration
   */
  struct IdpClient *client;

  /**
   * Key of the zone we are iterating over.
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * The issuer egos ABE master key
   */
  struct GNUNET_ABE_AbeMasterKey *abe_key;

  /**
   * Namestore iterator
   */
  struct GNUNET_NAMESTORE_ZoneIterator *ns_it;

  /**
   * The operation id fot the zone iteration in the response for the client
   */
  uint32_t request_id;

};



/**
 * An idp client
 */
struct IdpClient
{

  /**
   * The client
   */
  struct GNUNET_SERVICE_Client *client;

  /**
   * Message queue for transmission to @e client
   */
  struct GNUNET_MQ_Handle *mq;
  
  /**
   * Head of the DLL of
   * Attribute iteration operations in 
   * progress initiated by this client
   */
  struct AttributeIterator *op_head;

  /**
   * Tail of the DLL of
   * Attribute iteration operations 
   * in progress initiated by this client
   */
  struct AttributeIterator *op_tail;

  /**
   * Head of DLL of ticket iteration ops
   */
  struct TicketIteration *ticket_iter_head;

  /**
   * Tail of DLL of ticket iteration ops
   */
  struct TicketIteration *ticket_iter_tail;


  /**
   * Head of DLL of ticket revocation ops
   */
  struct TicketRevocationHandle *revocation_list_head;

  /**
   * Tail of DLL of ticket revocation ops
   */
  struct TicketRevocationHandle *revocation_list_tail;
};



struct AttributeStoreHandle
{

  /**
   * Client connection
   */
  struct IdpClient *client;

  /**
   * Identity
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * Identity pubkey
   */
  struct GNUNET_CRYPTO_EcdsaPublicKey identity_pkey;

  /**
   * The issuer egos ABE master key
   */
  struct GNUNET_ABE_AbeMasterKey *abe_key;

  /**
   * QueueEntry
   */
  struct GNUNET_NAMESTORE_QueueEntry *ns_qe;

  /**
   * The attribute to store
   */
  struct GNUNET_IDENTITY_ATTRIBUTE_Claim *claim;

  /**
   * request id
   */
  uint32_t r_id;
};


/* Prototype */
struct ParallelLookup;

struct ConsumeTicketHandle
{

  /**
   * Client connection
   */
  struct IdpClient *client;

  /**
   * Ticket
   */
  struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;

  /**
   * LookupRequest
   */
  struct GNUNET_GNS_LookupRequest *lookup_request;

  /**
   * Audience Key
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * Audience Key
   */
  struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;

  /**
   * Lookup DLL
   */
  struct ParallelLookup *parallel_lookups_head;

  /**
   * Lookup DLL
   */
  struct ParallelLookup *parallel_lookups_tail;
  
  /**
   * Kill task
   */
  struct GNUNET_SCHEDULER_Task *kill_task;

  /**
   * The ABE key
   */
  struct GNUNET_ABE_AbeKey *key;

  /**
   * Attributes
   */
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs;
  
  /**
   * Lookup time
   */
  struct GNUNET_TIME_Absolute lookup_start_time;
 
  /**
   * request id
   */
  uint32_t r_id;
};

/**
 * Handle for a parallel GNS lookup job
 */
struct ParallelLookup
{
  /* DLL */
  struct ParallelLookup *next;

  /* DLL */
  struct ParallelLookup *prev;

  /* The GNS request */
  struct GNUNET_GNS_LookupRequest *lookup_request;

  /* The handle the return to */
  struct ConsumeTicketHandle *handle;

  /**
   * Lookup time
   */
  struct GNUNET_TIME_Absolute lookup_start_time;

  /* The label to look up */
  char *label;
};

/**
 * Ticket revocation request handle
 */
struct TicketRevocationHandle
{
  /**
   * DLL
   */
  struct TicketRevocationHandle *next;

  /**
   * DLL
   */
  struct TicketRevocationHandle *prev;

  /**
   * Client connection
   */
  struct IdpClient *client;

  /**
   * Attributes to reissue
   */
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs;

  /**
   * Attributes to revoke
   */
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *rvk_attrs;

  /**
   * Issuer Key
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * Ticket to issue
   */
  struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;

  /**
   * QueueEntry
   */
  struct GNUNET_NAMESTORE_QueueEntry *ns_qe;

  /**
   * Namestore iterator
   */
  struct GNUNET_NAMESTORE_ZoneIterator *ns_it;

  /**
   * The ABE master key
   */
  struct GNUNET_ABE_AbeMasterKey *abe_key;

  /**
   * Offset
   */
  uint32_t offset;

  /**
   * request id
   */
  uint32_t r_id;
};



/**
 * Ticket issue request handle
 */
struct TicketIssueHandle
{

  /**
   * Client connection
   */
  struct IdpClient *client;

  /**
   * Attributes to issue
   */
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs;

  /**
   * Issuer Key
   */
  struct GNUNET_CRYPTO_EcdsaPrivateKey identity;

  /**
   * Ticket to issue
   */
  struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;

  /**
   * QueueEntry
   */
  struct GNUNET_NAMESTORE_QueueEntry *ns_qe;

  /**
   * request id
   */
  uint32_t r_id;
};


/**
 * DLL for ego handles to egos containing the ID_ATTRS in a map in json_t format
 *
 */
struct EgoEntry
{
  /**
   * DLL
   */
  struct EgoEntry *next;

  /**
   * DLL
   */
  struct EgoEntry *prev;

  /**
   * Ego handle
   */
  struct GNUNET_IDENTITY_Ego *ego;

  /**
   * Attribute map. Contains the attributes as json_t
   */
  struct GNUNET_CONTAINER_MultiHashMap *attr_map;

};

/**
 * Cleanup task
 */
static void
cleanup()
{
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Cleaning up\n");
  if (NULL != stats)
  {
    GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
    stats = NULL;
  }
  GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name,
                                              TKT_database)); 
  GNUNET_free (db_lib_name);
  db_lib_name = NULL;
  if (NULL != timeout_task)
    GNUNET_SCHEDULER_cancel (timeout_task);
  if (NULL != update_task)
    GNUNET_SCHEDULER_cancel (update_task);
  if (NULL != identity_handle)
    GNUNET_IDENTITY_disconnect (identity_handle);
  if (NULL != gns_handle)
    GNUNET_GNS_disconnect (gns_handle);
  if (NULL != credential_handle)
    GNUNET_CREDENTIAL_disconnect (credential_handle);
  if (NULL != ns_it)
    GNUNET_NAMESTORE_zone_iteration_stop (ns_it);
  if (NULL != ns_qe)
    GNUNET_NAMESTORE_cancel (ns_qe);
  if (NULL != ns_handle)
    GNUNET_NAMESTORE_disconnect (ns_handle);
  GNUNET_free_non_null (token);
  GNUNET_free_non_null (label);

}

/**
 * Shutdown task
 *
 * @param cls NULL
 */
static void
do_shutdown (void *cls)
{
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Shutting down...\n");
  cleanup();
}

/**
 * Finished storing newly bootstrapped ABE key
 */
static void
bootstrap_store_cont (void *cls,
                      int32_t success,
                      const char *emsg)
{
  struct AbeBootstrapHandle *abh = cls;
  if (GNUNET_SYSERR == success)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Failed to bootstrap ABE master %s\n",
                emsg);
    abh->proc (abh->proc_cls, NULL);
    GNUNET_free (abh->abe_key);
    GNUNET_free (abh);
    return;
  }
  abh->proc (abh->proc_cls, abh->abe_key);
  GNUNET_free (abh);
}

/**
 * Generates and stores a new ABE key
 */
static void
bootstrap_store_task (void *cls)
{
  struct AbeBootstrapHandle *abh = cls;
  struct GNUNET_GNSRECORD_Data rd[1];
  char *key;

  rd[0].data_size = GNUNET_ABE_cpabe_serialize_master_key (abh->abe_key,
                                                              (void**)&key);
  rd[0].data = key;
  rd[0].record_type = GNUNET_GNSRECORD_TYPE_ABE_MASTER;
  rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE;
  rd[0].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; //TODO sane?
  abh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
                                               &abh->identity,
                                               "+",
                                               1,
                                               rd,
                                               &bootstrap_store_cont,
                                               abh);
  GNUNET_free (key);
}

/**
 * Error checking for ABE master
 */
static void
bootstrap_abe_error (void *cls)
{
  struct AbeBootstrapHandle *abh = cls;
  abh->proc (abh->proc_cls, NULL);
  GNUNET_free (abh);
}


/**
 * Handle ABE lookup in namestore
 */
static void
bootstrap_abe_result (void *cls,
                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
                      const char *label,
                      unsigned int rd_count,
                      const struct GNUNET_GNSRECORD_Data *rd)
{
  struct AbeBootstrapHandle *abh = cls;
  struct GNUNET_ABE_AbeMasterKey *abe_key;

  for (uint32_t i=0;i<rd_count;i++) {
    if (GNUNET_GNSRECORD_TYPE_ABE_MASTER != rd[i].record_type)
      continue;
    abe_key = GNUNET_ABE_cpabe_deserialize_master_key (rd[i].data,
                                                          rd[i].data_size);
    abh->proc (abh->proc_cls, abe_key);
    GNUNET_free (abh);
    return;
  }

  //No ABE master found, bootstrapping...
  abh->abe_key = GNUNET_ABE_cpabe_create_master_key ();
  GNUNET_SCHEDULER_add_now (&bootstrap_store_task, abh);
}

/**
 * Bootstrap ABE master if it does not yet exists.
 * Will call the AbeBootstrapResult processor when done.
 * will always recreate the ABE key of GNUNET_YES == recreate
 */
static void
bootstrap_abe (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
               AbeBootstrapResult proc,
               void* cls,
               int recreate)
{
  struct AbeBootstrapHandle *abh;

  abh = GNUNET_new (struct AbeBootstrapHandle);
  abh->proc = proc;
  abh->proc_cls = cls;
  abh->identity = *identity;
  if (GNUNET_YES == recreate)
  {
    abh->abe_key = GNUNET_ABE_cpabe_create_master_key ();
    GNUNET_SCHEDULER_add_now (&bootstrap_store_task, abh);
  } else {
    abh->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle,
                                                  identity,
                                                  "+",
                                                  &bootstrap_abe_error,
                                                  abh,
                                                  &bootstrap_abe_result,
                                                  abh);
  }
}



static int
create_sym_key_from_ecdh(const struct GNUNET_HashCode *new_key_hash,
                         struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
                         struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
{
  struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str;

  GNUNET_CRYPTO_hash_to_enc (new_key_hash,
                             &new_key_hash_str);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str);
  static const char ctx_key[] = "gnuid-aes-ctx-key";
  GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
                     new_key_hash, sizeof (struct GNUNET_HashCode),
                     ctx_key, strlen (ctx_key),
                     NULL, 0);
  static const char ctx_iv[] = "gnuid-aes-ctx-iv";
  GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
                     new_key_hash, sizeof (struct GNUNET_HashCode),
                     ctx_iv, strlen (ctx_iv),
                     NULL, 0);
  return GNUNET_OK;
}

static void
cleanup_ticket_issue_handle (struct TicketIssueHandle *handle)
{
  if (NULL != handle->attrs)
    GNUNET_IDENTITY_ATTRIBUTE_list_destroy (handle->attrs);
  if (NULL != handle->ns_qe)
    GNUNET_NAMESTORE_cancel (handle->ns_qe);
  GNUNET_free (handle);
}


static void
send_ticket_result (struct IdpClient *client,
                    uint32_t r_id,
                    const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                    const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs)
{
  struct TicketResultMessage *irm;
  struct GNUNET_MQ_Envelope *env;
  struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket_buf;

  /* store ticket in DB */
  if (GNUNET_OK != TKT_database->store_ticket (TKT_database->cls,
                                               ticket,
                                               attrs))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unable to store ticket after issue\n");
    GNUNET_break (0);
  }

  env = GNUNET_MQ_msg_extra (irm,
                             sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket),
                             GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_TICKET_RESULT);
  ticket_buf = (struct GNUNET_IDENTITY_PROVIDER_Ticket *)&irm[1];
  *ticket_buf = *ticket;
  irm->id = htonl (r_id);
  GNUNET_MQ_send (client->mq,
                  env);
}

static void
store_ticket_issue_cont (void *cls,
                         int32_t success,
                         const char *emsg)
{
  struct TicketIssueHandle *handle = cls;

  handle->ns_qe = NULL;
  if (GNUNET_SYSERR == success)
  {
    cleanup_ticket_issue_handle (handle);
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
                "Unknown Error\n");
    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
    return;
  }
  send_ticket_result (handle->client,
                      handle->r_id,
                      &handle->ticket,
                      handle->attrs);
  cleanup_ticket_issue_handle (handle);
}



int
serialize_abe_keyinfo2 (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                        const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs,
                        const struct GNUNET_ABE_AbeKey *rp_key,
                        struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
                        char **result)
{
  struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;
  char *enc_keyinfo;
  char *serialized_key;
  char *buf;
  char *write_ptr;
  char attrs_str_len;
  ssize_t size;

  struct GNUNET_CRYPTO_SymmetricSessionKey skey;
  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
  struct GNUNET_HashCode new_key_hash;
  ssize_t enc_size;

  size = GNUNET_ABE_cpabe_serialize_key (rp_key,
                                            (void**)&serialized_key);
  attrs_str_len = 0;
  for (le = attrs->list_head; NULL != le; le = le->next) {
    attrs_str_len += strlen (le->claim->name) + 1;
  }
  buf = GNUNET_malloc (attrs_str_len + size);
  write_ptr = buf;
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Writing attributes\n");
  for (le = attrs->list_head; NULL != le; le = le->next) {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "%s\n", le->claim->name);


    GNUNET_memcpy (write_ptr,
                   le->claim->name,
                   strlen (le->claim->name));
    write_ptr[strlen (le->claim->name)] = ',';
    write_ptr += strlen (le->claim->name) + 1;
  }
  write_ptr--;
  write_ptr[0] = '\0'; //replace last , with a 0-terminator
  write_ptr++;
  GNUNET_memcpy (write_ptr,
                 serialized_key,
                 size);
  GNUNET_free (serialized_key);
  // ECDH keypair E = eG
  *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create();
  GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey,
                                      &ecdh_pubkey);
  enc_keyinfo = GNUNET_malloc (size + attrs_str_len);
  // Derived key K = H(eB)
  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey,
                                                        &ticket->audience,
                                                        &new_key_hash));
  create_sym_key_from_ecdh(&new_key_hash, &skey, &iv);
  enc_size = GNUNET_CRYPTO_symmetric_encrypt (buf,
                                              size + attrs_str_len,
                                              &skey, &iv,
                                              enc_keyinfo);
  *result = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+
                           enc_size);
  GNUNET_memcpy (*result,
                 &ecdh_pubkey,
                 sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  GNUNET_memcpy (*result + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
                 enc_keyinfo,
                 enc_size);
  GNUNET_free (enc_keyinfo);
  GNUNET_free (buf);
  return sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)+enc_size;
}



static void
issue_ticket_after_abe_bootstrap (void *cls,
                                  struct GNUNET_ABE_AbeMasterKey *abe_key)
{
  struct TicketIssueHandle *ih = cls;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;
  struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
  struct GNUNET_GNSRECORD_Data code_record[1];
  struct GNUNET_ABE_AbeKey *rp_key;
  char *code_record_data;
  char **attrs;
  char *label;
  char *policy;
  int attrs_len;
  uint32_t i;
  size_t code_record_len;

  //Create new ABE key for RP
  attrs_len = 0;
  for (le = ih->attrs->list_head; NULL != le; le = le->next)
    attrs_len++;
  attrs = GNUNET_malloc ((attrs_len + 1)*sizeof (char*));
  i = 0;
  for (le = ih->attrs->list_head; NULL != le; le = le->next) {
    GNUNET_asprintf (&policy, "%s_%lu",
                     le->claim->name,
                     le->claim->version);
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Adding attribute to key: %s\n",
                policy);
    attrs[i] = policy;
    i++;
  }
  attrs[i] = NULL;
  rp_key = GNUNET_ABE_cpabe_create_key (abe_key,
                                           attrs);

  //TODO review this wireformat
  code_record_len = serialize_abe_keyinfo2 (&ih->ticket,
                                            ih->attrs,
                                            rp_key,
                                            &ecdhe_privkey,
                                            &code_record_data);
  code_record[0].data = code_record_data;
  code_record[0].data_size = code_record_len;
  code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
  code_record[0].record_type = GNUNET_GNSRECORD_TYPE_ABE_KEY;
  code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;

  label = GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd,
                                               sizeof (uint64_t));
  //Publish record
  ih->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
                                              &ih->identity,
                                              label,
                                              1,
                                              code_record,
                                              &store_ticket_issue_cont,
                                              ih);
  //for (; i > 0; i--)
  //  GNUNET_free (attrs[i-1]);
  GNUNET_free (ecdhe_privkey);
  GNUNET_free (label);
  GNUNET_free (attrs);
  GNUNET_free (code_record_data);
  GNUNET_ABE_cpabe_delete_key (rp_key,
                                  GNUNET_YES);
  GNUNET_ABE_cpabe_delete_master_key (abe_key);
}


static int
check_issue_ticket_message(void *cls,
                           const struct IssueTicketMessage *im)
{
  uint16_t size;

  size = ntohs (im->header.size);
  if (size <= sizeof (struct IssueTicketMessage))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


static void
handle_issue_ticket_message (void *cls,
                             const struct IssueTicketMessage *im)
{
  struct TicketIssueHandle *ih;
  struct IdpClient *idp = cls;
  size_t attrs_len;

  ih = GNUNET_new (struct TicketIssueHandle);
  attrs_len = ntohs (im->attr_len);
  ih->attrs = GNUNET_IDENTITY_ATTRIBUTE_list_deserialize ((char*)&im[1], attrs_len);
  ih->r_id = ntohl (im->id);
  ih->client = idp;
  ih->identity = im->identity;
  GNUNET_CRYPTO_ecdsa_key_get_public (&ih->identity,
                                      &ih->ticket.identity);
  ih->ticket.audience = im->rp;
  ih->ticket.rnd =
    GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG,
                              UINT64_MAX);
  bootstrap_abe (&ih->identity, &issue_ticket_after_abe_bootstrap, ih, GNUNET_NO);
  GNUNET_SERVICE_client_continue (idp->client);

}

/**********************************************************
 * Revocation
 **********************************************************/

/**
 * Cleanup revoke handle
 *
 * @param rh the ticket revocation handle
 */
static void
cleanup_revoke_ticket_handle (struct TicketRevocationHandle *rh)
{
  if (NULL != rh->attrs)
    GNUNET_IDENTITY_ATTRIBUTE_list_destroy (rh->attrs);
  if (NULL != rh->rvk_attrs)
    GNUNET_IDENTITY_ATTRIBUTE_list_destroy (rh->rvk_attrs);
  if (NULL != rh->abe_key)
    GNUNET_ABE_cpabe_delete_master_key (rh->abe_key);
  if (NULL != rh->ns_qe)
    GNUNET_NAMESTORE_cancel (rh->ns_qe);
  if (NULL != rh->ns_it)
    GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it);
  GNUNET_free (rh);
}


/**
 * Send revocation result
 *
 * @param rh ticket revocation handle
 * @param success GNUNET_OK if successful result
 */
static void
send_revocation_finished (struct TicketRevocationHandle *rh,
                          uint32_t success)
{
  struct GNUNET_MQ_Envelope *env;
  struct RevokeTicketResultMessage *trm;

  env = GNUNET_MQ_msg (trm,
                       GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_REVOKE_TICKET_RESULT);
  trm->id = htonl (rh->r_id);
  trm->success = htonl (success);
  GNUNET_MQ_send (rh->client->mq,
                  env);
  GNUNET_CONTAINER_DLL_remove (rh->client->revocation_list_head,
                               rh->client->revocation_list_tail,
                               rh);
}


/**
 * Process ticket from database
 *
 * @param cls struct TicketIterationProcResult
 * @param ticket the ticket
 * @param attrs the attributes
 */
static void
ticket_reissue_proc (void *cls,
                     const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                     const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs);

static void
revocation_reissue_tickets (struct TicketRevocationHandle *rh);


static void reissue_next (void *cls)
{
  struct TicketRevocationHandle *rh = cls;
  revocation_reissue_tickets (rh);
}


static void
reissue_ticket_cont (void *cls,
                     int32_t success,
                     const char *emsg)
{
  struct TicketRevocationHandle *rh = cls;

  rh->ns_qe = NULL;
  if (GNUNET_SYSERR == success)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n",
                "Unknown Error\n");
    send_revocation_finished (rh, GNUNET_SYSERR);
    cleanup_revoke_ticket_handle (rh);
    return;
  }
  rh->offset++;
  GNUNET_SCHEDULER_add_now (&reissue_next, rh);
}


/**
 * Process ticket from database
 *
 * @param cls struct TicketIterationProcResult
 * @param ticket the ticket
 * @param attrs the attributes
 */
static void
ticket_reissue_proc (void *cls,
                     const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                     const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs)
{
  struct TicketRevocationHandle *rh = cls;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le_rollover;
  struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;
  struct GNUNET_GNSRECORD_Data code_record[1];
  struct GNUNET_ABE_AbeKey *rp_key;
  char *code_record_data;
  char **attr_arr;
  char *label;
  char *policy;
  int attrs_len;
  uint32_t i;
  int reissue_ticket;
  size_t code_record_len;


  if (NULL == ticket)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Iteration done\n");
    return;
  }

  if (0 == memcmp (&ticket->audience,
                   &rh->ticket.audience,
                   sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Do not reissue for this identity.!\n");

    rh->offset++;
    GNUNET_SCHEDULER_add_now (&reissue_next, rh);
    return;
  }

  /* 
   * Check if any attribute of this ticket intersects with a rollover attribute
   */
  reissue_ticket = GNUNET_NO;
  for (le = attrs->list_head; NULL != le; le = le->next)
  {
    for (le_rollover = rh->rvk_attrs->list_head;
         NULL != le_rollover;
         le_rollover = le_rollover->next)
    {
      if (0 == strcmp (le_rollover->claim->name,
                       le->claim->name))
      {
        reissue_ticket = GNUNET_YES;
        le->claim->version = le_rollover->claim->version;
      }
    }
  }

  if (GNUNET_NO == reissue_ticket)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Skipping ticket.\n");

    rh->offset++;
    GNUNET_SCHEDULER_add_now (&reissue_next, rh);


    return;
  }

  //Create new ABE key for RP
  attrs_len = 0;

  /* If this is the RP we want to revoke attributes of, the do so */

  for (le = attrs->list_head; NULL != le; le = le->next)
    attrs_len++;
  attr_arr = GNUNET_malloc ((attrs_len + 1)*sizeof (char*));
  i = 0;
  for (le = attrs->list_head; NULL != le; le = le->next) {
    GNUNET_asprintf (&policy, "%s_%lu",
                     le->claim->name,
                     le->claim->version);
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Recreating key with %s\n", policy);
    attr_arr[i] = policy;
    i++;
  }
  attr_arr[i] = NULL;
  rp_key = GNUNET_ABE_cpabe_create_key (rh->abe_key,
                                           attr_arr);

  //TODO review this wireformat
  code_record_len = serialize_abe_keyinfo2 (ticket,
                                            attrs,
                                            rp_key,
                                            &ecdhe_privkey,
                                            &code_record_data);
  code_record[0].data = code_record_data;
  code_record[0].data_size = code_record_len;
  code_record[0].expiration_time = GNUNET_TIME_UNIT_DAYS.rel_value_us;
  code_record[0].record_type = GNUNET_GNSRECORD_TYPE_ABE_KEY;
  code_record[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;

  label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
                                               sizeof (uint64_t));
  //Publish record
  rh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
                                              &rh->identity,
                                              label,
                                              1,
                                              code_record,
                                              &reissue_ticket_cont,
                                              rh);
  //for (; i > 0; i--)
  //  GNUNET_free (attr_arr[i-1]);
  GNUNET_free (ecdhe_privkey);
  GNUNET_free (label);
  GNUNET_free (attr_arr);
  GNUNET_free (code_record_data);
  GNUNET_ABE_cpabe_delete_key (rp_key, GNUNET_YES);
}


/* Prototype for below function */
static void
attr_reenc_cont (void *cls,
                 int32_t success,
                 const char *emsg);

static void
revocation_reissue_tickets (struct TicketRevocationHandle *rh)
{
  int ret;
  /* Done, issue new keys */
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Revocation Phase III: Reissuing Tickets\n");
  if (GNUNET_SYSERR == (ret = TKT_database->iterate_tickets (TKT_database->cls,
                                                             &rh->ticket.identity,
                                                             GNUNET_NO,
                                                             rh->offset,
                                                             &ticket_reissue_proc,
                                                             rh)))
  {
    GNUNET_break (0);
  }
  if (GNUNET_NO == ret)
  {
    send_revocation_finished (rh, GNUNET_OK);
    cleanup_revoke_ticket_handle (rh);
    return;
  }
}

/**
 * Revoke next attribte by reencryption with
 * new ABE master
 */
static void
reenc_next_attribute (struct TicketRevocationHandle *rh)
{
  struct GNUNET_GNSRECORD_Data rd[1];
  char* buf;
  char* enc_buf;
  size_t enc_size;
  char* rd_buf;
  size_t buf_size;
  char* policy;
  uint32_t attr_ver;

  if (NULL == rh->attrs->list_head)
  {
    revocation_reissue_tickets (rh);
    return;
  }
  buf_size = GNUNET_IDENTITY_ATTRIBUTE_serialize_get_size (rh->attrs->list_head->claim);
  buf = GNUNET_malloc (buf_size);
  GNUNET_IDENTITY_ATTRIBUTE_serialize (rh->attrs->list_head->claim,
                       buf);
  rh->attrs->list_head->claim->version++;
  GNUNET_asprintf (&policy, "%s_%lu",
                   rh->attrs->list_head->claim->name,
                   rh->attrs->list_head->claim->version);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Encrypting with policy %s\n", policy);
  /**
   * Encrypt the attribute value and store in namestore
   */
  enc_size = GNUNET_ABE_cpabe_encrypt (buf,
                                          buf_size,
                                          policy, //Policy
                                          rh->abe_key,
                                          (void**)&enc_buf);
  GNUNET_free (buf);
  if (GNUNET_SYSERR == enc_size)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unable to re-encrypt with policy %s\n",
                policy);
    GNUNET_free (policy);
    send_revocation_finished (rh, GNUNET_SYSERR);
    cleanup_revoke_ticket_handle (rh);
    return;
  }
  GNUNET_free (policy);

  rd[0].data_size = enc_size + sizeof (uint32_t);
  rd_buf = GNUNET_malloc (rd[0].data_size);
  attr_ver = htonl (rh->attrs->list_head->claim->version);
  GNUNET_memcpy (rd_buf,
                 &attr_ver,
                 sizeof (uint32_t));
  GNUNET_memcpy (rd_buf+sizeof (uint32_t),
                 enc_buf,
                 enc_size);
  rd[0].data = rd_buf;
  rd[0].record_type = GNUNET_GNSRECORD_TYPE_ID_ATTR;
  rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
  rd[0].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; //TODO sane?
  rh->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
                                              &rh->identity,
                                              rh->attrs->list_head->claim->name,
                                              1,
                                              rd,
                                              &attr_reenc_cont,
                                              rh);
  GNUNET_free (enc_buf);
  GNUNET_free (rd_buf);
}

/**
 * Namestore callback after revoked attribute
 * is stored
 */
static void
attr_reenc_cont (void *cls,
                 int32_t success,
                 const char *emsg)
{
  struct TicketRevocationHandle *rh = cls;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;

  if (GNUNET_SYSERR == success)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Failed to reencrypt attribute %s\n",
                emsg);
    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
    return;
  }
  if (NULL == rh->attrs->list_head)
  {
    revocation_reissue_tickets (rh);
    return;
  }
  le = rh->attrs->list_head;
  GNUNET_CONTAINER_DLL_remove (rh->attrs->list_head,
                               rh->attrs->list_tail,
                               le);
  GNUNET_assert (NULL != rh->rvk_attrs);
  GNUNET_CONTAINER_DLL_insert (rh->rvk_attrs->list_head,
                               rh->rvk_attrs->list_tail,
                               le);


  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Re-encrypting next attribute\n");
  reenc_next_attribute (rh);
}


static void
process_attributes_to_update (void *cls,
                              const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                              const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs)
{
  struct TicketRevocationHandle *rh = cls;

  rh->attrs = GNUNET_IDENTITY_ATTRIBUTE_list_dup (attrs);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Revocation Phase I: Collecting attributes\n");
  /* Reencrypt all attributes with new key */
  if (NULL == rh->attrs->list_head)
  {
    /* No attributes to reencrypt */
    send_revocation_finished (rh, GNUNET_OK);
    cleanup_revoke_ticket_handle (rh);
    return;
  } else {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Revocation Phase II: Re-encrypting attributes\n");
    reenc_next_attribute (rh);
  }

}



static void
get_ticket_after_abe_bootstrap (void *cls,
                                struct GNUNET_ABE_AbeMasterKey *abe_key)
{
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Finished ABE bootstrap\n");
  struct TicketRevocationHandle *rh = cls;
  rh->abe_key = abe_key;
  TKT_database->get_ticket_attributes (TKT_database->cls,
                                       &rh->ticket,
                                       &process_attributes_to_update,
                                       rh);
}

static int
check_revoke_ticket_message(void *cls,
                            const struct RevokeTicketMessage *im)
{
  uint16_t size;

  size = ntohs (im->header.size);
  if (size <= sizeof (struct RevokeTicketMessage))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}

static void
handle_revoke_ticket_message (void *cls,
                              const struct RevokeTicketMessage *rm)
{
  struct TicketRevocationHandle *rh;
  struct IdpClient *idp = cls;
  struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket;

  rh = GNUNET_new (struct TicketRevocationHandle);
  ticket = (struct GNUNET_IDENTITY_PROVIDER_Ticket*)&rm[1];
  rh->rvk_attrs = GNUNET_new (struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList);
  rh->ticket = *ticket;
  rh->r_id = ntohl (rm->id);
  rh->client = idp;
  rh->identity = rm->identity;
  GNUNET_CRYPTO_ecdsa_key_get_public (&rh->identity,
                                      &rh->ticket.identity);
  GNUNET_CONTAINER_DLL_insert (idp->revocation_list_head,
                               idp->revocation_list_tail,
                               rh);
  bootstrap_abe (&rh->identity, &get_ticket_after_abe_bootstrap, rh, GNUNET_NO);
  GNUNET_SERVICE_client_continue (idp->client);

}


static void
cleanup_consume_ticket_handle (struct ConsumeTicketHandle *handle)
{
  if (NULL != handle->key)
    GNUNET_ABE_cpabe_delete_key (handle->key,
                                 GNUNET_YES);
  if (NULL != handle->attrs)
    GNUNET_IDENTITY_ATTRIBUTE_list_destroy (handle->attrs);
  GNUNET_free (handle);
}



static int
check_consume_ticket_message(void *cls,
                             const struct ConsumeTicketMessage *cm)
{
  uint16_t size;

  size = ntohs (cm->header.size);
  if (size <= sizeof (struct ConsumeTicketMessage))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}

static void
process_parallel_lookup2 (void *cls, uint32_t rd_count,
                          const struct GNUNET_GNSRECORD_Data *rd)
{
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Parallel lookup finished (count=%u)\n", rd_count);
  struct ParallelLookup *parallel_lookup = cls;
  struct ConsumeTicketHandle *handle = parallel_lookup->handle;
  struct ConsumeTicketResultMessage *crm;
  struct GNUNET_MQ_Envelope *env;
  struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *attr_le;
  struct GNUNET_TIME_Absolute decrypt_duration;
  char *data;
  char *data_tmp;
  ssize_t attr_len;
  size_t attrs_len;

  GNUNET_CONTAINER_DLL_remove (handle->parallel_lookups_head,
                               handle->parallel_lookups_tail,
                               parallel_lookup);
  GNUNET_free (parallel_lookup->label);

  GNUNET_STATISTICS_update (stats,
                            "attribute_lookup_time_total",
                            GNUNET_TIME_absolute_get_duration (parallel_lookup->lookup_start_time).rel_value_us,
                            GNUNET_YES);
  GNUNET_STATISTICS_update (stats,
                            "attribute_lookups_count",
                            1,
                            GNUNET_YES);


  GNUNET_free (parallel_lookup);
  if (1 != rd_count)
    GNUNET_break(0);//TODO
  if (rd->record_type == GNUNET_GNSRECORD_TYPE_ID_ATTR)
  {
    decrypt_duration = GNUNET_TIME_absolute_get ();
    attr_len = GNUNET_ABE_cpabe_decrypt (rd->data + sizeof (uint32_t),
                                         rd->data_size - sizeof (uint32_t),
                                         handle->key,
                                         (void**)&data);
    if (GNUNET_SYSERR != attr_len) 
    {
      GNUNET_STATISTICS_update (stats,
                                "abe_decrypt_time_total",
                                GNUNET_TIME_absolute_get_duration (decrypt_duration).rel_value_us,
                                GNUNET_YES);
      GNUNET_STATISTICS_update (stats,
                                "abe_decrypt_count",
                                1,
                                GNUNET_YES);

      attr_le = GNUNET_new (struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry);
      attr_le->claim = GNUNET_IDENTITY_ATTRIBUTE_deserialize (data,
                                                              attr_len);
      attr_le->claim->version = ntohl(*(uint32_t*)rd->data);
      GNUNET_CONTAINER_DLL_insert (handle->attrs->list_head,
                                   handle->attrs->list_tail,
                                   attr_le);
      GNUNET_free (data);
    }
  }
  if (NULL != handle->parallel_lookups_head)
    return; //Wait for more
  /* Else we are done */

  /* Store ticket in DB */
  if (GNUNET_OK != TKT_database->store_ticket (TKT_database->cls,
                                               &handle->ticket,
                                               handle->attrs))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unable to store ticket after consume\n");
    GNUNET_break (0);
  }

  GNUNET_SCHEDULER_cancel (handle->kill_task);
  attrs_len = GNUNET_IDENTITY_ATTRIBUTE_list_serialize_get_size (handle->attrs);
  env = GNUNET_MQ_msg_extra (crm,
                             attrs_len,
                             GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_CONSUME_TICKET_RESULT);
  crm->id = htonl (handle->r_id);
  crm->attrs_len = htons (attrs_len);
  crm->identity = handle->ticket.identity;
  data_tmp = (char *) &crm[1];
  GNUNET_IDENTITY_ATTRIBUTE_list_serialize (handle->attrs,
                                            data_tmp);
  GNUNET_MQ_send (handle->client->mq, env);
  cleanup_consume_ticket_handle (handle);
}

void
abort_parallel_lookups2 (void *cls)
{
  struct ConsumeTicketHandle *handle = cls;
  struct ParallelLookup *lu;
  struct ParallelLookup *tmp;
  struct AttributeResultMessage *arm;
  struct GNUNET_MQ_Envelope *env;

  for (lu = handle->parallel_lookups_head;
       NULL != lu;) {
    GNUNET_GNS_lookup_cancel (lu->lookup_request);
    GNUNET_free (lu->label);
    tmp = lu->next;
    GNUNET_CONTAINER_DLL_remove (handle->parallel_lookups_head,
                                 handle->parallel_lookups_tail,
                                 lu);
    GNUNET_free (lu);
    lu = tmp;
  }
  env = GNUNET_MQ_msg (arm,
                       GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_RESULT);
  arm->id = htonl (handle->r_id);
  arm->attr_len = htons (0);
  GNUNET_MQ_send (handle->client->mq, env);

}


static void
process_consume_abe_key (void *cls, uint32_t rd_count,
                         const struct GNUNET_GNSRECORD_Data *rd)
{
  struct ConsumeTicketHandle *handle = cls;
  struct GNUNET_HashCode new_key_hash;
  struct GNUNET_CRYPTO_SymmetricSessionKey enc_key;
  struct GNUNET_CRYPTO_SymmetricInitializationVector enc_iv;
  struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_key;
  struct ParallelLookup *parallel_lookup;
  size_t size;
  char *buf;
  char *scope;
  char *lookup_query;

  handle->lookup_request = NULL;
  if (1 != rd_count)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Number of keys %d != 1.",
                rd_count);
    cleanup_consume_ticket_handle (handle);
    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
    return;
  }

  //Decrypt
  ecdh_key = (struct GNUNET_CRYPTO_EcdhePublicKey *)rd->data;

  buf = GNUNET_malloc (rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));

  //Calculate symmetric key from ecdh parameters
  GNUNET_assert (GNUNET_OK == 
                 GNUNET_CRYPTO_ecdsa_ecdh (&handle->identity,
                                           ecdh_key,
                                           &new_key_hash));
  create_sym_key_from_ecdh (&new_key_hash,
                            &enc_key,
                            &enc_iv);
  size = GNUNET_CRYPTO_symmetric_decrypt (rd->data + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
                                          rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey),
                                          &enc_key,
                                          &enc_iv,
                                          buf);

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Decrypted bytes: %zd Expected bytes: %zd\n",
              size, rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  GNUNET_STATISTICS_update (stats,
                            "abe_key_lookup_time_total",
                            GNUNET_TIME_absolute_get_duration (handle->lookup_start_time).rel_value_us,
                            GNUNET_YES);
  GNUNET_STATISTICS_update (stats,
                            "abe_key_lookups_count",
                            1,
                            GNUNET_YES);
  scopes = GNUNET_strdup (buf);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Scopes %s\n", scopes);
  handle->key = GNUNET_ABE_cpabe_deserialize_key ((void*)(buf + strlen (scopes) + 1),
                                                  rd->data_size - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)
                                                  - strlen (scopes) - 1);

  for (scope = strtok (scopes, ","); NULL != scope; scope = strtok (NULL, ","))
  {
    GNUNET_asprintf (&lookup_query,
                     "%s.gnu",
                     scope);
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Looking up %s\n", lookup_query);
    parallel_lookup = GNUNET_new (struct ParallelLookup);
    parallel_lookup->handle = handle;
    parallel_lookup->label = GNUNET_strdup (scope);
    parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get();
    parallel_lookup->lookup_request
      = GNUNET_GNS_lookup (gns_handle,
                           lookup_query,
                           &handle->ticket.identity,
                           GNUNET_GNSRECORD_TYPE_ID_ATTR,
                           GNUNET_GNS_LO_DEFAULT,
                           &process_parallel_lookup2,
                           parallel_lookup);
    GNUNET_CONTAINER_DLL_insert (handle->parallel_lookups_head,
                                 handle->parallel_lookups_tail,
                                 parallel_lookup);
    GNUNET_free (lookup_query);
  }
  GNUNET_free (scopes);
  GNUNET_free (buf);
  handle->kill_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES,3),
                                                    &abort_parallel_lookups2,
                                                    handle);
}


static void
handle_consume_ticket_message (void *cls,
                               const struct ConsumeTicketMessage *cm)
{
  struct ConsumeTicketHandle *ch;
  struct IdpClient *idp = cls;
  char* lookup_query;
  char* rnd_label;

  ch = GNUNET_new (struct ConsumeTicketHandle);
  ch->r_id = ntohl (cm->id);
  ch->client = idp;
  ch->identity = cm->identity;
  ch->attrs = GNUNET_new (struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList);
  GNUNET_CRYPTO_ecdsa_key_get_public (&ch->identity,
                                      &ch->identity_pub);
  ch->ticket = *((struct GNUNET_IDENTITY_PROVIDER_Ticket*)&cm[1]);
  rnd_label = GNUNET_STRINGS_data_to_string_alloc (&ch->ticket.rnd,
                                                   sizeof (uint64_t));
  GNUNET_asprintf (&lookup_query,
                   "%s.gnu",
                   rnd_label);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Looking for ABE key under %s\n", lookup_query);
  ch->lookup_start_time = GNUNET_TIME_absolute_get ();
  ch->lookup_request
    = GNUNET_GNS_lookup (gns_handle,
                         lookup_query,
                         &ch->ticket.identity,
                         GNUNET_GNSRECORD_TYPE_ABE_KEY,
                         GNUNET_GNS_LO_DEFAULT,
                         &process_consume_abe_key,
                         ch);
  GNUNET_free (rnd_label);
  GNUNET_free (lookup_query);
  GNUNET_SERVICE_client_continue (idp->client);
}

static void
cleanup_as_handle (struct AttributeStoreHandle *handle)
{
  if (NULL != handle->claim)
    GNUNET_free (handle->claim);
  if (NULL != handle->abe_key)
    GNUNET_ABE_cpabe_delete_master_key (handle->abe_key);
  GNUNET_free (handle);
}

static void
attr_store_cont (void *cls,
                 int32_t success,
                 const char *emsg)
{
  struct AttributeStoreHandle *as_handle = cls;
  struct GNUNET_MQ_Envelope *env;
  struct AttributeStoreResultMessage *acr_msg;

  if (GNUNET_SYSERR == success)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Failed to store attribute %s\n",
                emsg);
    cleanup_as_handle (as_handle);
    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
    return;
  }

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Sending ATTRIBUTE_STORE_RESPONSE message\n");
  env = GNUNET_MQ_msg (acr_msg,
                       GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_STORE_RESPONSE);
  acr_msg->id = htonl (as_handle->r_id);
  acr_msg->op_result = htonl (GNUNET_OK);
  GNUNET_MQ_send (as_handle->client->mq,
                  env);
  cleanup_as_handle (as_handle);
}

static void
attr_store_task (void *cls)
{
  struct AttributeStoreHandle *as_handle = cls;
  struct GNUNET_GNSRECORD_Data rd[1];
  char* buf;
  char* policy;
  char* enc_buf;
  char* rd_buf;
  size_t enc_size;
  size_t buf_size;
  uint32_t attr_ver;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Storing attribute\n");
  buf_size = GNUNET_IDENTITY_ATTRIBUTE_serialize_get_size (as_handle->claim);
  buf = GNUNET_malloc (buf_size);

  GNUNET_IDENTITY_ATTRIBUTE_serialize (as_handle->claim,
                                       buf);

  GNUNET_asprintf (&policy,
                   "%s_%lu",
                   as_handle->claim->name,
                   as_handle->claim->version);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Encrypting with policy %s\n", policy);
  /**
   * Encrypt the attribute value and store in namestore
   */
  enc_size = GNUNET_ABE_cpabe_encrypt (buf,
                                       buf_size,
                                       policy, //Policy
                                       as_handle->abe_key,
                                       (void**)&enc_buf);
  if (GNUNET_SYSERR == enc_size)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Failed to encrypt with policy %s\n",
                policy);
    cleanup_as_handle (as_handle);
    GNUNET_free (buf);
    GNUNET_free (policy);
    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
    return;
  }
  GNUNET_free (buf);
  GNUNET_free (policy);
  rd[0].data_size = enc_size + sizeof (uint32_t);
  rd_buf = GNUNET_malloc (rd[0].data_size);
  attr_ver = htonl (as_handle->claim->version);
  GNUNET_memcpy (rd_buf,
                 &attr_ver,
                 sizeof (uint32_t));
  GNUNET_memcpy (rd_buf+sizeof (uint32_t),
                 enc_buf,
                 enc_size);
  rd[0].data = rd_buf;
  rd[0].record_type = GNUNET_GNSRECORD_TYPE_ID_ATTR;
  rd[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
  rd[0].expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; //TODO sane?
  as_handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
                                                     &as_handle->identity,
                                                     as_handle->claim->name,
                                                     1,
                                                     rd,
                                                     &attr_store_cont,
                                                     as_handle);
  GNUNET_free (enc_buf);
  GNUNET_free (rd_buf);
}


static void
store_after_abe_bootstrap (void *cls,
                           struct GNUNET_ABE_AbeMasterKey *abe_key)
{
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Finished ABE bootstrap\n");
  struct AttributeStoreHandle *ash = cls;
  ash->abe_key = abe_key;
  GNUNET_SCHEDULER_add_now (&attr_store_task, ash);
}

static int
check_attribute_store_message(void *cls,
                              const struct AttributeStoreMessage *sam)
{
  uint16_t size;

  size = ntohs (sam->header.size);
  if (size <= sizeof (struct AttributeStoreMessage))
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


static void
handle_attribute_store_message (void *cls,
                                const struct AttributeStoreMessage *sam)
{
  struct AttributeStoreHandle *as_handle;
  struct IdpClient *idp = cls;
  size_t data_len;
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received ATTRIBUTE_STORE message\n");

  data_len = ntohs (sam->attr_len);

  as_handle = GNUNET_new (struct AttributeStoreHandle);
  as_handle->claim = GNUNET_IDENTITY_ATTRIBUTE_deserialize ((char*)&sam[1],
                                                            data_len);

  as_handle->r_id = ntohl (sam->id);
  as_handle->identity = sam->identity;
  GNUNET_CRYPTO_ecdsa_key_get_public (&sam->identity,
                                      &as_handle->identity_pkey);

  GNUNET_SERVICE_client_continue (idp->client);
  as_handle->client = idp;
  bootstrap_abe (&as_handle->identity, &store_after_abe_bootstrap, as_handle, GNUNET_NO);
}

static void
cleanup_iter_handle (struct AttributeIterator *ai)
{
  if (NULL != ai->abe_key)
    GNUNET_ABE_cpabe_delete_master_key (ai->abe_key);
  GNUNET_CONTAINER_DLL_remove (ai->client->op_head,
                               ai->client->op_tail,
                               ai);
  GNUNET_free (ai);
}

static void
attr_iter_error (void *cls)
{
  struct AttributeIterator *ai = cls;
  //TODO
  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
              "Failed to iterate over attributes\n");
  cleanup_iter_handle (ai);
  GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
}

static void
attr_iter_finished (void *cls)
{
  struct AttributeIterator *ai = cls;
  struct GNUNET_MQ_Envelope *env;
  struct AttributeResultMessage *arm;

  env = GNUNET_MQ_msg (arm,
                       GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_RESULT);
  arm->id = htonl (ai->request_id);
  arm->attr_len = htons (0);
  GNUNET_MQ_send (ai->client->mq, env);
  cleanup_iter_handle (ai);
}

static void
attr_iter_cb (void *cls,
              const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
              const char *label,
              unsigned int rd_count,
              const struct GNUNET_GNSRECORD_Data *rd)
{
  struct AttributeIterator *ai = cls;
  struct AttributeResultMessage *arm;
  struct GNUNET_ABE_AbeKey *key;
  struct GNUNET_MQ_Envelope *env;
  ssize_t msg_extra_len;
  char* attr_ser;
  char* attrs[2];
  char* data_tmp;
  char* policy;
  uint32_t attr_ver;

  if (rd_count != 1)
  {
    GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it);
    return;
  }

  if (GNUNET_GNSRECORD_TYPE_ID_ATTR != rd->record_type) {
    GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it);
    return;
  }
  attr_ver = ntohl(*((uint32_t*)rd->data));
  GNUNET_asprintf (&policy, "%s_%lu",
                   label, attr_ver);
  attrs[0] = policy;
  attrs[1] = 0;
  key = GNUNET_ABE_cpabe_create_key (ai->abe_key,
                                     attrs);
  msg_extra_len = GNUNET_ABE_cpabe_decrypt (rd->data+sizeof (uint32_t),
                                            rd->data_size-sizeof (uint32_t),
                                            key,
                                            (void**)&attr_ser);
  if (GNUNET_SYSERR == msg_extra_len) {
    GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it);
    return;
  }

  GNUNET_ABE_cpabe_delete_key (key,
                               GNUNET_YES);
  //GNUNET_free (policy);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Found attribute: %s\n", label);
  env = GNUNET_MQ_msg_extra (arm,
                             msg_extra_len,
                             GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_RESULT);
  arm->id = htonl (ai->request_id);
  arm->attr_len = htons (msg_extra_len);
  GNUNET_CRYPTO_ecdsa_key_get_public (zone,
                                      &arm->identity);
  data_tmp = (char *) &arm[1];
  GNUNET_memcpy (data_tmp,
                 attr_ser,
                 msg_extra_len);
  GNUNET_MQ_send (ai->client->mq, env);
  GNUNET_free (attr_ser);
  GNUNET_ABE_cpabe_delete_master_key (ai->abe_key);
  ai->abe_key = NULL;
}


void
iterate_after_abe_bootstrap (void *cls,
                             struct GNUNET_ABE_AbeMasterKey *abe_key)
{
  struct AttributeIterator *ai = cls;
  ai->abe_key = abe_key;
  ai->ns_it = GNUNET_NAMESTORE_zone_iteration_start (ns_handle,
                                                     &ai->identity,
                                                     &attr_iter_error,
                                                     ai,
                                                     &attr_iter_cb,
                                                     ai,
                                                     &attr_iter_finished,
                                                     ai);
}

void
iterate_next_after_abe_bootstrap (void *cls,
                                  struct GNUNET_ABE_AbeMasterKey *abe_key)
{
  struct AttributeIterator *ai = cls;
  ai->abe_key = abe_key;
  GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it);
}



static void
handle_iteration_start (void *cls,
                        const struct AttributeIterationStartMessage *ais_msg)
{
  struct IdpClient *idp = cls;
  struct AttributeIterator *ai;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received ATTRIBUTE_ITERATION_START message\n");
  ai = GNUNET_new (struct AttributeIterator);
  ai->request_id = ntohl (ais_msg->id);
  ai->client = idp;
  ai->identity = ais_msg->identity;

  GNUNET_CONTAINER_DLL_insert (idp->op_head,
                               idp->op_tail,
                               ai);
  bootstrap_abe (&ai->identity, &iterate_after_abe_bootstrap, ai, GNUNET_NO);
  GNUNET_SERVICE_client_continue (idp->client);
}


static void
handle_iteration_stop (void *cls,
                       const struct AttributeIterationStopMessage *ais_msg)
{
  struct IdpClient *idp = cls;
  struct AttributeIterator *ai;
  uint32_t rid;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received `%s' message\n",
              "ATTRIBUTE_ITERATION_STOP");
  rid = ntohl (ais_msg->id);
  for (ai = idp->op_head; NULL != ai; ai = ai->next)
    if (ai->request_id == rid)
      break;
  if (NULL == ai)
  {
    GNUNET_break (0);
    GNUNET_SERVICE_client_drop (idp->client);
    return;
  }
  GNUNET_CONTAINER_DLL_remove (idp->op_head,
                               idp->op_tail,
                               ai);
  GNUNET_free (ai);
  GNUNET_SERVICE_client_continue (idp->client);
}


static void
handle_iteration_next (void *cls,
                       const struct AttributeIterationNextMessage *ais_msg)
{
  struct IdpClient *idp = cls;
  struct AttributeIterator *ai;
  uint32_t rid;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received ATTRIBUTE_ITERATION_NEXT message\n");
  rid = ntohl (ais_msg->id);
  for (ai = idp->op_head; NULL != ai; ai = ai->next)
    if (ai->request_id == rid)
      break;
  if (NULL == ai)
  {
    GNUNET_break (0);
    GNUNET_SERVICE_client_drop (idp->client);
    return;
  }
  bootstrap_abe (&ai->identity,
                 &iterate_next_after_abe_bootstrap,
                 ai,
                 GNUNET_NO);
  GNUNET_SERVICE_client_continue (idp->client);
}

/**
 * Ticket iteration processor result
 */
enum ZoneIterationResult
{
  /**
   * Iteration start.
   */
  IT_START = 0,

  /**
   * Found tickets,
   * Continue to iterate with next iteration_next call
   */
  IT_SUCCESS_MORE_AVAILABLE = 1,

  /**
   * Iteration complete
   */
  IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE = 2
};


/**
 * Context for ticket iteration
 */
struct TicketIterationProcResult
{
  /**
   * The ticket iteration handle
   */
  struct TicketIteration *ti;

  /**
   * Iteration result: iteration done?
   * #IT_SUCCESS_MORE_AVAILABLE:  if there may be more results overall but
   * we got one for now and have sent it to the client
   * #IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE: if there are no further results,
   * #IT_START: if we are still trying to find a result.
   */
  int res_iteration_finished;

};

static void
cleanup_ticket_iter_handle (struct TicketIteration *ti)
{
  GNUNET_free (ti);
}

/**
 * Process ticket from database
 *
 * @param cls struct TicketIterationProcResult
 * @param ticket the ticket
 * @param attrs the attributes
 */
static void
ticket_iterate_proc (void *cls,
                     const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
                     const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs)
{
  struct TicketIterationProcResult *proc = cls;

  if (NULL == ticket)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Iteration done\n");
    proc->res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE;
    return;
  }
  proc->res_iteration_finished = IT_SUCCESS_MORE_AVAILABLE;
  send_ticket_result (proc->ti->client,
                      proc->ti->r_id,
                      ticket,
                      attrs);

}

/**
 * Perform ticket iteration step
 *
 * @param ti ticket iterator to process
 */
static void
run_ticket_iteration_round (struct TicketIteration *ti)
{
  struct TicketIterationProcResult proc;
  struct GNUNET_MQ_Envelope *env;
  struct TicketResultMessage *trm;
  int ret;

  memset (&proc, 0, sizeof (proc));
  proc.ti = ti;
  proc.res_iteration_finished = IT_START;
  while (IT_START == proc.res_iteration_finished)
  {
    if (GNUNET_SYSERR ==
        (ret = TKT_database->iterate_tickets (TKT_database->cls,
                                              &ti->identity,
                                              ti->is_audience,
                                              ti->offset,
                                              &ticket_iterate_proc,
                                              &proc)))
    {
      GNUNET_break (0);
      break;
    }
    if (GNUNET_NO == ret)
      proc.res_iteration_finished = IT_SUCCESS_NOT_MORE_RESULTS_AVAILABLE;
    ti->offset++;
  }
  if (IT_SUCCESS_MORE_AVAILABLE == proc.res_iteration_finished)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "More results available\n");
    return; /* more later */
  }
  /* send empty response to indicate end of list */
  env = GNUNET_MQ_msg (trm,
                       GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_TICKET_RESULT);
  trm->id = htonl (ti->r_id);
  GNUNET_MQ_send (ti->client->mq,
                  env);
  GNUNET_CONTAINER_DLL_remove (ti->client->ticket_iter_head,
                               ti->client->ticket_iter_tail,
                               ti);
  cleanup_ticket_iter_handle (ti);
}

static void
handle_ticket_iteration_start (void *cls,
                               const struct TicketIterationStartMessage *tis_msg)
{
  struct IdpClient *client = cls;
  struct TicketIteration *ti;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received TICKET_ITERATION_START message\n");
  ti = GNUNET_new (struct TicketIteration);
  ti->r_id = ntohl (tis_msg->id);
  ti->offset = 0;
  ti->client = client;
  ti->identity = tis_msg->identity;
  ti->is_audience = ntohl (tis_msg->is_audience);

  GNUNET_CONTAINER_DLL_insert (client->ticket_iter_head,
                               client->ticket_iter_tail,
                               ti);
  run_ticket_iteration_round (ti);
  GNUNET_SERVICE_client_continue (client->client);
}


static void
handle_ticket_iteration_stop (void *cls,
                              const struct TicketIterationStopMessage *tis_msg)
{
  struct IdpClient *client = cls;
  struct TicketIteration *ti;
  uint32_t rid;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received `%s' message\n",
              "TICKET_ITERATION_STOP");
  rid = ntohl (tis_msg->id);
  for (ti = client->ticket_iter_head; NULL != ti; ti = ti->next)
    if (ti->r_id == rid)
      break;
  if (NULL == ti)
  {
    GNUNET_break (0);
    GNUNET_SERVICE_client_drop (client->client);
    return;
  }
  GNUNET_CONTAINER_DLL_remove (client->ticket_iter_head,
                               client->ticket_iter_tail,
                               ti);
  cleanup_ticket_iter_handle (ti);
  GNUNET_SERVICE_client_continue (client->client);
}


static void
handle_ticket_iteration_next (void *cls,
                              const struct TicketIterationNextMessage *tis_msg)
{
  struct IdpClient *client = cls;
  struct TicketIteration *ti;
  uint32_t rid;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received TICKET_ITERATION_NEXT message\n");
  rid = ntohl (tis_msg->id);
  for (ti = client->ticket_iter_head; NULL != ti; ti = ti->next)
    if (ti->r_id == rid)
      break;
  if (NULL == ti)
  {
    GNUNET_break (0);
    GNUNET_SERVICE_client_drop (client->client);
    return;
  }
  run_ticket_iteration_round (ti);
  GNUNET_SERVICE_client_continue (client->client);
}




/**
 * Main function that will be run
 *
 * @param cls closure
 * @param c the configuration used 
 * @param server the service handle
 */
static void
run (void *cls,
     const struct GNUNET_CONFIGURATION_Handle *c,
     struct GNUNET_SERVICE_Handle *server)
{
  char *database;
  cfg = c;

  stats = GNUNET_STATISTICS_create ("identity-provider", cfg);

  //Connect to identity and namestore services
  ns_handle = GNUNET_NAMESTORE_connect (cfg);
  if (NULL == ns_handle)
  {
    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to namestore");
  }

  gns_handle = GNUNET_GNS_connect (cfg);
  if (NULL == gns_handle)
  {
    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns");
  }
  credential_handle = GNUNET_CREDENTIAL_connect (cfg);
  if (NULL == credential_handle)
  {
    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to credential");
  }
  identity_handle = GNUNET_IDENTITY_connect (cfg,
                                             NULL,
                                             NULL);
  /* Loading DB plugin */
  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_string (cfg,
                                             "identity-provider",
                                             "database",
                                             &database))
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "No database backend configured\n");
  GNUNET_asprintf (&db_lib_name,
                   "libgnunet_plugin_identity_provider_%s",
                   database);
  TKT_database = GNUNET_PLUGIN_load (db_lib_name,
                                     (void *) cfg);
  GNUNET_free (database);
  if (NULL == TKT_database)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Could not load database backend `%s'\n",
                db_lib_name);
    GNUNET_SCHEDULER_shutdown ();
    return;
  }

  if (GNUNET_OK ==
      GNUNET_CONFIGURATION_get_value_time (cfg,
                                           "identity-provider",
                                           "TOKEN_EXPIRATION_INTERVAL",
                                           &token_expiration_interval))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Time window for zone iteration: %s\n",
                GNUNET_STRINGS_relative_time_to_string (token_expiration_interval,
                                                        GNUNET_YES));
  } else {
    token_expiration_interval = DEFAULT_TOKEN_EXPIRATION_INTERVAL;
  }

  GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
}

/**
 * Called whenever a client is disconnected.
 *
 * @param cls closure
 * @param client identification of the client
 * @param app_ctx @a client
 */
static void
client_disconnect_cb (void *cls,
                      struct GNUNET_SERVICE_Client *client,
                      void *app_ctx)
{
  struct IdpClient *idp = app_ctx;
  struct AttributeIterator *ai;
  struct TicketIteration *ti;
  struct TicketRevocationHandle *rh;

  //TODO other operations

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Client %p disconnected\n",
              client);

  while (NULL != (ai = idp->op_head))
  {
    GNUNET_CONTAINER_DLL_remove (idp->op_head,
                                 idp->op_tail,
                                 ai);
    GNUNET_free (ai);
  }
  while (NULL != (rh = idp->revocation_list_head))
  {
    GNUNET_CONTAINER_DLL_remove (idp->revocation_list_head,
                                 idp->revocation_list_tail,
                                 rh);
    cleanup_revoke_ticket_handle (rh);
  }
  while (NULL != (ti = idp->ticket_iter_head))
  {
    GNUNET_CONTAINER_DLL_remove (idp->ticket_iter_head,
                                 idp->ticket_iter_tail,
                                 ti);
    cleanup_ticket_iter_handle (ti);
  }
  GNUNET_free (idp);
}


/**
 * Add a client to our list of active clients.
 *
 * @param cls NULL
 * @param client client to add
 * @param mq message queue for @a client
 * @return internal namestore client structure for this client
 */
static void *
client_connect_cb (void *cls,
                   struct GNUNET_SERVICE_Client *client,
                   struct GNUNET_MQ_Handle *mq)
{
  struct IdpClient *idp;
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Client %p connected\n",
              client);
  idp = GNUNET_new (struct IdpClient);
  idp->client = client;
  idp->mq = mq;
  return idp;
}



/**
 * Define "main" method using service macro.
 */
GNUNET_SERVICE_MAIN
("identity-provider",
 GNUNET_SERVICE_OPTION_NONE,
 &run,
 &client_connect_cb,
 &client_disconnect_cb,
 NULL,
 GNUNET_MQ_hd_var_size (attribute_store_message,
                        GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_STORE,
                        struct AttributeStoreMessage,
                        NULL),
 GNUNET_MQ_hd_fixed_size (iteration_start, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_ITERATION_START,
                          struct AttributeIterationStartMessage,
                          NULL),
 GNUNET_MQ_hd_fixed_size (iteration_next, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_ITERATION_NEXT,
                          struct AttributeIterationNextMessage,
                          NULL),
 GNUNET_MQ_hd_fixed_size (iteration_stop, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ATTRIBUTE_ITERATION_STOP,
                          struct AttributeIterationStopMessage,
                          NULL),
 GNUNET_MQ_hd_var_size (issue_ticket_message,
                        GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_TICKET,
                        struct IssueTicketMessage,
                        NULL),
 GNUNET_MQ_hd_var_size (consume_ticket_message,
                        GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_CONSUME_TICKET,
                        struct ConsumeTicketMessage,
                        NULL),
 GNUNET_MQ_hd_fixed_size (ticket_iteration_start, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_TICKET_ITERATION_START,
                          struct TicketIterationStartMessage,
                          NULL),
 GNUNET_MQ_hd_fixed_size (ticket_iteration_next, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_TICKET_ITERATION_NEXT,
                          struct TicketIterationNextMessage,
                          NULL),
 GNUNET_MQ_hd_fixed_size (ticket_iteration_stop, 
                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_TICKET_ITERATION_STOP,
                          struct TicketIterationStopMessage,
                          NULL),
 GNUNET_MQ_hd_var_size (revoke_ticket_message,
                        GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_REVOKE_TICKET,
                        struct RevokeTicketMessage,
                        NULL),
 GNUNET_MQ_handler_end());
/* end of gnunet-service-identity-provider.c */
