Blame src/verify.c

Packit d7e8d0
/* verify.c - Signature verification.
Packit Service 30b792
 * Copyright (C) 2000 Werner Koch (dd9jn)
Packit Service 30b792
 * Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1-or-later
Packit Service 30b792
 */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
#include <limits.h>
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "context.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
typedef struct
Packit d7e8d0
{
Packit d7e8d0
  struct _gpgme_op_verify_result result;
Packit d7e8d0
Packit d7e8d0
  /* The error code from a FAILURE status line or 0.  */
Packit d7e8d0
  gpg_error_t failure_code;
Packit d7e8d0
Packit d7e8d0
  gpgme_signature_t current_sig;
Packit d7e8d0
  int did_prepare_new_sig;
Packit d7e8d0
  int only_newsig_seen;
Packit d7e8d0
  int plaintext_seen;
Packit d7e8d0
  int conflict_user_seen;
Packit d7e8d0
} *op_data_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
release_op_data (void *hook)
Packit d7e8d0
{
Packit d7e8d0
  op_data_t opd = (op_data_t) hook;
Packit d7e8d0
  gpgme_signature_t sig = opd->result.signatures;
Packit d7e8d0
Packit d7e8d0
  while (sig)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_signature_t next = sig->next;
Packit d7e8d0
      gpgme_sig_notation_t notation = sig->notations;
Packit d7e8d0
Packit d7e8d0
      while (notation)
Packit d7e8d0
	{
Packit d7e8d0
	  gpgme_sig_notation_t next_nota = notation->next;
Packit d7e8d0
Packit d7e8d0
	  _gpgme_sig_notation_free (notation);
Packit d7e8d0
	  notation = next_nota;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (sig->fpr)
Packit d7e8d0
	free (sig->fpr);
Packit d7e8d0
      if (sig->pka_address)
Packit d7e8d0
	free (sig->pka_address);
Packit d7e8d0
      if (sig->key)
Packit d7e8d0
        gpgme_key_unref (sig->key);
Packit d7e8d0
      free (sig);
Packit d7e8d0
      sig = next;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (opd->result.file_name)
Packit d7e8d0
    free (opd->result.file_name);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_verify_result_t
Packit d7e8d0
gpgme_op_verify_result (gpgme_ctx_t ctx)
Packit d7e8d0
{
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx, "");
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err || !opd)
Packit d7e8d0
    {
Packit Service 30b792
      TRACE_SUC ("result=(null)");
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* It is possible that we saw a new signature only followed by an
Packit d7e8d0
     ERROR line for that.  In particular a missing X.509 key triggers
Packit d7e8d0
     this.  In this case it is surprising that the summary field has
Packit d7e8d0
     not been updated.  We fix it here by explicitly looking for this
Packit d7e8d0
     case.  The real fix would be to have GPGME emit ERRSIG.  */
Packit d7e8d0
  for (sig = opd->result.signatures; sig; sig = sig->next)
Packit d7e8d0
    {
Packit d7e8d0
      if (!sig->summary)
Packit d7e8d0
        {
Packit d7e8d0
          switch (gpg_err_code (sig->status))
Packit d7e8d0
            {
Packit d7e8d0
            case GPG_ERR_KEY_EXPIRED:
Packit d7e8d0
              sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
Packit d7e8d0
              break;
Packit d7e8d0
Packit d7e8d0
            case GPG_ERR_NO_PUBKEY:
Packit d7e8d0
              sig->summary |= GPGME_SIGSUM_KEY_MISSING;
Packit d7e8d0
              break;
Packit d7e8d0
Packit d7e8d0
            default:
Packit d7e8d0
              break;
Packit d7e8d0
            }
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Now for some tracing stuff. */
Packit d7e8d0
  if (_gpgme_debug_trace ())
Packit d7e8d0
    {
Packit d7e8d0
      int i;
Packit d7e8d0
Packit d7e8d0
      for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
Packit d7e8d0
	{
Packit Service 30b792
	  TRACE_LOG  ("sig[%i] = fpr %s, summary 0x%x, status %s",
Packit d7e8d0
		      i, sig->fpr, sig->summary, gpg_strerror (sig->status));
Packit Service 30b792
	  TRACE_LOG  ("sig[%i] = timestamps 0x%lx/0x%lx flags:%s%s%s",
Packit d7e8d0
		      i, sig->timestamp, sig->exp_timestamp,
Packit d7e8d0
		      sig->wrong_key_usage ? "wrong key usage" : "",
Packit d7e8d0
		      sig->pka_trust == 1 ? "pka bad"
Packit d7e8d0
		      : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
Packit d7e8d0
		      sig->chain_model ? "chain model" : "");
Packit Service 30b792
	  TRACE_LOG  ("sig[%i] = validity 0x%x (%s), algos %s/%s",
Packit d7e8d0
		      i, sig->validity, gpg_strerror (sig->validity_reason),
Packit d7e8d0
		      gpgme_pubkey_algo_name (sig->pubkey_algo),
Packit d7e8d0
		      gpgme_hash_algo_name (sig->hash_algo));
Packit d7e8d0
	  if (sig->pka_address)
Packit d7e8d0
	    {
Packit Service 30b792
	      TRACE_LOG  ("sig[%i] = PKA address %s", i, sig->pka_address);
Packit d7e8d0
	    }
Packit d7e8d0
	  if (sig->notations)
Packit d7e8d0
	    {
Packit Service 30b792
	      TRACE_LOG  ("sig[%i] = has notations (not shown)", i);
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  TRACE_SUC ("result=%p", &opd->result);
Packit d7e8d0
  return &opd->result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Build a summary vector from RESULT. */
Packit d7e8d0
static void
Packit d7e8d0
calc_sig_summary (gpgme_signature_t sig)
Packit d7e8d0
{
Packit d7e8d0
  unsigned long sum = 0;
Packit d7e8d0
Packit d7e8d0
  /* Calculate the red/green flag.  */
Packit d7e8d0
  if (sig->validity == GPGME_VALIDITY_FULL
Packit d7e8d0
      || sig->validity == GPGME_VALIDITY_ULTIMATE)
Packit d7e8d0
    {
Packit d7e8d0
      if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
Packit d7e8d0
	  || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
Packit d7e8d0
	  || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
Packit d7e8d0
	sum |= GPGME_SIGSUM_GREEN;
Packit d7e8d0
    }
Packit d7e8d0
  else if (sig->validity == GPGME_VALIDITY_NEVER)
Packit d7e8d0
    {
Packit d7e8d0
      if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
Packit d7e8d0
	  || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
Packit d7e8d0
	  || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
Packit d7e8d0
	sum |= GPGME_SIGSUM_RED;
Packit d7e8d0
    }
Packit d7e8d0
  else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
Packit d7e8d0
    sum |= GPGME_SIGSUM_RED;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  /* FIXME: handle the case when key and message are expired. */
Packit d7e8d0
  switch (gpg_err_code (sig->status))
Packit d7e8d0
    {
Packit d7e8d0
    case GPG_ERR_SIG_EXPIRED:
Packit d7e8d0
      sum |= GPGME_SIGSUM_SIG_EXPIRED;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPG_ERR_KEY_EXPIRED:
Packit d7e8d0
      sum |= GPGME_SIGSUM_KEY_EXPIRED;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPG_ERR_NO_PUBKEY:
Packit d7e8d0
      sum |= GPGME_SIGSUM_KEY_MISSING;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPG_ERR_CERT_REVOKED:
Packit d7e8d0
      sum |= GPGME_SIGSUM_KEY_REVOKED;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPG_ERR_BAD_SIGNATURE:
Packit d7e8d0
    case GPG_ERR_NO_ERROR:
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      sum |= GPGME_SIGSUM_SYS_ERROR;
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Now look at the certain reason codes.  */
Packit d7e8d0
  switch (gpg_err_code (sig->validity_reason))
Packit d7e8d0
    {
Packit d7e8d0
    case GPG_ERR_CRL_TOO_OLD:
Packit d7e8d0
      if (sig->validity == GPGME_VALIDITY_UNKNOWN)
Packit d7e8d0
        sum |= GPGME_SIGSUM_CRL_TOO_OLD;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPG_ERR_CERT_REVOKED:
Packit d7e8d0
      /* Note that this is a second way to set this flag.  It may also
Packit d7e8d0
         have been set due to a sig->status of STATUS_REVKEYSIG from
Packit d7e8d0
         parse_new_sig.  */
Packit d7e8d0
      sum |= GPGME_SIGSUM_KEY_REVOKED;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Check other flags. */
Packit d7e8d0
  if (sig->wrong_key_usage)
Packit d7e8d0
    sum |= GPGME_SIGSUM_BAD_POLICY;
Packit d7e8d0
Packit d7e8d0
  /* Set the valid flag when the signature is unquestionable
Packit d7e8d0
     valid.  (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */
Packit d7e8d0
  if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
Packit d7e8d0
    sum |= GPGME_SIGSUM_VALID;
Packit d7e8d0
Packit d7e8d0
  sig->summary = sum;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
prepare_new_sig (op_data_t opd)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit d7e8d0
  if (opd->only_newsig_seen && opd->current_sig)
Packit d7e8d0
    {
Packit d7e8d0
      /* We have only seen the NEWSIG status and nothing else - we
Packit d7e8d0
         better skip this signature therefore and reuse it for the
Packit d7e8d0
         next possible signature. */
Packit d7e8d0
      sig = opd->current_sig;
Packit d7e8d0
      memset (sig, 0, sizeof *sig);
Packit d7e8d0
      assert (opd->result.signatures == sig);
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      sig = calloc (1, sizeof (*sig));
Packit d7e8d0
      if (!sig)
Packit d7e8d0
        return gpg_error_from_syserror ();
Packit d7e8d0
      if (!opd->result.signatures)
Packit d7e8d0
        opd->result.signatures = sig;
Packit d7e8d0
      if (opd->current_sig)
Packit d7e8d0
        opd->current_sig->next = sig;
Packit d7e8d0
      opd->current_sig = sig;
Packit d7e8d0
    }
Packit d7e8d0
  opd->did_prepare_new_sig = 1;
Packit d7e8d0
  opd->only_newsig_seen = 0;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args,
Packit d7e8d0
               gpgme_protocol_t protocol)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
  char *end = strchr (args, ' ');
Packit d7e8d0
  char *tail;
Packit Service 30b792
  int got_fpr = 0;
Packit d7e8d0
Packit d7e8d0
  if (end)
Packit d7e8d0
    {
Packit d7e8d0
      *end = '\0';
Packit d7e8d0
      end++;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (!opd->did_prepare_new_sig)
Packit d7e8d0
    {
Packit d7e8d0
      gpg_error_t err;
Packit d7e8d0
Packit d7e8d0
      err = prepare_new_sig (opd);
Packit d7e8d0
      if (err)
Packit d7e8d0
        return err;
Packit d7e8d0
    }
Packit d7e8d0
  assert (opd->did_prepare_new_sig);
Packit d7e8d0
  opd->did_prepare_new_sig = 0;
Packit d7e8d0
Packit d7e8d0
  assert (opd->current_sig);
Packit d7e8d0
  sig = opd->current_sig;
Packit d7e8d0
Packit d7e8d0
  /* FIXME: We should set the source of the state.  */
Packit d7e8d0
  switch (code)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_STATUS_GOODSIG:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_NO_ERROR);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_EXPSIG:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_EXPKEYSIG:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_BADSIG:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_REVKEYSIG:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_ERRSIG:
Packit d7e8d0
      /* Parse the pubkey algo.  */
Packit d7e8d0
      if (!end)
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
      gpg_err_set_errno (0);
Packit d7e8d0
      sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0), protocol);
Packit d7e8d0
      if (errno || end == tail || *tail != ' ')
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
      end = tail;
Packit d7e8d0
      while (*end == ' ')
Packit d7e8d0
	end++;
Packit d7e8d0
Packit d7e8d0
      /* Parse the hash algo.  */
Packit d7e8d0
      if (!*end)
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
      gpg_err_set_errno (0);
Packit d7e8d0
      sig->hash_algo = strtol (end, &tail, 0);
Packit d7e8d0
      if (errno || end == tail || *tail != ' ')
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
      end = tail;
Packit d7e8d0
      while (*end == ' ')
Packit d7e8d0
	end++;
Packit d7e8d0
Packit d7e8d0
      /* Skip the sig class.  */
Packit d7e8d0
      end = strchr (end, ' ');
Packit d7e8d0
      if (!end)
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
      while (*end == ' ')
Packit d7e8d0
	end++;
Packit d7e8d0
Packit d7e8d0
      /* Parse the timestamp.  */
Packit d7e8d0
      sig->timestamp = _gpgme_parse_timestamp (end, &tail);
Packit d7e8d0
      if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
Packit d7e8d0
	return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      end = tail;
Packit d7e8d0
      while (*end == ' ')
Packit d7e8d0
	end++;
Packit d7e8d0
Packit d7e8d0
      /* Parse the return code.  */
Packit d7e8d0
      if (!*end)
Packit d7e8d0
	goto parse_err_sig_fail;
Packit d7e8d0
Packit Service 30b792
      gpg_err_set_errno (0);
Packit Service 30b792
      sig->status = strtoul (end, &tail, 10);
Packit Service 30b792
      if (errno || end == tail || (*tail && *tail != ' '))
Packit Service 30b792
	goto parse_err_sig_fail;
Packit Service 30b792
      if (!*tail)
Packit Service 30b792
        goto parse_err_sig_ok;
Packit Service 30b792
      end = tail;
Packit Service 30b792
      while (*end == ' ')
Packit Service 30b792
	end++;
Packit Service 30b792
Packit Service 30b792
      /* Parse the new fingerprint (from the ISSUER_FPR subpacket).  */
Packit Service 30b792
      if (!*end || (*end == '-' && (end[1] == ' ' || !end[1])))
Packit Service 30b792
        goto parse_err_sig_ok;  /* Okay (just trailing spaces).  */
Packit Service 30b792
      sig->fpr = strdup (end);
Packit Service 30b792
      if (!sig->fpr)
Packit Service 30b792
	return gpg_error_from_syserror ();
Packit Service 30b792
      got_fpr = 1;
Packit d7e8d0
      goto parse_err_sig_ok;
Packit d7e8d0
Packit d7e8d0
    parse_err_sig_fail:
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
    parse_err_sig_ok:
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      return gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
    }
Packit d7e8d0
Packit Service 30b792
  if (*args && !got_fpr)
Packit d7e8d0
    {
Packit d7e8d0
      sig->fpr = strdup (args);
Packit d7e8d0
      if (!sig->fpr)
Packit d7e8d0
	return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_valid_sig (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol)
Packit d7e8d0
{
Packit d7e8d0
  char *end = strchr (args, ' ');
Packit d7e8d0
  if (end)
Packit d7e8d0
    {
Packit d7e8d0
      *end = '\0';
Packit d7e8d0
      end++;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (!*args)
Packit d7e8d0
    /* We require at least the fingerprint.  */
Packit d7e8d0
    return gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
Packit d7e8d0
  if (sig->fpr)
Packit d7e8d0
    free (sig->fpr);
Packit d7e8d0
  sig->fpr = strdup (args);
Packit d7e8d0
  if (!sig->fpr)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  /* Skip the creation date.  */
Packit d7e8d0
  end = strchr (end, ' ');
Packit d7e8d0
  if (end)
Packit d7e8d0
    {
Packit d7e8d0
      char *tail;
Packit d7e8d0
Packit d7e8d0
      sig->timestamp = _gpgme_parse_timestamp (end, &tail);
Packit d7e8d0
      if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
Packit d7e8d0
	return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      end = tail;
Packit d7e8d0
Packit d7e8d0
      sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
Packit d7e8d0
      if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
Packit d7e8d0
	return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      end = tail;
Packit d7e8d0
Packit d7e8d0
      while (*end == ' ')
Packit d7e8d0
	end++;
Packit d7e8d0
      /* Skip the signature version.  */
Packit d7e8d0
      end = strchr (end, ' ');
Packit d7e8d0
      if (end)
Packit d7e8d0
	{
Packit d7e8d0
	  while (*end == ' ')
Packit d7e8d0
	    end++;
Packit d7e8d0
Packit d7e8d0
	  /* Skip the reserved field.  */
Packit d7e8d0
	  end = strchr (end, ' ');
Packit d7e8d0
	  if (end)
Packit d7e8d0
	    {
Packit d7e8d0
	      /* Parse the pubkey algo.  */
Packit d7e8d0
	      gpg_err_set_errno (0);
Packit d7e8d0
	      sig->pubkey_algo = _gpgme_map_pk_algo (strtol (end, &tail, 0),
Packit d7e8d0
                                                     protocol);
Packit d7e8d0
	      if (errno || end == tail || *tail != ' ')
Packit d7e8d0
		return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
	      end = tail;
Packit d7e8d0
Packit d7e8d0
	      while (*end == ' ')
Packit d7e8d0
		end++;
Packit d7e8d0
Packit d7e8d0
	      if (*end)
Packit d7e8d0
		{
Packit d7e8d0
		  /* Parse the hash algo.  */
Packit d7e8d0
Packit d7e8d0
		  gpg_err_set_errno (0);
Packit d7e8d0
		  sig->hash_algo = strtol (end, &tail, 0);
Packit d7e8d0
		  if (errno || end == tail || *tail != ' ')
Packit d7e8d0
		    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
		  end = tail;
Packit d7e8d0
		}
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_sig_notation_t *lastp = &sig->notations;
Packit d7e8d0
  gpgme_sig_notation_t notation = sig->notations;
Packit d7e8d0
  char *p;
Packit d7e8d0
Packit d7e8d0
  if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
Packit d7e8d0
    {
Packit d7e8d0
      p = strchr (args, ' ');
Packit d7e8d0
      if (p)
Packit d7e8d0
        *p = '\0';
Packit d7e8d0
Packit d7e8d0
      /* FIXME: We could keep a pointer to the last notation in the list.  */
Packit d7e8d0
      while (notation && notation->value)
Packit d7e8d0
	{
Packit d7e8d0
	  lastp = &notation->next;
Packit d7e8d0
	  notation = notation->next;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (notation)
Packit d7e8d0
	/* There is another notation name without data for the
Packit d7e8d0
	   previous one.  The crypto backend misbehaves.  */
Packit d7e8d0
	return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
      err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
Packit d7e8d0
      if (code == GPGME_STATUS_NOTATION_NAME)
Packit d7e8d0
	{
Packit d7e8d0
	  err = _gpgme_decode_percent_string (args, &notation->name, 0, 0);
Packit d7e8d0
	  if (err)
Packit d7e8d0
	    {
Packit d7e8d0
	      _gpgme_sig_notation_free (notation);
Packit d7e8d0
	      return err;
Packit d7e8d0
	    }
Packit d7e8d0
Packit d7e8d0
	  notation->name_len = strlen (notation->name);
Packit d7e8d0
Packit d7e8d0
	  /* Set default flags for use with older gpg versions which
Packit d7e8d0
           * do not emit a NOTATIONS_FLAG line.  */
Packit d7e8d0
	  notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
Packit d7e8d0
	  notation->human_readable = 1;
Packit d7e8d0
	}
Packit d7e8d0
      else
Packit d7e8d0
	{
Packit d7e8d0
	  /* This is a policy URL.  */
Packit d7e8d0
Packit d7e8d0
	  err = _gpgme_decode_percent_string (args, &notation->value, 0, 0);
Packit d7e8d0
	  if (err)
Packit d7e8d0
	    {
Packit d7e8d0
	      _gpgme_sig_notation_free (notation);
Packit d7e8d0
	      return err;
Packit d7e8d0
	    }
Packit d7e8d0
Packit d7e8d0
	  notation->value_len = strlen (notation->value);
Packit d7e8d0
	}
Packit d7e8d0
      *lastp = notation;
Packit d7e8d0
    }
Packit d7e8d0
  else if (code == GPGME_STATUS_NOTATION_FLAGS)
Packit d7e8d0
    {
Packit d7e8d0
      char *field[2];
Packit d7e8d0
Packit d7e8d0
      while (notation && notation->next)
Packit d7e8d0
	{
Packit d7e8d0
	  lastp = &notation->next;
Packit d7e8d0
	  notation = notation->next;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (!notation || !notation->name)
Packit d7e8d0
        { /* There are notation flags without a previous notation name.
Packit d7e8d0
           * The crypto backend misbehaves.  */
Packit d7e8d0
          return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
        }
Packit d7e8d0
      if (_gpgme_split_fields (args, field, DIM (field)) < 2)
Packit d7e8d0
        { /* Required args missing.  */
Packit d7e8d0
          return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
        }
Packit d7e8d0
      notation->flags = 0;
Packit d7e8d0
      if (atoi (field[0]))
Packit d7e8d0
        {
Packit d7e8d0
          notation->flags |= GPGME_SIG_NOTATION_CRITICAL;
Packit d7e8d0
          notation->critical = 1;
Packit d7e8d0
        }
Packit d7e8d0
      if (atoi (field[1]))
Packit d7e8d0
        {
Packit d7e8d0
          notation->flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
Packit d7e8d0
          notation->human_readable = 1;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  else if (code == GPGME_STATUS_NOTATION_DATA)
Packit d7e8d0
    {
Packit d7e8d0
      int len = strlen (args) + 1;
Packit d7e8d0
      char *dest;
Packit d7e8d0
Packit d7e8d0
      /* FIXME: We could keep a pointer to the last notation in the list.  */
Packit d7e8d0
      while (notation && notation->next)
Packit d7e8d0
	{
Packit d7e8d0
	  lastp = &notation->next;
Packit d7e8d0
	  notation = notation->next;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      if (!notation || !notation->name)
Packit d7e8d0
	/* There is notation data without a previous notation
Packit d7e8d0
	   name.  The crypto backend misbehaves.  */
Packit d7e8d0
	return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
      if (!notation->value)
Packit d7e8d0
	{
Packit d7e8d0
	  dest = notation->value = malloc (len);
Packit d7e8d0
	  if (!dest)
Packit d7e8d0
	    return gpg_error_from_syserror ();
Packit d7e8d0
	}
Packit d7e8d0
      else
Packit d7e8d0
	{
Packit d7e8d0
	  int cur_len = strlen (notation->value);
Packit d7e8d0
	  dest = realloc (notation->value, len + strlen (notation->value));
Packit d7e8d0
	  if (!dest)
Packit d7e8d0
	    return gpg_error_from_syserror ();
Packit d7e8d0
	  notation->value = dest;
Packit d7e8d0
	  dest += cur_len;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
      err = _gpgme_decode_percent_string (args, &dest, len, 0);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
Packit d7e8d0
      notation->value_len += strlen (dest);
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  char *end = strchr (args, ' ');
Packit d7e8d0
Packit d7e8d0
  if (end)
Packit d7e8d0
    *end = '\0';
Packit d7e8d0
Packit d7e8d0
  switch (code)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_STATUS_TRUST_UNDEFINED:
Packit d7e8d0
    default:
Packit d7e8d0
      sig->validity = GPGME_VALIDITY_UNKNOWN;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TRUST_NEVER:
Packit d7e8d0
      sig->validity = GPGME_VALIDITY_NEVER;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TRUST_MARGINAL:
Packit d7e8d0
      sig->validity = GPGME_VALIDITY_MARGINAL;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TRUST_FULLY:
Packit d7e8d0
    case GPGME_STATUS_TRUST_ULTIMATE:
Packit d7e8d0
      sig->validity = GPGME_VALIDITY_FULL;
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  sig->validity_reason = 0;
Packit d7e8d0
  sig->chain_model = 0;
Packit d7e8d0
  if (*args)
Packit d7e8d0
    {
Packit d7e8d0
      sig->validity_reason = atoi (args);
Packit d7e8d0
      while (*args && *args != ' ')
Packit d7e8d0
        args++;
Packit d7e8d0
      if (*args)
Packit d7e8d0
        {
Packit d7e8d0
          while (*args == ' ')
Packit d7e8d0
            args++;
Packit d7e8d0
          if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2]))
Packit d7e8d0
            sig->chain_model = 1;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse a TOFU_USER line and put the info into SIG.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_tofu_user (gpgme_signature_t sig, char *args, gpgme_protocol_t protocol)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit d7e8d0
  char *tail;
Packit d7e8d0
  gpgme_user_id_t uid;
Packit d7e8d0
  gpgme_tofu_info_t ti;
Packit d7e8d0
  char *fpr = NULL;
Packit d7e8d0
  char *address = NULL;
Packit d7e8d0
Packit d7e8d0
  tail = strchr (args, ' ');
Packit d7e8d0
  if (!tail || tail == args)
Packit d7e8d0
    {
Packit d7e8d0
      err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No fingerprint.  */
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
  *tail++ = 0;
Packit d7e8d0
Packit d7e8d0
  fpr = strdup (args);
Packit d7e8d0
  if (!fpr)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (sig->key && sig->key->fpr && strcmp (sig->key->fpr, fpr))
Packit d7e8d0
    {
Packit d7e8d0
      /* GnuPG since 2.1.17 emits multiple TOFU_USER lines with
Packit d7e8d0
         different fingerprints in case of conflicts for a signature. */
Packit d7e8d0
      err = gpg_error (GPG_ERR_DUP_VALUE);
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  args = tail;
Packit d7e8d0
  tail = strchr (args, ' ');
Packit d7e8d0
  if (tail == args)
Packit d7e8d0
    {
Packit d7e8d0
      err = trace_gpg_error (GPG_ERR_INV_ENGINE);  /* No addr-spec.  */
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
  if (tail)
Packit d7e8d0
    *tail = 0;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_decode_percent_string (args, &address, 0, 0);
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto leave;
Packit d7e8d0
Packit d7e8d0
  if (!sig->key)
Packit d7e8d0
    {
Packit d7e8d0
      err = _gpgme_key_new (&sig->key);
Packit d7e8d0
      if (err)
Packit d7e8d0
        goto leave;
Packit d7e8d0
      sig->key->fpr = fpr;
Packit d7e8d0
      sig->key->protocol = protocol;
Packit d7e8d0
      fpr = NULL;
Packit d7e8d0
    }
Packit d7e8d0
  else if (!sig->key->fpr)
Packit d7e8d0
    {
Packit d7e8d0
      err = trace_gpg_error (GPG_ERR_INTERNAL);
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_key_append_name (sig->key, address, 0);
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto leave;
Packit d7e8d0
Packit d7e8d0
  uid = sig->key->_last_uid;
Packit d7e8d0
  assert (uid);
Packit d7e8d0
Packit d7e8d0
  ti = calloc (1, sizeof *ti);
Packit d7e8d0
  if (!ti)
Packit d7e8d0
    {
Packit d7e8d0
      err = gpg_error_from_syserror ();
Packit d7e8d0
      goto leave;
Packit d7e8d0
    }
Packit d7e8d0
  uid->tofu = ti;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
 leave:
Packit d7e8d0
  free (fpr);
Packit d7e8d0
  free (address);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse a TOFU_STATS line and store it in the last tofu info of SIG.
Packit d7e8d0
 *
Packit d7e8d0
 *   TOFU_STATS <validity> <sign-count> <encr-count> \
Packit d7e8d0
 *                         [<policy> [<tm1> <tm2> <tm3> <tm4>]]
Packit d7e8d0
 */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_tofu_stats (gpgme_signature_t sig, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_tofu_info_t ti;
Packit d7e8d0
  char *field[8];
Packit d7e8d0
  int nfields;
Packit d7e8d0
  unsigned long uval;
Packit d7e8d0
Packit d7e8d0
  if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
Packit d7e8d0
  if (ti->signfirst || ti->signcount || ti->validity || ti->policy)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
Packit d7e8d0
Packit d7e8d0
  nfields = _gpgme_split_fields (args, field, DIM (field));
Packit d7e8d0
  if (nfields < 3)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required args missing.  */
Packit d7e8d0
Packit d7e8d0
  /* Note that we allow a value of up to 7 which is what we can store
Packit d7e8d0
   * in the ti->validity.  */
Packit d7e8d0
  err = _gpgme_strtoul_field (field[0], &uval);
Packit d7e8d0
  if (err || uval > 7)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  ti->validity = uval;
Packit d7e8d0
Packit d7e8d0
  /* Parse the sign-count.  */
Packit d7e8d0
  err = _gpgme_strtoul_field (field[1], &uval);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  if (uval > USHRT_MAX)
Packit d7e8d0
    uval = USHRT_MAX;
Packit d7e8d0
  ti->signcount = uval;
Packit d7e8d0
Packit d7e8d0
  /* Parse the encr-count.  */
Packit d7e8d0
  err = _gpgme_strtoul_field (field[2], &uval);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  if (uval > USHRT_MAX)
Packit d7e8d0
    uval = USHRT_MAX;
Packit d7e8d0
  ti->encrcount = uval;
Packit d7e8d0
Packit d7e8d0
  if (nfields == 3)
Packit d7e8d0
    return 0; /* All mandatory fields parsed.  */
Packit d7e8d0
Packit d7e8d0
  /* Parse the policy.  */
Packit d7e8d0
  if (!strcmp (field[3], "none"))
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_NONE;
Packit d7e8d0
  else if (!strcmp (field[3], "auto"))
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_AUTO;
Packit d7e8d0
  else if (!strcmp (field[3], "good"))
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_GOOD;
Packit d7e8d0
  else if (!strcmp (field[3], "bad"))
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_BAD;
Packit d7e8d0
  else if (!strcmp (field[3], "ask"))
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_ASK;
Packit d7e8d0
  else /* "unknown" and invalid policy strings.  */
Packit d7e8d0
    ti->policy = GPGME_TOFU_POLICY_UNKNOWN;
Packit d7e8d0
Packit d7e8d0
  if (nfields == 4)
Packit d7e8d0
    return 0; /* No more optional fields.  */
Packit d7e8d0
Packit d7e8d0
  /* Parse first and last seen timestamps (none or both are required).  */
Packit d7e8d0
  if (nfields < 6)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* "tm2" missing.  */
Packit d7e8d0
  err = _gpgme_strtoul_field (field[4], &uval);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  ti->signfirst = uval;
Packit d7e8d0
  err = _gpgme_strtoul_field (field[5], &uval);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
  ti->signlast = uval;
Packit d7e8d0
  if (nfields > 7)
Packit d7e8d0
    {
Packit d7e8d0
      /* This condition is only to allow for gpg 2.1.15 - can
Packit d7e8d0
       * eventually be removed.  */
Packit d7e8d0
      err = _gpgme_strtoul_field (field[6], &uval);
Packit d7e8d0
      if (err)
Packit d7e8d0
        return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      ti->encrfirst = uval;
Packit d7e8d0
      err = _gpgme_strtoul_field (field[7], &uval);
Packit d7e8d0
      if (err)
Packit d7e8d0
        return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      ti->encrlast = uval;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse a TOFU_STATS_LONG line and store it in the last tofu info of SIG.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  gpgme_tofu_info_t ti;
Packit d7e8d0
  char *p;
Packit d7e8d0
Packit d7e8d0
  if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen.  */
Packit d7e8d0
  if (ti->description)
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set.  */
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  /* Remove the non-breaking spaces.  */
Packit d7e8d0
  if (!raw)
Packit d7e8d0
    {
Packit d7e8d0
      for (p = ti->description; *p; p++)
Packit d7e8d0
        if (*p == '~')
Packit d7e8d0
          *p = ' ';
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse an error status line and if SET_STATUS is true update the
Packit d7e8d0
   result status as appropriate.  With SET_STATUS being false, only
Packit d7e8d0
   check for an error.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_error (gpgme_signature_t sig, char *args, int set_status)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  char *where = strchr (args, ' ');
Packit d7e8d0
  char *which;
Packit d7e8d0
Packit d7e8d0
  if (where)
Packit d7e8d0
    {
Packit d7e8d0
      *where = '\0';
Packit d7e8d0
      which = where + 1;
Packit d7e8d0
Packit d7e8d0
      where = strchr (which, ' ');
Packit d7e8d0
      if (where)
Packit d7e8d0
	*where = '\0';
Packit d7e8d0
Packit d7e8d0
      where = args;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
  err = atoi (which);
Packit d7e8d0
Packit d7e8d0
  if (!strcmp (where, "proc_pkt.plaintext")
Packit d7e8d0
      && gpg_err_code (err) == GPG_ERR_BAD_DATA)
Packit d7e8d0
    {
Packit d7e8d0
      /* This indicates a double plaintext.  The only solid way to
Packit d7e8d0
         handle this is by failing the oepration.  */
Packit d7e8d0
      return gpg_error (GPG_ERR_BAD_DATA);
Packit d7e8d0
    }
Packit d7e8d0
  else if (!set_status)
Packit d7e8d0
    ;
Packit d7e8d0
  else if (!strcmp (where, "verify.findkey"))
Packit d7e8d0
    sig->status = err;
Packit d7e8d0
  else if (!strcmp (where, "verify.keyusage")
Packit d7e8d0
	   && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
Packit d7e8d0
    sig->wrong_key_usage = 1;
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
  char *end;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  sig = opd->current_sig;
Packit d7e8d0
Packit d7e8d0
  switch (code)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_STATUS_NEWSIG:
Packit d7e8d0
      if (sig)
Packit d7e8d0
        calc_sig_summary (sig);
Packit d7e8d0
      err = prepare_new_sig (opd);
Packit d7e8d0
      opd->only_newsig_seen = 1;
Packit d7e8d0
      opd->conflict_user_seen = 0;
Packit d7e8d0
      return err;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_GOODSIG:
Packit d7e8d0
    case GPGME_STATUS_EXPSIG:
Packit d7e8d0
    case GPGME_STATUS_EXPKEYSIG:
Packit d7e8d0
    case GPGME_STATUS_BADSIG:
Packit d7e8d0
    case GPGME_STATUS_ERRSIG:
Packit d7e8d0
    case GPGME_STATUS_REVKEYSIG:
Packit d7e8d0
      if (sig && !opd->did_prepare_new_sig)
Packit d7e8d0
	calc_sig_summary (sig);
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      return parse_new_sig (opd, code, args, ctx->protocol);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_VALIDSIG:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      return sig ? parse_valid_sig (sig, args, ctx->protocol)
Packit d7e8d0
	: trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_NODATA:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (!sig)
Packit d7e8d0
	return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_UNEXPECTED:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (!sig)
Packit d7e8d0
	return gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
      sig->status = gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_NOTATION_NAME:
Packit d7e8d0
    case GPGME_STATUS_NOTATION_FLAGS:
Packit d7e8d0
    case GPGME_STATUS_NOTATION_DATA:
Packit d7e8d0
    case GPGME_STATUS_POLICY_URL:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      return sig ? parse_notation (sig, code, args)
Packit d7e8d0
	: trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TRUST_UNDEFINED:
Packit d7e8d0
    case GPGME_STATUS_TRUST_NEVER:
Packit d7e8d0
    case GPGME_STATUS_TRUST_MARGINAL:
Packit d7e8d0
    case GPGME_STATUS_TRUST_FULLY:
Packit d7e8d0
    case GPGME_STATUS_TRUST_ULTIMATE:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      return sig ? parse_trust (sig, code, args)
Packit d7e8d0
	: trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_PKA_TRUST_BAD:
Packit d7e8d0
    case GPGME_STATUS_PKA_TRUST_GOOD:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      /* Check that we only get one of these status codes per
Packit d7e8d0
         signature; if not the crypto backend misbehaves.  */
Packit d7e8d0
      if (!sig || sig->pka_trust || sig->pka_address)
Packit d7e8d0
        return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
Packit d7e8d0
      end = strchr (args, ' ');
Packit d7e8d0
      if (end)
Packit d7e8d0
        *end = 0;
Packit d7e8d0
      sig->pka_address = strdup (args);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TOFU_USER:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (!sig)
Packit d7e8d0
        return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
      err = parse_tofu_user (sig, args, ctx->protocol);
Packit d7e8d0
      /* gpg emits TOFU User lines for each conflicting key.
Packit d7e8d0
       * GPGME does not expose this to have a clean API and
Packit d7e8d0
       * a GPGME user can do a keylisting with the address
Packit d7e8d0
       * normalisation.
Packit d7e8d0
       * So when a duplicated TOFU_USER line is encountered
Packit Service 30b792
       * we ignore the conflicting tofu stats emitted afterwards.
Packit d7e8d0
       */
Packit d7e8d0
      if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
Packit d7e8d0
        {
Packit d7e8d0
          opd->conflict_user_seen = 1;
Packit d7e8d0
          break;
Packit d7e8d0
        }
Packit d7e8d0
      opd->conflict_user_seen = 0;
Packit d7e8d0
      return trace_gpg_error (err);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TOFU_STATS:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (opd->conflict_user_seen)
Packit d7e8d0
        break;
Packit d7e8d0
      return sig ? parse_tofu_stats (sig, args)
Packit d7e8d0
        /*    */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_TOFU_STATS_LONG:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (opd->conflict_user_seen)
Packit d7e8d0
        break;
Packit d7e8d0
      return sig ? parse_tofu_stats_long (sig, args, ctx->raw_description)
Packit d7e8d0
        /*    */ : trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_ERROR:
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      /* Some  error stati are informational, so we don't return an
Packit d7e8d0
         error code if we are not ready to process this status.  */
Packit d7e8d0
      return parse_error (sig, args, !!sig );
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_FAILURE:
Packit d7e8d0
      opd->failure_code = _gpgme_parse_failure (args);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_EOF:
Packit d7e8d0
      if (sig && !opd->did_prepare_new_sig)
Packit d7e8d0
	calc_sig_summary (sig);
Packit d7e8d0
      if (opd->only_newsig_seen && sig)
Packit d7e8d0
        {
Packit d7e8d0
          gpgme_signature_t sig2;
Packit d7e8d0
          /* The last signature has no valid information - remove it
Packit d7e8d0
             from the list. */
Packit d7e8d0
          assert (!sig->next);
Packit d7e8d0
          if (sig == opd->result.signatures)
Packit d7e8d0
            opd->result.signatures = NULL;
Packit d7e8d0
          else
Packit d7e8d0
            {
Packit d7e8d0
              for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
Packit d7e8d0
                if (sig2->next == sig)
Packit d7e8d0
                  {
Packit d7e8d0
                    sig2->next = NULL;
Packit d7e8d0
                    break;
Packit d7e8d0
                  }
Packit d7e8d0
            }
Packit d7e8d0
          /* Note that there is no need to release the members of SIG
Packit d7e8d0
             because we won't be here if they have been set. */
Packit d7e8d0
          free (sig);
Packit d7e8d0
          opd->current_sig = NULL;
Packit d7e8d0
        }
Packit d7e8d0
      opd->only_newsig_seen = 0;
Packit d7e8d0
      if (opd->failure_code)
Packit d7e8d0
        return opd->failure_code;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_PLAINTEXT:
Packit d7e8d0
      if (++opd->plaintext_seen > 1)
Packit d7e8d0
        return gpg_error (GPG_ERR_BAD_DATA);
Packit Service 30b792
      {
Packit Service 30b792
        int mime = 0;
Packit Service 30b792
        err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime);
Packit Service 30b792
        if (err)
Packit Service 30b792
          return err;
Packit Service 30b792
        opd->result.is_mime = !!mime;
Packit Service 30b792
      }
Packit Service 30b792
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE:
Packit d7e8d0
      PARSE_COMPLIANCE_FLAGS (args, opd->current_sig);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_progress_status_handler (priv, code, args);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_verify_status_handler (priv, code, args);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
_gpgme_op_verify_init_result (gpgme_ctx_t ctx)
Packit d7e8d0
{
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
Packit d7e8d0
  return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
Packit d7e8d0
				sizeof (*opd), release_op_data);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
Packit d7e8d0
	      gpgme_data_t signed_text, gpgme_data_t plaintext)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_reset (ctx, synchronous);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_verify_init_result (ctx);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
Packit d7e8d0
Packit d7e8d0
  if (!sig)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
Packit d7e8d0
  return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext,
Packit d7e8d0
                                  ctx);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Decrypt ciphertext CIPHER and make a signature verification within
Packit d7e8d0
   CTX and store the resulting plaintext in PLAIN.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
Packit d7e8d0
		       gpgme_data_t signed_text, gpgme_data_t plaintext)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit Service 30b792
  TRACE_BEG  (DEBUG_CTX, "gpgme_op_verify_start", ctx,
Packit d7e8d0
	      "sig=%p, signed_text=%p, plaintext=%p",
Packit d7e8d0
	      sig, signed_text, plaintext);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = verify_start (ctx, 0, sig, signed_text, plaintext);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Decrypt ciphertext CIPHER and make a signature verification within
Packit d7e8d0
   CTX and store the resulting plaintext in PLAIN.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
Packit d7e8d0
		 gpgme_data_t plaintext)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit Service 30b792
  TRACE_BEG  (DEBUG_CTX, "gpgme_op_verify", ctx,
Packit d7e8d0
	      "sig=%p, signed_text=%p, plaintext=%p",
Packit d7e8d0
	      sig, signed_text, plaintext);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = verify_start (ctx, 1, sig, signed_text, plaintext);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_wait_one (ctx);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Compatibility interfaces.  */
Packit d7e8d0
Packit d7e8d0
/* Get the key used to create signature IDX in CTX and return it in
Packit d7e8d0
   R_KEY.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_verify_result_t result;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  result = gpgme_op_verify_result (ctx);
Packit d7e8d0
  sig = result->signatures;
Packit d7e8d0
Packit d7e8d0
  while (sig && idx)
Packit d7e8d0
    {
Packit d7e8d0
      sig = sig->next;
Packit d7e8d0
      idx--;
Packit d7e8d0
    }
Packit d7e8d0
  if (!sig || idx)
Packit d7e8d0
    return gpg_error (GPG_ERR_EOF);
Packit d7e8d0
Packit d7e8d0
  return gpgme_get_key (ctx, sig->fpr, r_key, 0);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Retrieve the signature status of signature IDX in CTX after a
Packit d7e8d0
   successful verify operation in R_STAT (if non-null).  The creation
Packit d7e8d0
   time stamp of the signature is returned in R_CREATED (if non-null).
Packit d7e8d0
   The function returns a string containing the fingerprint.  */
Packit d7e8d0
const char *
Packit d7e8d0
gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
Packit d7e8d0
                      _gpgme_sig_stat_t *r_stat, time_t *r_created)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_verify_result_t result;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit d7e8d0
  result = gpgme_op_verify_result (ctx);
Packit d7e8d0
  sig = result->signatures;
Packit d7e8d0
Packit d7e8d0
  while (sig && idx)
Packit d7e8d0
    {
Packit d7e8d0
      sig = sig->next;
Packit d7e8d0
      idx--;
Packit d7e8d0
    }
Packit d7e8d0
  if (!sig || idx)
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  if (r_stat)
Packit d7e8d0
    {
Packit d7e8d0
      switch (gpg_err_code (sig->status))
Packit d7e8d0
	{
Packit d7e8d0
	case GPG_ERR_NO_ERROR:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_GOOD;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_BAD_SIGNATURE:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_BAD;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_NO_PUBKEY:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_NOKEY;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_NO_DATA:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_NOSIG;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_SIG_EXPIRED:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_GOOD_EXP;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_KEY_EXPIRED:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	default:
Packit d7e8d0
	  *r_stat = GPGME_SIG_STAT_ERROR;
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  if (r_created)
Packit d7e8d0
    *r_created = sig->timestamp;
Packit d7e8d0
  return sig->fpr;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Retrieve certain attributes of a signature.  IDX is the index
Packit d7e8d0
   number of the signature after a successful verify operation.  WHAT
Packit d7e8d0
   is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
Packit d7e8d0
   one.  WHATIDX is to be passed as 0 for most attributes . */
Packit d7e8d0
unsigned long
Packit d7e8d0
gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
Packit d7e8d0
                          _gpgme_attr_t what, int whatidx)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_verify_result_t result;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit d7e8d0
  (void)whatidx;
Packit d7e8d0
Packit d7e8d0
  result = gpgme_op_verify_result (ctx);
Packit d7e8d0
  sig = result->signatures;
Packit d7e8d0
Packit d7e8d0
  while (sig && idx)
Packit d7e8d0
    {
Packit d7e8d0
      sig = sig->next;
Packit d7e8d0
      idx--;
Packit d7e8d0
    }
Packit d7e8d0
  if (!sig || idx)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  switch (what)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_ATTR_CREATED:
Packit d7e8d0
      return sig->timestamp;
Packit d7e8d0
Packit d7e8d0
    case GPGME_ATTR_EXPIRE:
Packit d7e8d0
      return sig->exp_timestamp;
Packit d7e8d0
Packit d7e8d0
    case GPGME_ATTR_VALIDITY:
Packit d7e8d0
      return (unsigned long) sig->validity;
Packit d7e8d0
Packit d7e8d0
    case GPGME_ATTR_SIG_STATUS:
Packit d7e8d0
      switch (gpg_err_code (sig->status))
Packit d7e8d0
	{
Packit d7e8d0
	case GPG_ERR_NO_ERROR:
Packit d7e8d0
	  return GPGME_SIG_STAT_GOOD;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_BAD_SIGNATURE:
Packit d7e8d0
	  return GPGME_SIG_STAT_BAD;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_NO_PUBKEY:
Packit d7e8d0
	  return GPGME_SIG_STAT_NOKEY;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_NO_DATA:
Packit d7e8d0
	  return GPGME_SIG_STAT_NOSIG;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_SIG_EXPIRED:
Packit d7e8d0
	  return GPGME_SIG_STAT_GOOD_EXP;
Packit d7e8d0
Packit d7e8d0
	case GPG_ERR_KEY_EXPIRED:
Packit d7e8d0
	  return GPGME_SIG_STAT_GOOD_EXPKEY;
Packit d7e8d0
Packit d7e8d0
	default:
Packit d7e8d0
	  return GPGME_SIG_STAT_ERROR;
Packit d7e8d0
	}
Packit d7e8d0
Packit d7e8d0
    case GPGME_ATTR_SIG_SUMMARY:
Packit d7e8d0
      return sig->summary;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
const char *
Packit d7e8d0
gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
Packit d7e8d0
                           _gpgme_attr_t what, int whatidx)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_verify_result_t result;
Packit d7e8d0
  gpgme_signature_t sig;
Packit d7e8d0
Packit d7e8d0
  result = gpgme_op_verify_result (ctx);
Packit d7e8d0
  sig = result->signatures;
Packit d7e8d0
Packit d7e8d0
  while (sig && idx)
Packit d7e8d0
    {
Packit d7e8d0
      sig = sig->next;
Packit d7e8d0
      idx--;
Packit d7e8d0
    }
Packit d7e8d0
  if (!sig || idx)
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  switch (what)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_ATTR_FPR:
Packit d7e8d0
      return sig->fpr;
Packit d7e8d0
Packit d7e8d0
    case GPGME_ATTR_ERRTOK:
Packit d7e8d0
      if (whatidx == 1)
Packit d7e8d0
        return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
Packit d7e8d0
      else
Packit d7e8d0
	return "";
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return NULL;
Packit d7e8d0
}