diff --git a/doc/gpg.texi b/doc/gpg.texi index 6f0249ab1..9a0622199 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -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 diff --git a/g10/gpg.c b/g10/gpg.c index b1d6c3464..009b84c0b 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -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)) { diff --git a/g10/import.c b/g10/import.c index 332e2667a..deb2787ea 100644 --- a/g10/import.c +++ b/g10/import.c @@ -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. */ diff --git a/g10/main.h b/g10/main.h index 322f43c53..58f2a73f8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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,