gpg: New option --import-filter

* g10/gpg.c (oImportFilter): New.
(opts): Add --import-filter.
(main): Handle option.
* g10/import.c: Include recsel.h, init.h, and mbox-util.h.
(import_keep_uid): New global var.
(cleanup_import_globals): New.
(parse_and_set_import_filter): New.
(filter_getval): New.
(apply_keep_uid_filter): New.
(import_one): Apply filter if set.
--

Funny new option.  It can for example be used to export a key with
only one user id:

  gpg --no-options --import --import-options import-export \
      --import-filter keep-uid='mbox=wk@gnupg.org'         \
     < full-key.pub > key-with-one-uid.pub

More features will eventually be added.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-07-01 16:24:04 +02:00
parent f015552374
commit 5137bf73cc
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 182 additions and 1 deletions

View File

@ -2218,6 +2218,45 @@ opposite meaning. The options are:
Defaults to no.
@end table
@item --import-filter @code{@var{name}=@var{expr}}
@opindex import-filter
This option defines an import filter which is implied to the imported
keyblock right before it will be stored. @var{name} defines the type
of filter to use, @var{expr} the expression to evaluate. The option
can be used several times which then appends more expression to the
same @var{name}.
@noindent
The available filter types are:
@table @asis
@item keep-uid
This filter will keep a user id packet and its dependent packets in
the keyblock if the expression evaluates to true.
@end table
The syntax for the expression is defined in the appendix (FIXME). The
property names for the expressions depend on the actual filter type
and are indicated in the following table.
The available properties are:
@table @asis
@item uid
A string with the user id. (keep-uid)
@item mbox
The addr-spec part of a user id with mailbox or the empty string.
(keep-uid)
@item primary
Boolean indicating whether the user id is the primary one. (keep-uid)
@end table
@item --export-options @code{parameters}
@opindex export-options
This is a space or comma delimited string that gives options for

View File

@ -300,6 +300,7 @@ enum cmd_and_opt_values
oKeyServer,
oKeyServerOptions,
oImportOptions,
oImportFilter,
oExportOptions,
oListOptions,
oVerifyOptions,
@ -572,6 +573,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"),
ARGPARSE_s_s (oImportOptions, "import-options", "@"),
ARGPARSE_s_s (oImportFilter, "import-filter", "@"),
ARGPARSE_s_s (oExportOptions, "export-options", "@"),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"),
@ -2033,6 +2035,7 @@ parse_tofu_db_format (const char *db_format)
}
}
/* This function called to initialized a new control object. It is
assumed that this object has been zeroed out before calling this
function. */
@ -3031,6 +3034,11 @@ main (int argc, char **argv)
log_error(_("invalid import options\n"));
}
break;
case oImportFilter:
rc = parse_and_set_import_filter (pargs.r.ret_str);
if (rc)
log_error (_("invalid filter option: %s\n"), gpg_strerror (rc));
break;
case oExportOptions:
if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
{

View File

@ -1,6 +1,6 @@
/* import.c - import a key into our key storage.
* Copyright (C) 1998-2007, 2010-2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
* Copyright (C) 2014, 2016 Werner Koch
*
* This file is part of GnuPG.
*
@ -35,9 +35,13 @@
#include "i18n.h"
#include "ttyio.h"
#include "status.h"
#include "recsel.h"
#include "keyserver-internal.h"
#include "call-agent.h"
#include "../common/membuf.h"
#include "../common/init.h"
#include "../common/mbox-util.h"
struct import_stats_s
{
@ -60,6 +64,16 @@ struct import_stats_s
};
/* A global variable to store the selector created from
* --import-filter keep-uid=EXPR.
*
* FIXME: We should put this into the CTRL object but that requires a
* lot more changes right now.
*/
static recsel_expr_t import_keep_uid;
static int import (ctrl_t ctrl,
IOBUF inp, const char* fname, struct import_stats_s *stats,
unsigned char **fpr, size_t *fpr_len, unsigned int options,
@ -95,6 +109,16 @@ static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs,
static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs,
const char *fname, u32 *keyid );
static void
cleanup_import_globals (void)
{
recsel_release (import_keep_uid);
import_keep_uid = NULL;
}
int
parse_import_options(char *str,unsigned int *options,int noisy)
{
@ -143,6 +167,39 @@ parse_import_options(char *str,unsigned int *options,int noisy)
}
/* Parse and set an import filter from string. STRING has the format
* "NAME=EXPR" with NAME being the name of the filter. Spaces before
* and after NAME are not allowed. If this function is all called
* several times all expressions for the same NAME are concatenated.
* Supported filter names are:
*
* - keep-uid :: If the expression evaluates to true for a certain
* user ID packet, that packet and all it dependencies
* will be imported. The expression may use these
* variables:
*
* - uid :: The entire user ID.
* - mbox :: The mail box part of the user ID.
* - primary :: Evaluate to true for the primary user ID.
*/
gpg_error_t
parse_and_set_import_filter (const char *string)
{
gpg_error_t err;
/* Auto register the cleanup function. */
register_mem_cleanup_func (cleanup_import_globals);
if (!strncmp (string, "keep-uid=", 9))
err = recsel_parse_expr (&import_keep_uid, string+9);
else
err = gpg_error (GPG_ERR_INV_NAME);
return err;
}
import_stats_t
import_new_stats_handle (void)
{
@ -983,6 +1040,74 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock)
}
/* Helper for apply_keep_uid_filter. */
static const char *
filter_getval (void *cookie, const char *propname)
{
kbnode_t node = cookie;
const char *result;
if (node->pkt->pkttype == PKT_USER_ID)
{
if (!strcmp (propname, "uid"))
result = node->pkt->pkt.user_id->name;
else if (!strcmp (propname, "mbox"))
{
if (!node->pkt->pkt.user_id->mbox)
{
node->pkt->pkt.user_id->mbox
= mailbox_from_userid (node->pkt->pkt.user_id->name);
}
return node->pkt->pkt.user_id->mbox;
}
else if (!strcmp (propname, "primary"))
result = node->pkt->pkt.user_id->is_primary? "1":"0";
else
result = NULL;
}
else
result = NULL;
return result;
}
/*
* Apply the keep-uid filter to the keyblock. The deleted nodes are
* marked and thus the caller should call commit_kbnode afterwards.
* KEYBLOCK must not have any blocks marked as deleted.
*/
static void
apply_keep_uid_filter (kbnode_t keyblock, recsel_expr_t selector)
{
kbnode_t node;
for (node = keyblock->next; node; node = node->next )
{
if (node->pkt->pkttype == PKT_USER_ID)
{
if (!recsel_select (selector, filter_getval, node))
{
/* log_debug ("keep-uid: deleting '%s'\n", */
/* node->pkt->pkt.user_id->name); */
/* The UID packet and all following packets up to the
* next UID or a subkey. */
delete_kbnode (node);
for (; node->next
&& node->next->pkt->pkttype != PKT_USER_ID
&& node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
&& node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
node = node->next)
delete_kbnode (node->next);
}
/* else */
/* log_debug ("keep-uid: keeping '%s'\n", */
/* node->pkt->pkt.user_id->name); */
}
}
}
/*
* Try to import one keyblock. Return an error only in serious cases,
* but never for an invalid keyblock. It uses log_error to increase
@ -1116,6 +1241,14 @@ import_one (ctrl_t ctrl,
/* Get rid of deleted nodes. */
commit_kbnode (&keyblock);
/* Apply import filter. */
if (import_keep_uid)
{
apply_keep_uid_filter (keyblock, import_keep_uid);
commit_kbnode (&keyblock);
}
/* Show the key in the form it is merged or inserted. We skip this
* if "import-export" is also active without --armor or the output
* file has explicily been given. */

View File

@ -349,6 +349,7 @@ typedef struct import_stats_s *import_stats_t;
typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg);
int parse_import_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_import_filter (const char *string);
void import_keys (ctrl_t ctrl, char **fnames, int nnames,
import_stats_t stats_hd, unsigned int options);
int import_keys_stream (ctrl_t ctrl, iobuf_t inp, import_stats_t stats_hd,