gpgme/gpgme/keylist.c
Marcus Brinkmann da95ecfc2c 2001-11-21 Marcus Brinkmann <marcus@g10code.de>
* context.h: Do not include rungpg.h, but engine.h.
	(struct gpgme_context_s): Replace member gpg with engine.
	* gpgme.c (gpgme_release): Release engine, not gpg.

	* recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function
	moved ...
	* rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here.
	Make static, change order of arguments, and return an error value.
	* ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype.

	* rungpg.h (_gpgme_gpg_op_verify): Add prototype.
	(_gpgme_gpg_op_encrypt): Likewise.
	(_gpgme_gpg_op_decrypt): Likewise.
	(_gpgme_gpg_op_delete): Likewise.
	(_gpgme_gpg_op_export): Likewise.
	(_gpgme_gpg_op_genkey): Likewise.
	(_gpgme_gpg_op_import): Likewise.
	(_gpgme_gpg_op_keylist): Likewise.
	(_gpgme_gpg_op_sign): Likewise.
	(_gpgme_gpg_op_trustlist): Likewise.
	* rungpg.c (_gpgme_gpg_op_verify): New function.
	(_gpgme_gpg_op_encrypt): Likewise.
	(_gpgme_gpg_op_decrypt): Likewise.
	(_gpgme_gpg_op_delete): Likewise.
	(_gpgme_gpg_op_export): Likewise.
	(_gpgme_gpg_op_genkey): Likewise.
	(_gpgme_gpg_op_import): Likewise.
	(_gpgme_gpg_op_keylist): Likewise.
	(_gpgme_gpg_op_sign): Likewise.
	(_gpgme_gpg_op_trustlist): Likewise.

	* engine.h (_gpgme_engine_set_status_handler): Add prototype.
	(_gpgme_engine_set_command_handler): Likewise.
	(_gpgme_engine_set_colon_line_handler): Likewise.
	(_gpgme_engine_op_decrypt): Likewise.
	(_gpgme_engine_op_delete): Likewise.
	(_gpgme_engine_op_encrypt): Likewise.
	(_gpgme_engine_op_export): Likewise.
	(_gpgme_engine_op_genkey): Likewise.
	(_gpgme_engine_op_import): Likewise.
	(_gpgme_engine_op_keylist): Likewise.
	(_gpgme_engine_op_sign): Likewise.
	(_gpgme_engine_op_trustlist): Likewise.
	(_gpgme_engine_op_verify): Likewise.
	(_gpgme_engine_start): Likewise.
	* engine.c (_gpgme_engine_set_status_handler): New function.
	(_gpgme_engine_set_command_handler): Likewise.
	(_gpgme_engine_set_colon_line_handler): Likewise.
	(_gpgme_engine_op_decrypt): Likewise.
	(_gpgme_engine_op_delete): Likewise.
	(_gpgme_engine_op_encrypt): Likewise.
	(_gpgme_engine_op_export): Likewise.
	(_gpgme_engine_op_genkey): Likewise.
	(_gpgme_engine_op_import): Likewise.
	(_gpgme_engine_op_keylist): Likewise.
	(_gpgme_engine_op_sign): Likewise.
	(_gpgme_engine_op_trustlist): Likewise.
	(_gpgme_engine_op_verify): Likewise.
	(_gpgme_engine_start): Likewise.

	* verify.c (gpgme_op_verify_start): Reimplement in terms of above
	functions.
	* encrypt.c (gpgme_op_encrypt_start): Likewise.
	* decrypt.c (_gpgme_decrypt_start): Likewise.
	* passphrase.c (_gpgme_passphrase_start): Likewise.
	* keylist.c (gpgme_op_keylist_start): Likewise.
2001-11-21 03:40:17 +00:00

482 lines
13 KiB
C

/* keylist.c - key listing
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of GPGME.
*
* GPGME 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 2 of the License, or
* (at your option) any later version.
*
* GPGME 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "util.h"
#include "context.h"
#include "ops.h"
#include "key.h"
#define my_isdigit(a) ( (a) >='0' && (a) <= '9' )
static void finish_key ( GpgmeCtx ctx );
static void
keylist_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
{
if ( ctx->out_of_core )
return;
switch (code) {
case STATUS_EOF:
finish_key (ctx);
break;
default:
/* ignore all other codes */
break;
}
}
static time_t
parse_timestamp ( char *p )
{
if (!*p )
return 0;
return (time_t)strtoul (p, NULL, 10);
}
static void
set_mainkey_trust_info ( GpgmeKey key, const char *s )
{
/* look at letters and stop at the first digit */
for (; *s && !my_isdigit (*s); s++ ) {
switch (*s) {
case 'e': key->keys.flags.expired = 1; break;
case 'r': key->keys.flags.revoked = 1; break;
case 'd': key->keys.flags.disabled = 1; break;
case 'i': key->keys.flags.invalid = 1; break;
}
}
}
static void
set_userid_flags ( GpgmeKey key, const char *s )
{
/* look at letters and stop at the first digit */
for (; *s && !my_isdigit (*s); s++ ) {
switch (*s) {
case 'r': key->uids->revoked = 1; break;
case 'i': key->uids->invalid = 1; break;
case 'n': key->uids->validity = GPGME_VALIDITY_NEVER; break;
case 'm': key->uids->validity = GPGME_VALIDITY_MARGINAL; break;
case 'f': key->uids->validity = GPGME_VALIDITY_FULL; break;
case 'u': key->uids->validity = GPGME_VALIDITY_ULTIMATE; break;
}
}
}
static void
set_subkey_trust_info ( struct subkey_s *k, const char *s )
{
/* look at letters and stop at the first digit */
for (; *s && !my_isdigit (*s); s++ ) {
switch (*s) {
case 'e': k->flags.expired = 1; break;
case 'r': k->flags.revoked = 1; break;
case 'd': k->flags.disabled = 1; break;
case 'i': k->flags.invalid = 1; break;
}
}
}
static void
set_mainkey_capability ( GpgmeKey key, const char *s )
{
for (; *s ; s++ ) {
switch (*s) {
case 'e': key->keys.flags.can_encrypt = 1; break;
case 's': key->keys.flags.can_sign = 1; break;
case 'c': key->keys.flags.can_certify = 1; break;
case 'E': key->gloflags.can_encrypt = 1; break;
case 'S': key->gloflags.can_sign = 1; break;
case 'C': key->gloflags.can_certify = 1; break;
}
}
}
static void
set_subkey_capability ( struct subkey_s *k, const char *s )
{
for (; *s; s++ ) {
switch (*s) {
case 'e': k->flags.can_encrypt = 1; break;
case 's': k->flags.can_sign = 1; break;
case 'c': k->flags.can_certify = 1; break;
}
}
}
/* Note: we are allowed to modify line */
static void
keylist_colon_handler ( GpgmeCtx ctx, char *line )
{
char *p, *pend;
int field = 0;
enum {
RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC
} rectype = RT_NONE;
GpgmeKey key = ctx->tmp_key;
int i;
const char *trust_info = NULL;
struct subkey_s *sk = NULL;
if ( ctx->out_of_core )
return;
if (!line) { /* EOF */
finish_key (ctx);
return;
}
for (p = line; p; p = pend) {
field++;
pend = strchr (p, ':');
if (pend)
*pend++ = 0;
if ( field == 1 ) {
if ( !strcmp ( p, "sig" ) )
rectype = RT_SIG;
else if ( !strcmp ( p, "uid" ) && key ) {
rectype = RT_UID;
key = ctx->tmp_key;
}
else if ( !strcmp (p, "sub") && key ) {
/* start a new subkey */
rectype = RT_SUB;
if ( !(sk = _gpgme_key_add_subkey (key)) ) {
ctx->out_of_core=1;
return;
}
}
else if ( !strcmp (p, "ssb") && key ) {
/* start a new secret subkey */
rectype = RT_SSB;
if ( !(sk = _gpgme_key_add_secret_subkey (key)) ) {
ctx->out_of_core=1;
return;
}
}
else if ( !strcmp (p, "pub") ) {
/* start a new keyblock */
if ( _gpgme_key_new ( &key ) ) {
ctx->out_of_core=1; /* the only kind of error we can get*/
return;
}
rectype = RT_PUB;
finish_key ( ctx );
assert ( !ctx->tmp_key );
ctx->tmp_key = key;
}
else if ( !strcmp (p, "sec") ) {
/* start a new keyblock */
if ( _gpgme_key_new_secret ( &key ) ) {
ctx->out_of_core=1; /*the only kind of error we can get*/
return;
}
rectype = RT_SEC;
finish_key ( ctx );
assert ( !ctx->tmp_key );
ctx->tmp_key = key;
}
else if ( !strcmp ( p, "fpr" ) && key )
rectype = RT_FPR;
else
rectype = RT_NONE;
}
else if ( rectype == RT_PUB || rectype == RT_SEC ) {
switch (field) {
case 2: /* trust info */
trust_info = p;
set_mainkey_trust_info (key, trust_info);
break;
case 3: /* key length */
i = atoi (p);
if ( i > 1 ) /* ignore invalid values */
key->keys.key_len = i;
break;
case 4: /* pubkey algo */
i = atoi (p);
if ( i > 1 && i < 128 )
key->keys.key_algo = i;
break;
case 5: /* long keyid */
if ( strlen (p) == DIM(key->keys.keyid)-1 )
strcpy (key->keys.keyid, p);
break;
case 6: /* timestamp (seconds) */
key->keys.timestamp = parse_timestamp (p);
break;
case 7: /* valid for n days */
break;
case 8: /* reserved (LID) */
break;
case 9: /* ownertrust */
break;
case 10: /* not used due to --fixed-list-mode option */
break;
case 11: /* signature class */
break;
case 12: /* capabilities */
set_mainkey_capability (key, p );
break;
case 13:
pend = NULL; /* we can stop here */
break;
}
}
else if ( (rectype == RT_SUB || rectype== RT_SSB) && sk ) {
switch (field) {
case 2: /* trust info */
set_subkey_trust_info ( sk, p);
break;
case 3: /* key length */
i = atoi (p);
if ( i > 1 ) /* ignore invalid values */
sk->key_len = i;
break;
case 4: /* pubkey algo */
i = atoi (p);
if ( i > 1 && i < 128 )
sk->key_algo = i;
break;
case 5: /* long keyid */
if ( strlen (p) == DIM(sk->keyid)-1 )
strcpy (sk->keyid, p);
break;
case 6: /* timestamp (seconds) */
sk->timestamp = parse_timestamp (p);
break;
case 7: /* valid for n days */
break;
case 8: /* reserved (LID) */
break;
case 9: /* ownertrust */
break;
case 10:/* user ID n/a for a subkey */
break;
case 11: /* signature class */
break;
case 12: /* capability */
set_subkey_capability ( sk, p );
break;
case 13:
pend = NULL; /* we can stop here */
break;
}
}
else if ( rectype == RT_UID ) {
switch (field) {
case 2: /* trust info */
trust_info = p; /*save for later */
break;
case 10: /* user ID */
if ( _gpgme_key_append_name ( key, p) )
ctx->out_of_core = 1;
else {
if (trust_info)
set_userid_flags (key, trust_info);
}
pend = NULL; /* we can stop here */
break;
}
}
else if ( rectype == RT_FPR ) {
switch (field) {
case 10: /* fingerprint (take only the first one)*/
if ( !key->keys.fingerprint && *p ) {
key->keys.fingerprint = xtrystrdup (p);
if ( !key->keys.fingerprint )
ctx->out_of_core = 1;
}
pend = NULL; /* that is all we want */
break;
}
}
}
}
/*
* We have read an entire key into ctx->tmp_key and should now finish
* it. It is assumed that this releases ctx->tmp_key.
*/
static void
finish_key ( GpgmeCtx ctx )
{
GpgmeKey key = ctx->tmp_key;
struct key_queue_item_s *q, *q2;
if (key) {
ctx->tmp_key = NULL;
_gpgme_key_cache_add (key);
q = xtrymalloc ( sizeof *q );
if ( !q ) {
gpgme_key_release (key);
ctx->out_of_core = 1;
return;
}
q->key = key;
q->next = NULL;
/* fixme: lock queue. Use a tail pointer? */
if ( !(q2 = ctx->key_queue) )
ctx->key_queue = q;
else {
for ( ; q2->next; q2 = q2->next )
;
q2->next = q;
}
ctx->key_cond = 1;
/* fixme: unlock queue */
}
}
/**
* gpgme_op_keylist_start:
* @c: context
* @pattern: a GnuPG user ID or NULL for all
* @secret_only: List only keys where the secret part is available
*
* Note that this function also cancels a pending key listing
* operaton. To actually retrieve the key, use
* gpgme_op_keylist_next().
*
* Return value: 0 on success or an errorcode.
**/
GpgmeError
gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only)
{
GpgmeError err = 0;
if (!ctx)
return mk_error (Invalid_Value);
ctx->pending = 1;
_gpgme_release_result (ctx);
ctx->out_of_core = 0;
if (ctx->engine)
{
_gpgme_engine_release (ctx->engine);
ctx->engine = NULL;
}
gpgme_key_release (ctx->tmp_key);
ctx->tmp_key = NULL;
/* Fixme: Release key_queue. */
err = _gpgme_engine_new (ctx->use_cms ? GPGME_PROTOCOL_CMS
: GPGME_PROTOCOL_OpenPGP, &ctx->engine);
if (err)
goto leave;
_gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
err = _gpgme_engine_set_colon_line_handler (ctx->engine,
keylist_colon_handler, ctx);
if (err)
goto leave;
_gpgme_engine_set_verbosity (ctx->engine, ctx->verbosity);
err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, ctx->keylist_mode);
if (!err) /* And kick off the process. */
err = _gpgme_engine_start (ctx->engine, ctx);
leave:
if (err)
{
ctx->pending = 0;
_gpgme_engine_release (ctx->engine);
ctx->engine = NULL;
}
return err;
}
/**
* gpgme_op_keylist_next:
* @c: Context
* @r_key: Returned key object
*
* Return the next key from the key listing started with
* gpgme_op_keylist_start(). The caller must free the key using
* gpgme_key_release().
*
* Return value: 0 on success, %GPGME_EOF or anoter error code.
**/
GpgmeError
gpgme_op_keylist_next ( GpgmeCtx c, GpgmeKey *r_key )
{
struct key_queue_item_s *q;
if (!r_key)
return mk_error (Invalid_Value);
*r_key = NULL;
if (!c)
return mk_error (Invalid_Value);
if ( !c->pending )
return mk_error (No_Request);
if ( c->out_of_core )
return mk_error (Out_Of_Core);
if ( !c->key_queue ) {
_gpgme_wait_on_condition (c, 1, &c->key_cond );
if ( c->out_of_core )
return mk_error (Out_Of_Core);
if ( !c->key_cond )
return mk_error (EOF);
c->key_cond = 0;
assert ( c->key_queue );
}
q = c->key_queue;
c->key_queue = q->next;
if (!c->key_queue)
c->key_cond = 0;
*r_key = q->key;
xfree (q);
return 0;
}