/*
   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 Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License,
   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
   Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */
/**
 * @author Martin Schanzenbach
 * @file src/reclaim/gnunet-reclaim.c
 * @brief Identity Provider utility
 *
 */
#include "platform.h"
#include <inttypes.h>

#include "gnunet_util_lib.h"

#include "gnunet_identity_service.h"
#include "gnunet_namestore_service.h"
#include "gnunet_reclaim_service.h"
#include "gnunet_signatures.h"
/**
 * return value
 */
static int ret;

/**
 * List attribute flag
 */
static int list;

/**
 * List credentials flag
 */
static int list_credentials;

/**
 * Credential ID string
 */
static char *credential_id;

/**
 * Credential ID
 */
static struct GNUNET_RECLAIM_Identifier credential;

/**
 * Credential name
 */
static char *credential_name;

/**
 * Credential type
 */
static char *credential_type;

/**
 * Credential exists
 */
static int credential_exists;

/**
 * Relying party
 */
static char *rp;

/**
 * The attribute
 */
static char *attr_name;

/**
 * Attribute value
 */
static char *attr_value;

/**
 * Attributes to issue
 */
static char *issue_attrs;

/**
 * Ticket to consume
 */
static char *consume_ticket;

/**
 * Attribute type
 */
static char *type_str;

/**
 * Ticket to revoke
 */
static char *revoke_ticket;

/**
 * Ticket listing
 */
static int list_tickets;

/**
 * Ego name
 */
static char *ego_name;

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

/**
 * reclaim handle
 */
static struct GNUNET_RECLAIM_Handle *reclaim_handle;

/**
 * reclaim operation
 */
static struct GNUNET_RECLAIM_Operation *reclaim_op;

/**
 * Attribute iterator
 */
static struct GNUNET_RECLAIM_AttributeIterator *attr_iterator;

/**
 * Credential iterator
 */
static struct GNUNET_RECLAIM_CredentialIterator *cred_iterator;


/**
 * Ticket iterator
 */
static struct GNUNET_RECLAIM_TicketIterator *ticket_iterator;


/**
 * ego private key
 */
static const struct GNUNET_IDENTITY_PrivateKey *pkey;

/**
 * rp public key
 */
static struct GNUNET_IDENTITY_PublicKey rp_key;

/**
 * Ticket to consume
 */
static struct GNUNET_RECLAIM_Ticket ticket;

/**
 * Attribute list
 */
static struct GNUNET_RECLAIM_AttributeList *attr_list;

/**
 * Attribute expiration interval
 */
static struct GNUNET_TIME_Relative exp_interval;

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

/**
 * Cleanup task
 */
static struct GNUNET_SCHEDULER_Task *cleanup_task;

/**
 * Claim to store
 */
struct GNUNET_RECLAIM_Attribute *claim;

/**
 * Claim to delete
 */
static char *attr_delete;

/**
 * Claim object to delete
 */
static struct GNUNET_RECLAIM_Attribute *attr_to_delete;

static void
do_cleanup (void *cls)
{
  cleanup_task = NULL;
  if (NULL != timeout)
    GNUNET_SCHEDULER_cancel (timeout);
  if (NULL != reclaim_op)
    GNUNET_RECLAIM_cancel (reclaim_op);
  if (NULL != attr_iterator)
    GNUNET_RECLAIM_get_attributes_stop (attr_iterator);
  if (NULL != cred_iterator)
    GNUNET_RECLAIM_get_credentials_stop (cred_iterator);
  if (NULL != ticket_iterator)
    GNUNET_RECLAIM_ticket_iteration_stop (ticket_iterator);
  if (NULL != reclaim_handle)
    GNUNET_RECLAIM_disconnect (reclaim_handle);
  if (NULL != identity_handle)
    GNUNET_IDENTITY_disconnect (identity_handle);
  if (NULL != attr_list)
    GNUNET_free (attr_list);
  if (NULL != attr_to_delete)
    GNUNET_free (attr_to_delete);
  if (NULL == credential_type)
    GNUNET_free (credential_type);
}


static void
ticket_issue_cb (void *cls,
                 const struct GNUNET_RECLAIM_Ticket *ticket,
                 const struct GNUNET_RECLAIM_PresentationList *presentations)
{
  char *ticket_str;

  reclaim_op = NULL;
  if (NULL != ticket)
  {
    ticket_str =
      GNUNET_STRINGS_data_to_string_alloc (ticket,
                                           sizeof(
                                             struct GNUNET_RECLAIM_Ticket));
    printf ("%s\n", ticket_str);
    GNUNET_free (ticket_str);
  }
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
store_cont (void *cls, int32_t success, const char *emsg)
{
  reclaim_op = NULL;
  if (GNUNET_SYSERR == success)
  {
    fprintf (stderr, "%s\n", emsg);
  }
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
process_attrs (void *cls,
               const struct GNUNET_IDENTITY_PublicKey *identity,
               const struct GNUNET_RECLAIM_Attribute *attr,
               const struct GNUNET_RECLAIM_Presentation *presentation)
{
  char *value_str;
  char *id;
  const char *attr_type;

  if (NULL == identity)
  {
    reclaim_op = NULL;
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
    return;
  }
  if (NULL == attr)
  {
    ret = 1;
    return;
  }
  attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
  id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
  value_str = NULL;
  if (NULL == presentation)
  {
    value_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
                                                          attr->data,
                                                          attr->data_size);
  }
  else
  {
    struct GNUNET_RECLAIM_AttributeListEntry *ale;
    struct GNUNET_RECLAIM_AttributeList *al
      = GNUNET_RECLAIM_presentation_get_attributes (presentation);

    for (ale = al->list_head; NULL != ale; ale = ale->next)
    {
      if (0 != strncmp (attr->data, ale->attribute->name, attr->data_size))
        continue;
      value_str
        = GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type,
                                                    ale->attribute->data,
                                                    ale->attribute->data_size);
      break;
    }
  }
  fprintf (stdout,
           "Name: %s; Value: %s (%s); Flag %u; ID: %s %s\n",
           attr->name,
           (NULL != value_str) ? value_str : "???",
           attr_type,
           attr->flag,
           id,
           (NULL == presentation) ? "" : "(ATTESTED)");
  GNUNET_free (value_str);
  GNUNET_free (id);
}


static void
ticket_iter_err (void *cls)
{
  ticket_iterator = NULL;
  fprintf (stderr, "Failed to iterate over tickets\n");
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
ticket_iter_fin (void *cls)
{
  ticket_iterator = NULL;
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
ticket_iter (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
{
  char *aud;
  char *ref;
  char *tkt;

  aud =
    GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
                                         sizeof(struct
                                                GNUNET_IDENTITY_PublicKey));
  ref = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
  tkt =
    GNUNET_STRINGS_data_to_string_alloc (ticket,
                                         sizeof(struct GNUNET_RECLAIM_Ticket));
  fprintf (stdout, "Ticket: %s | ID: %s | Audience: %s\n", tkt, ref, aud);
  GNUNET_free (aud);
  GNUNET_free (ref);
  GNUNET_free (tkt);
  GNUNET_RECLAIM_ticket_iteration_next (ticket_iterator);
}


static void
iter_error (void *cls)
{
  attr_iterator = NULL;
  cred_iterator = NULL;
  fprintf (stderr, "Failed\n");

  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
timeout_task (void *cls)
{
  timeout = NULL;
  ret = 1;
  fprintf (stderr, "Timeout\n");
  if (NULL == cleanup_task)
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
process_rvk (void *cls, int success, const char *msg)
{
  reclaim_op = NULL;
  if (GNUNET_OK != success)
  {
    fprintf (stderr, "Revocation failed.\n");
    ret = 1;
  }
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
process_delete (void *cls, int success, const char *msg)
{
  reclaim_op = NULL;
  if (GNUNET_OK != success)
  {
    fprintf (stderr, "Deletion failed.\n");
    ret = 1;
  }
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
iter_finished (void *cls)
{
  char *data;
  size_t data_size;
  int type;

  attr_iterator = NULL;
  if (list)
  {
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
    return;
  }

  if (issue_attrs)
  {
    reclaim_op = GNUNET_RECLAIM_ticket_issue (reclaim_handle,
                                              pkey,
                                              &rp_key,
                                              attr_list,
                                              &ticket_issue_cb,
                                              NULL);
    return;
  }
  if (consume_ticket)
  {
    reclaim_op = GNUNET_RECLAIM_ticket_consume (reclaim_handle,
                                                pkey,
                                                &ticket,
                                                &process_attrs,
                                                NULL);
    timeout = GNUNET_SCHEDULER_add_delayed (
      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
      &timeout_task,
      NULL);
    return;
  }
  if (revoke_ticket)
  {
    reclaim_op = GNUNET_RECLAIM_ticket_revoke (reclaim_handle,
                                               pkey,
                                               &ticket,
                                               &process_rvk,
                                               NULL);
    return;
  }
  if (attr_delete)
  {
    if (NULL == attr_to_delete)
    {
      fprintf (stdout, "No such attribute ``%s''\n", attr_delete);
      GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
      return;
    }
    reclaim_op = GNUNET_RECLAIM_attribute_delete (reclaim_handle,
                                                  pkey,
                                                  attr_to_delete,
                                                  &process_delete,
                                                  NULL);
    return;
  }
  if (attr_name)
  {
    if (NULL == type_str)
      type = GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING;
    else
      type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);

    GNUNET_assert (GNUNET_SYSERR !=
                   GNUNET_RECLAIM_attribute_string_to_value (type,
                                                             attr_value,
                                                             (void **) &data,
                                                             &data_size));
    if (NULL != claim)
    {
      claim->type = type;
      claim->data = data;
      claim->data_size = data_size;
    }
    else
    {
      claim =
        GNUNET_RECLAIM_attribute_new (attr_name, NULL, type, data, data_size);
    }
    if (NULL != credential_id)
    {
      claim->credential = credential;
    }
    reclaim_op = GNUNET_RECLAIM_attribute_store (reclaim_handle,
                                                 pkey,
                                                 claim,
                                                 &exp_interval,
                                                 &store_cont,
                                                 NULL);
    GNUNET_free (data);
    GNUNET_free (claim);
    return;
  }
  cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
}


static void
iter_cb (void *cls,
         const struct GNUNET_IDENTITY_PublicKey *identity,
         const struct GNUNET_RECLAIM_Attribute *attr)
{
  struct GNUNET_RECLAIM_AttributeListEntry *le;
  char *attrs_tmp;
  char *attr_str;
  char *label;
  char *id;
  const char *attr_type;

  if ((NULL != attr_name) && (NULL == claim))
  {
    if (0 == strcasecmp (attr_name, attr->name))
    {
      claim = GNUNET_RECLAIM_attribute_new (attr->name,
                                            &attr->credential,
                                            attr->type,
                                            attr->data,
                                            attr->data_size);
      claim->id = attr->id;
    }
  }
  else if (issue_attrs)
  {
    attrs_tmp = GNUNET_strdup (issue_attrs);
    attr_str = strtok (attrs_tmp, ",");
    while (NULL != attr_str)
    {
      if (0 != strcasecmp (attr_str, attr->name))
      {
        attr_str = strtok (NULL, ",");
        continue;
      }
      le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
      le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
                                                    &attr->credential,
                                                    attr->type,
                                                    attr->data,
                                                    attr->data_size);
      le->attribute->flag = attr->flag;
      le->attribute->id = attr->id;
      GNUNET_CONTAINER_DLL_insert (attr_list->list_head,
                                   attr_list->list_tail,
                                   le);
      break;
    }
    GNUNET_free (attrs_tmp);
  }
  else if (attr_delete && (NULL == attr_to_delete))
  {
    label = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
    if (0 == strcasecmp (attr_delete, label))
    {
      attr_to_delete = GNUNET_RECLAIM_attribute_new (attr->name,
                                                     &attr->credential,
                                                     attr->type,
                                                     attr->data,
                                                     attr->data_size);
      attr_to_delete->id = attr->id;
    }
    GNUNET_free (label);
  }
  else if (list)
  {
    attr_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
                                                         attr->data,
                                                         attr->data_size);
    attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
    id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id));
    if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->credential))
    {
      fprintf (stdout,
               "%s: ``%s'' (%s); ID: %s\n",
               attr->name,
               attr_str,
               attr_type,
               id);
    }
    else
    {
      char *cred_id =
        GNUNET_STRINGS_data_to_string_alloc (&attr->credential,
                                             sizeof(attr->credential));
      fprintf (stdout,
               "%s: ``%s'' in credential presentation `%s' (%s); ID: %s\n",
               attr->name,
               attr_str,
               cred_id,
               attr_type,
               id);
      GNUNET_free (cred_id);

    }
    GNUNET_free (id);
  }
  GNUNET_RECLAIM_get_attributes_next (attr_iterator);
}


static void
cred_iter_finished (void *cls)
{
  cred_iterator = NULL;

  // Add new credential
  if ((NULL != credential_name) &&
      (NULL != attr_value))
  {
    enum GNUNET_RECLAIM_CredentialType ctype =
      GNUNET_RECLAIM_credential_typename_to_number (credential_type);
    struct GNUNET_RECLAIM_Credential *credential =
      GNUNET_RECLAIM_credential_new (credential_name,
                                     ctype,
                                     attr_value,
                                     strlen (attr_value));
    reclaim_op = GNUNET_RECLAIM_credential_store (reclaim_handle,
                                                  pkey,
                                                  credential,
                                                  &exp_interval,
                                                  store_cont,
                                                  NULL);
    return;

  }
  if (list_credentials)
  {
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
    return;
  }
  attr_iterator = GNUNET_RECLAIM_get_attributes_start (reclaim_handle,
                                                       pkey,
                                                       &iter_error,
                                                       NULL,
                                                       &iter_cb,
                                                       NULL,
                                                       &iter_finished,
                                                       NULL);

}


static void
cred_iter_cb (void *cls,
              const struct GNUNET_IDENTITY_PublicKey *identity,
              const struct GNUNET_RECLAIM_Credential *cred)
{
  char *cred_str;
  char *attr_str;
  char *id;
  const char *cred_type;
  struct GNUNET_RECLAIM_AttributeListEntry *ale;

  if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&credential,
                                                &cred->id))
    credential_exists = GNUNET_YES;
  if (list_credentials)
  {
    cred_str = GNUNET_RECLAIM_credential_value_to_string (cred->type,
                                                          cred->data,
                                                          cred->data_size);
    cred_type = GNUNET_RECLAIM_credential_number_to_typename (cred->type);
    id = GNUNET_STRINGS_data_to_string_alloc (&cred->id, sizeof(cred->id));
    fprintf (stdout,
             "%s: ``%s'' (%s); ID: %s\n",
             cred->name,
             cred_str,
             cred_type,
             id);
    struct GNUNET_RECLAIM_AttributeList *attrs =
      GNUNET_RECLAIM_credential_get_attributes (cred);
    if (NULL != attrs)
    {
      fprintf (stdout,
               "\t Attributes:\n");
      for (ale = attrs->list_head; NULL != ale; ale = ale->next)
      {
        attr_str = GNUNET_RECLAIM_attribute_value_to_string (
          ale->attribute->type,
          ale->attribute->data,
          ale->attribute->data_size);
        fprintf (stdout,
                 "\t %s: %s\n", ale->attribute->name, attr_str);
        GNUNET_free (attr_str);
      }
      GNUNET_RECLAIM_attribute_list_destroy (attrs);
    }
    GNUNET_free (id);
  }
  GNUNET_RECLAIM_get_credentials_next (cred_iterator);
}


static void
start_process ()
{
  if (NULL == pkey)
  {
    fprintf (stderr, "Ego %s not found\n", ego_name);
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
    return;
  }
  if (NULL == credential_type)
    credential_type = GNUNET_strdup ("JWT");
  credential = GNUNET_RECLAIM_ID_ZERO;
  if (NULL != credential_id)
    GNUNET_STRINGS_string_to_data (credential_id,
                                   strlen (credential_id),
                                   &credential, sizeof(credential));
  credential_exists = GNUNET_NO;
  if (list_tickets)
  {
    ticket_iterator = GNUNET_RECLAIM_ticket_iteration_start (reclaim_handle,
                                                             pkey,
                                                             &ticket_iter_err,
                                                             NULL,
                                                             &ticket_iter,
                                                             NULL,
                                                             &ticket_iter_fin,
                                                             NULL);
    return;
  }

  if ((NULL != rp) &&
      (GNUNET_OK !=
       GNUNET_IDENTITY_public_key_from_string (rp, &rp_key)) )
  {
    fprintf (stderr, "%s is not a public key!\n", rp);
    cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL);
    return;
  }
  if (NULL != consume_ticket)
    GNUNET_STRINGS_string_to_data (consume_ticket,
                                   strlen (consume_ticket),
                                   &ticket,
                                   sizeof(struct GNUNET_RECLAIM_Ticket));
  if (NULL != revoke_ticket)
    GNUNET_STRINGS_string_to_data (revoke_ticket,
                                   strlen (revoke_ticket),
                                   &ticket,
                                   sizeof(struct GNUNET_RECLAIM_Ticket));

  attr_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
  claim = NULL;
  cred_iterator = GNUNET_RECLAIM_get_credentials_start (reclaim_handle,
                                                        pkey,
                                                        &iter_error,
                                                        NULL,
                                                        &cred_iter_cb,
                                                        NULL,
                                                        &cred_iter_finished,
                                                        NULL);

}


static int init = GNUNET_YES;

static void
ego_cb (void *cls,
        struct GNUNET_IDENTITY_Ego *ego,
        void **ctx,
        const char *name)
{
  if (NULL == name)
  {
    if (GNUNET_YES == init)
    {
      init = GNUNET_NO;
      start_process ();
    }
    return;
  }
  if (0 != strcmp (name, ego_name))
    return;
  pkey = GNUNET_IDENTITY_ego_get_private_key (ego);
}


static void
run (void *cls,
     char *const *args,
     const char *cfgfile,
     const struct GNUNET_CONFIGURATION_Handle *c)
{
  ret = 0;
  if (NULL == ego_name)
  {
    ret = 1;
    fprintf (stderr, _ ("Ego is required\n"));
    return;
  }

  if ((NULL == attr_value) && (NULL != attr_name))
  {
    ret = 1;
    fprintf (stderr, _ ("Attribute value missing!\n"));
    return;
  }

  if ((NULL == rp) && (NULL != issue_attrs))
  {
    ret = 1;
    fprintf (stderr, _ ("Requesting party key is required!\n"));
    return;
  }

  reclaim_handle = GNUNET_RECLAIM_connect (c);
  // Get Ego
  identity_handle = GNUNET_IDENTITY_connect (c, &ego_cb, NULL);
}


int
main (int argc, char *const argv[])
{
  exp_interval = GNUNET_TIME_UNIT_HOURS;
  struct GNUNET_GETOPT_CommandLineOption options[] = {
    GNUNET_GETOPT_option_string ('a',
                                 "add",
                                 "NAME",
                                 gettext_noop ("Add or update an attribute NAME"),
                                 &attr_name),
    GNUNET_GETOPT_option_string ('d',
                                 "delete",
                                 "ID",
                                 gettext_noop ("Delete the attribute with ID"),
                                 &attr_delete),
    GNUNET_GETOPT_option_string ('V',
                                 "value",
                                 "VALUE",
                                 gettext_noop ("The attribute VALUE"),
                                 &attr_value),
    GNUNET_GETOPT_option_string ('e',
                                 "ego",
                                 "EGO",
                                 gettext_noop ("The EGO to use"),
                                 &ego_name),
    GNUNET_GETOPT_option_string ('r',
                                 "rp",
                                 "RP",
                                 gettext_noop (
                                   "Specify the relying party for issue"),
                                 &rp),
    GNUNET_GETOPT_option_flag ('D',
                               "dump",
                               gettext_noop ("List attributes for EGO"),
                               &list),
    GNUNET_GETOPT_option_flag ('A',
                               "credentials",
                               gettext_noop ("List credentials for EGO"),
                               &list_credentials),
    GNUNET_GETOPT_option_string ('I',
                                 "credential-id",
                                 "CREDENTIAL_ID",
                                 gettext_noop (
                                   "Credential to use for attribute"),
                                 &credential_id),
    GNUNET_GETOPT_option_string ('N',
                                 "credential-name",
                                 "NAME",
                                 gettext_noop ("Credential name"),
                                 &credential_name),
    GNUNET_GETOPT_option_string ('i',
                                 "issue",
                                 "A1,A2,...",
                                 gettext_noop (
                                   "Issue a ticket for a set of attributes separated by comma"),
                                 &issue_attrs),
    GNUNET_GETOPT_option_string ('C',
                                 "consume",
                                 "TICKET",
                                 gettext_noop ("Consume a ticket"),
                                 &consume_ticket),
    GNUNET_GETOPT_option_string ('R',
                                 "revoke",
                                 "TICKET",
                                 gettext_noop ("Revoke a ticket"),
                                 &revoke_ticket),
    GNUNET_GETOPT_option_string ('t',
                                 "type",
                                 "TYPE",
                                 gettext_noop ("Type of attribute"),
                                 &type_str),
    GNUNET_GETOPT_option_string ('u',
                                 "credential-type",
                                 "TYPE",
                                 gettext_noop ("Type of credential"),
                                 &credential_type),
    GNUNET_GETOPT_option_flag ('T',
                               "tickets",
                               gettext_noop ("List tickets of ego"),
                               &list_tickets),
    GNUNET_GETOPT_option_relative_time ('E',
                                        "expiration",
                                        "INTERVAL",
                                        gettext_noop (
                                          "Expiration interval of the attribute"),
                                        &exp_interval),

    GNUNET_GETOPT_OPTION_END
  };
  if (GNUNET_OK != GNUNET_PROGRAM_run (argc,
                                       argv,
                                       "gnunet-reclaim",
                                       _ ("re:claimID command line tool"),
                                       options,
                                       &run,
                                       NULL))
    return 1;
  else
    return ret;
}
