diff --git a/TODO b/TODO index e74c5abc..477d868b 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,6 @@ Hey Emacs, this is -*- outline -*- mode! The test is currently disabled there and in gpg/t-import. ** When gpg supports it, write binary subpackets directly, and parse SUBPACKET status lines. -** External key listing for OpenPGP. - This probably requires changes at gpg. - * ABI's to break: ** gpgme_edit_cb_t: Add "processed" return argument @@ -81,7 +78,7 @@ Hey Emacs, this is -*- outline -*- mode! release everything properly at a reset and at an error. Think hard about where to guarantee what (ie, what happens if start fails, are the fds unregistered immediately - i think so?) -** Optimize the case where a data object has an underlying fd we can pass +** Optimize the case where a data object has 0an underlying fd we can pass directly to the engine. This will be automatic with socket I/O and descriptor passing. ** Move code common to all engines up from gpg to engine. diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 9e4c831d..29113948 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,12 @@ +2006-02-22 Marcus Brinkmann + + * rungpg.c (read_colon_line): Invoke colon preprocess handler if + it is set. + (colon_preprocessor_t): New type. + (struct engine_gpg): New member colon.preprocess_fnc. + (gpg_keylist_preprocess): New function. + * keylist.c (keylist_colon_handler): Allow short key IDs. + 2006-02-15 Marcus Brinkmann * w32-io.c (create_writer): Make C->have_data a manually resetted diff --git a/gpgme/keylist.c b/gpgme/keylist.c index 50201773..bf731f0d 100644 --- a/gpgme/keylist.c +++ b/gpgme/keylist.c @@ -176,7 +176,8 @@ set_mainkey_trust_info (gpgme_key_t key, const char *src) case 'd': /* Note that gpg 1.3 won't print that anymore but only uses - the capabilities field. */ + the capabilities field. However, it is still used for + external key listings. */ key->disabled = 1; break; @@ -493,8 +494,9 @@ keylist_colon_handler (void *priv, char *line) subkey->pubkey_algo = i; } - /* Field 5 has the long keyid. */ - if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1) + /* Field 5 has the long keyid. Allow short key IDs for the + output of an external keyserver listing. */ + if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1) strcpy (subkey->_keyid, field[4]); /* Field 6 has the timestamp (seconds). */ diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index 0a3f0bb5..16805f1e 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -65,6 +65,8 @@ struct fd_data_map_s }; +typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline); + struct engine_gpg { char *file_name; @@ -95,6 +97,7 @@ struct engine_gpg engine_colon_line_handler_t fnc; /* this indicate use of this structrue */ void *fnc_value; void *tag; + colon_preprocessor_t preprocess_fnc; } colon; char **argv; @@ -1013,14 +1016,27 @@ read_colon_line (engine_gpg_t gpg) { /* (we require that the last line is terminated by a LF) and we skip empty lines. Note: we use UTF8 encoding - and escaping of special characters We require at + and escaping of special characters. We require at least one colon to cope with some other printed information. */ *p = 0; if (*buffer && strchr (buffer, ':')) { + char *line = NULL; + + if (gpg->colon.preprocess_fnc) + { + gpgme_error_t err; + + err = gpg->colon.preprocess_fnc (buffer, &line); + if (err) + return err; + } + assert (gpg->colon.fnc); - gpg->colon.fnc (gpg->colon.fnc_value, buffer); + gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer); + if (line) + free (line); } /* To reuse the buffer for the next line we have to @@ -1613,6 +1629,93 @@ gpg_import (void *engine, gpgme_data_t keydata) } +/* The output for external keylistings in GnuPG is different from all + the other key listings. We catch this here with a special + preprocessor that reformats the colon handler lines. */ +static gpgme_error_t +gpg_keylist_preprocess (char *line, char **r_line) +{ + enum + { + RT_NONE, RT_INFO, RT_PUB, RT_UID + } + rectype = RT_NONE; +#define NR_FIELDS 16 + char *field[NR_FIELDS]; + int fields = 0; + + *r_line = NULL; + + while (line && fields < NR_FIELDS) + { + field[fields++] = line; + line = strchr (line, ':'); + if (line) + *(line++) = '\0'; + } + + if (!strcmp (field[0], "info")) + rectype = RT_INFO; + else if (!strcmp (field[0], "pub")) + rectype = RT_PUB; + else if (!strcmp (field[0], "uid")) + rectype = RT_UID; + else + rectype = RT_NONE; + + switch (rectype) + { + case RT_INFO: + /* FIXME: Eventually, check the version number at least. */ + return 0; + + case RT_PUB: + if (fields < 7) + return 0; + + /* The format is: + + pub:::::: + + as defined in 5.2. Machine Readable Indexes of the OpenPGP + HTTP Keyserver Protocol (draft). + + We want: + pub:o::::::::::::: + */ + + if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::", + field[6], field[3], field[2], field[1], + field[4], field[5]) < 0) + return gpg_error_from_errno (errno); + return 0; + + case RT_UID: + /* The format is: + + uid:::: + + as defined in 5.2. Machine Readable Indexes of the OpenPGP + HTTP Keyserver Protocol (draft). + + We want: + uid:o::::::::: + */ + + if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:", + field[4], field[2], field[3], field[1]) < 0) + return gpg_error_from_errno (errno); + return 0; + + case RT_NONE: + /* Unknown record. */ + break; + } + return 0; + +} + + static gpgme_error_t gpg_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) @@ -1620,6 +1723,13 @@ gpg_keylist (void *engine, const char *pattern, int secret_only, engine_gpg_t gpg = engine; gpgme_error_t err; + if (mode & GPGME_KEYLIST_MODE_EXTERN) + { + if ((mode & GPGME_KEYLIST_MODE_LOCAL) + || secret_only) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + } + err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--fixed-list-mode"); @@ -1635,10 +1745,20 @@ gpg_keylist (void *engine, const char *pattern, int secret_only, err = add_arg (gpg, "show-sig-subpackets=\"20,26\""); } if (!err) - err = add_arg (gpg, secret_only ? "--list-secret-keys" - : ((mode & GPGME_KEYLIST_MODE_SIGS) - ? "--check-sigs" : "--list-keys")); - + { + if (mode & GPGME_KEYLIST_MODE_EXTERN) + { + err = add_arg (gpg, "--search-keys"); + gpg->colon.preprocess_fnc = gpg_keylist_preprocess; + } + else + { + err = add_arg (gpg, secret_only ? "--list-secret-keys" + : ((mode & GPGME_KEYLIST_MODE_SIGS) + ? "--check-sigs" : "--list-keys")); + } + } + /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--");