diff --git a/NEWS b/NEWS index 771a25c7..06eb9606 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ Noteworthy changes in version 0.4.0 (unreleased) ------------------------------------------------ + * Supports signatures of user IDs in keys via the new + GPGME_KEYLIST_MODE_SIGS keylist mode and the + gpgme_key_sig_get_string_attr and gpgme_key_sig_get_ulong_attr + interfaces. The XML info about a key also includes the signatures + if available. + * New data object interface, which is more flexible and transparent. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -23,6 +29,11 @@ gpgme_op_verify CHANGED: Take different data objects for gpgme_op_verify_start CHANGED: See gpgme_op_verify. gpgme_check_engine REMOVED: Deprecated since 0.3.0. gpgme_op_genkey CHANGED: New parameter FPR. +GPGME_KEYLIST_MODE_SIGS NEW +gpgme_key_sig_get_string_attr NEW +gpgme_key_sig_get_ulong_attr NEW +gpgme_get_key NEW +GPGME_ATTR_SIG_CLASS NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.13 (2002-11-20) diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index a34cf953..c9986a0d 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,62 @@ +2002-12-04 Marcus Brinkmann + + * gpgme.h: Add prototype for gpgme_get_key. + * key.c (gpgme_get_key): New function. + * verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key. + + * gpgme.h: Add prototypes for new interfaces + gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr. + (enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS. + * gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS. + * key.h (struct certsig_s): New members ALGO, NAME_PART, + EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS. + + * conversion.c (_gpgme_decode_c_string): Add new parameter LEN. + Use that to determine if allocation is desired or not. + * util.h: Adjust prototype of _gpgme_decode_c_string. + * keylist.c (keylist_colon_handler): Adjust caller of + _gpgme_decode_c_string. + + * key.h (struct gpgme_key_s): New member last_uid. + * key.c (_gpgme_key_append_name): Rewritten using + _gpgme_decode_c_string and the last_uid pointer. + (my_isdigit): Macro removed. + (ALLOC_CHUNK): Likewise. + * keylist.c (set_userid_flags): Use last_uid member of KEY. + + * context.h (struct user_id_s): New member last_certsig. + * key.h: Add prototype for _gpgme_key_add_certsig. + * key.c (_gpgme_key_add_certsig): New function. + (set_user_id_part): Move function before _gpgme_key_add_certsig. + (parse_user_id): Change first argument to SRC, add new arguments + NAME, EMAIL and COMMENT. Change code to use these arguments + instead going through UID. Move function before + _gpgme_add_certsig. + (parse_x509_user_id): Likewise. + (_gpgme_key_append_name): Adjust arguments to parse_x509_user_id + and parse_user_id invocation. + (one_certsig_as_xml): New function. + (one_uid_as_xml): Print signatures. + * context.h (struct gpgme_context_s): New member TMP_UID. + * keylist.c (keylist_colon_handler): Rewritten, implement "sig" + record entries. + + * key.c (get_certsig): New function. + (gpgme_key_sig_get_string_attr): Likewise. + (gpgme_key_sig_get_ulong_attr): Likewise. + + * keylist.c: Include . + (my_isdigit): Macro removed. + (set_mainkey_trust_info): Use isdigit, not my_isdigit. + (set_userid_flags): Likewise. + (set_subkey_trust_info): Likewise. + (set_ownertrust): Likewise. + (finish_key): Move function up a bit and remove prototype. + + * rungpg.c (gpg_keylist_ext): Correct precedence of signature + listing mode. + (gpg_keylist_ext): Implement signature listing mode. + 2002-11-25 Marcus Brinkmann * rungpg.c (_gpgme_gpg_spawn): Do not set parent fds to -1. diff --git a/gpgme/context.h b/gpgme/context.h index 4a588ce3..ced7a793 100644 --- a/gpgme/context.h +++ b/gpgme/context.h @@ -1,23 +1,22 @@ /* context.h - * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001, 2002 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 - */ + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef CONTEXT_H #define CONTEXT_H @@ -93,6 +92,7 @@ struct gpgme_context_s /* Used by keylist.c. */ GpgmeKey tmp_key; + struct user_id_s *tmp_uid; /* Something new is available. */ volatile int key_cond; struct key_queue_item_s *key_queue; @@ -124,6 +124,7 @@ struct user_id_s unsigned int invalid : 1; GpgmeValidity validity; struct certsig_s *certsigs; + struct certsig_s *last_certsig; const char *name_part; /* All 3 point into strings behind name */ const char *email_part; /* or to read-only strings. */ const char *comment_part; diff --git a/gpgme/conversion.c b/gpgme/conversion.c index e0344ec7..32fc954c 100644 --- a/gpgme/conversion.c +++ b/gpgme/conversion.c @@ -57,18 +57,29 @@ _gpgme_hextobyte (const byte *str) } +/* Decode the C formatted string SRC and store the result in the + buffer *DESTP which is LEN bytes long. If LEN is zero, then a + large enough buffer is allocated with malloc and *DESTP is set to + the result. Currently, LEN is only used to specify if allocation + is desired or not, the caller is expected to make sure that *DESTP + is large enough if LEN is not zero. */ GpgmeError -_gpgme_decode_c_string (const char *src, char **destp) +_gpgme_decode_c_string (const char *src, char **destp, int len) { char *dest; - /* We can malloc a buffer of the same length, because the converted - string will never be larger. */ - dest = malloc (strlen (src) + 1); - if (!dest) - return mk_error (Out_Of_Core); + if (len) + dest = *destp; + else + { + /* We can malloc a buffer of the same length, because the converted + string will never be larger. */ + dest = malloc (strlen (src) + 1); + if (!dest) + return mk_error (Out_Of_Core); - *destp = dest; + *destp = dest; + } while (*src) { diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index 3e965b40..aaa8e944 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -1,25 +1,26 @@ /* gpgme.c - GnuPG Made Easy - * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001, 2002 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 - */ + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H #include +#endif #include #include #include @@ -348,8 +349,9 @@ gpgme_set_keylist_mode (GpgmeCtx ctx, int mode) return mk_error (Invalid_Value); if (!((mode & GPGME_KEYLIST_MODE_LOCAL) - || (mode & GPGME_KEYLIST_MODE_EXTERN))) - return mk_error (Invalid_Value); + || (mode & GPGME_KEYLIST_MODE_EXTERN) + || (mode & GPGME_KEYLIST_MODE_SIGS))) + return mk_error (Invalid_Value); ctx->keylist_mode = mode; return 0; diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 6d9e4b73..198a06b0 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -196,7 +196,8 @@ typedef enum GPGME_ATTR_CHAINID = 28, GPGME_ATTR_SIG_STATUS = 29, GPGME_ATTR_ERRTOK = 30, - GPGME_ATTR_SIG_SUMMARY = 31 + GPGME_ATTR_SIG_SUMMARY = 31, + GPGME_ATTR_SIG_CLASS = 32 } GpgmeAttr; @@ -611,6 +612,13 @@ GpgmeError gpgme_data_rewind (GpgmeData dh); /* Key and trust functions. */ +/* Get the key with the fingerprint FPR from the key cache or from the + crypto backend. If FORCE_UPDATE is true, force a refresh of the + key from the crypto backend and replace the key in the cache, if + any. If SECRET is true, get the secret key. */ +GpgmeError gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key, + int secret, int force_update); + /* Acquire a reference to KEY. */ void gpgme_key_ref (GpgmeKey key); @@ -635,6 +643,21 @@ const char *gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what, unsigned long gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what, const void *reserved, int idx); +/* Return the value of the attribute WHAT of a signature on user ID + UID_IDX in KEY, which has to be representable by a string. IDX + specifies the signature. */ +const char *gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, + GpgmeAttr what, + const void *reserved, int idx); + +/* Return the value of the attribute WHAT of a signature on user ID + UID_IDX in KEY, which has to be representable by an unsigned + integer string. IDX specifies the signature. */ +unsigned long gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, + GpgmeAttr what, + const void *reserved, int idx); + + /* Release the trust item ITEM. */ void gpgme_trust_item_release (GpgmeTrustItem item); diff --git a/gpgme/key.c b/gpgme/key.c index ebb060d9..d67d710d 100644 --- a/gpgme/key.c +++ b/gpgme/key.c @@ -1,25 +1,26 @@ -/* key.c - Key and keyList objects - * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001, 2002 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 - */ +/* key.c - Key objects. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H #include +#endif #include #include #include @@ -31,13 +32,11 @@ #include "key.h" #include "sema.h" -#define ALLOC_CHUNK 1024 -#define my_isdigit(a) ((a) >='0' && (a) <= '9') - #if SIZEOF_UNSIGNED_INT < 4 #error unsigned int too short to be used as a hash value #endif + struct key_cache_item_s { struct key_cache_item_s *next; @@ -82,6 +81,7 @@ hash_key (const char *fpr, unsigned int *rhash) return 0; } + void _gpgme_key_cache_init (void) { @@ -251,7 +251,7 @@ _gpgme_key_cache_get (const char *fpr) return NULL; } - + static const char * pkalgo_to_string (int algo) { @@ -288,12 +288,14 @@ key_new (GpgmeKey *r_key, int secret) return 0; } + GpgmeError _gpgme_key_new (GpgmeKey *r_key) { return key_new (r_key, 0); } + GpgmeError _gpgme_key_new_secret (GpgmeKey *r_key) { @@ -317,7 +319,7 @@ gpgme_key_ref (GpgmeKey key) UNLOCK (key_ref_lock); } - + static struct subkey_s * add_subkey (GpgmeKey key, int secret) { @@ -327,7 +329,7 @@ add_subkey (GpgmeKey key, int secret) if (!k) return NULL; - if(!(kk = key->keys.next)) + if (!(kk = key->keys.next)) key->keys.next = k; else { @@ -354,7 +356,173 @@ _gpgme_key_add_secret_subkey (GpgmeKey key) return add_subkey (key, 1); } + +static char * +set_user_id_part (char *tail, const char *buf, size_t len) +{ + while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t')) + len--; + for (; len; len--) + *tail++ = *buf++; + *tail++ = 0; + return tail; +} + +static void +parse_user_id (const char *src, const char **name, const char **email, + const char **comment, char *tail) +{ + const char *start = NULL; + int in_name = 0; + int in_email = 0; + int in_comment = 0; + + while (*src) + { + if (in_email) + { + if (*src == '<') + /* Not legal but anyway. */ + in_email++; + else if (*src == '>') + { + if (!--in_email && !*email) + { + *email = tail; + tail = set_user_id_part (tail, start, src - start); + } + } + } + else if (in_comment) + { + if (*src == '(') + in_comment++; + else if (*src == ')') + { + if (!--in_comment && !*comment) + { + *comment = tail; + tail = set_user_id_part (tail, start, src - start); + } + } + } + else if (*src == '<') + { + if (in_name) + { + if (!*name) + { + *name = tail; + tail = set_user_id_part (tail, start, src - start); + } + in_name = 0; + } + in_email = 1; + start = src + 1; + } + else if (*src == '(') + { + if (in_name) + { + if (!*name) + { + *name = tail; + tail = set_user_id_part (tail, start, src - start); + } + in_name = 0; + } + in_comment = 1; + start = src + 1; + } + else if (!in_name && *src != ' ' && *src != '\t') + { + in_name = 1; + start = src; + } + src++; + } + + if (in_name) + { + if (!*name) + { + *name = tail; + tail = set_user_id_part (tail, start, src - start); + } + } + + /* Let unused parts point to an EOS. */ + tail--; + if (!*name) + *name = tail; + if (!*email) + *email = tail; + if (!*comment) + *comment = tail; +} + + +static void +parse_x509_user_id (const char *src, const char **name, const char **email, + const char **comment, char *tail) +{ + if (*src == '<' && src[strlen (src) - 1] == '>') + *email = src; + + /* Let unused parts point to an EOS. */ + tail--; + if (!*name) + *name = tail; + if (!*email) + *email = tail; + if (!*comment) + *comment = tail; +} + + +struct certsig_s * +_gpgme_key_add_certsig (GpgmeKey key, char *src) +{ + int src_len = src ? strlen (src) : 0; + struct user_id_s *uid; + struct certsig_s *certsig; + + assert (key); /* XXX */ + + uid = key->last_uid; + assert (uid); /* XXX */ + + /* We can malloc a buffer of the same length, because the converted + string will never be larger. Actually we allocate it twice the + size, so that we are able to store the parsed stuff there too. */ + certsig = calloc (1, sizeof (*certsig) + 2 * src_len + 3); + if (!certsig) + return NULL; + + if (src) + { + char *dst = certsig->name; + _gpgme_decode_c_string (src, &dst, src_len + 1); + dst += src_len + 1; + if (key->x509) + parse_x509_user_id (src, &certsig->name_part, &certsig->email_part, + &certsig->comment_part, dst); + else + parse_user_id (src, &certsig->name_part, &certsig->email_part, + &certsig->comment_part, dst); + } + + if (!uid->certsigs) + uid->certsigs = certsig; + if (uid->last_certsig) + uid->last_certsig->next = certsig; + uid->last_certsig = certsig; + + return certsig; +} + + /** * gpgme_key_release: * @key: Key Object or NULL @@ -406,6 +574,7 @@ gpgme_key_release (GpgmeKey key) free (key); } + /** * gpgme_key_unref: * @key: Key Object @@ -418,237 +587,41 @@ gpgme_key_unref (GpgmeKey key) gpgme_key_release (key); } - -static char * -set_user_id_part (char *tail, const char *buf, size_t len) -{ - while (len && (buf[len-1] == ' ' || buf[len-1] == '\t')) - len--; - for (; len; len--) - *tail++ = *buf++; - *tail++ = 0; - return tail; -} - - -static void -parse_user_id (struct user_id_s *uid, char *tail) -{ - const char *s, *start=NULL; - int in_name = 0; - int in_email = 0; - int in_comment = 0; - - for (s = uid->name; *s; s++) - { - if (in_email) - { - if (*s == '<') - /* Not legal but anyway. */ - in_email++; - else if (*s == '>') - { - if (!--in_email) - { - if (!uid->email_part) - { - uid->email_part = tail; - tail = set_user_id_part ( tail, start, s-start ); - } - } - } - } - else if (in_comment) - { - if (*s == '(') - in_comment++; - else if (*s== ')') - { - if (!--in_comment) - { - if (!uid->comment_part) - { - uid->comment_part = tail; - tail = set_user_id_part ( tail, start, s-start ); - } - } - } - } - else if (*s == '<') - { - if (in_name) - { - if (!uid->name_part) - { - uid->name_part = tail; - tail = set_user_id_part (tail, start, s-start); - } - in_name = 0; - } - in_email = 1; - start = s+1; - } - else if (*s == '(') - { - if (in_name) - { - if (!uid->name_part) - { - uid->name_part = tail; - tail = set_user_id_part (tail, start, s-start ); - } - in_name = 0; - } - in_comment = 1; - start = s+1; - } - else if (!in_name && *s != ' ' && *s != '\t') - { - in_name = 1; - start = s; - } - } - - if (in_name) - { - if (!uid->name_part) - { - uid->name_part = tail; - tail = set_user_id_part (tail, start, s-start); - } - } - - /* Let unused parts point to an EOS. */ - tail--; - if (!uid->name_part) - uid->name_part = tail; - if (!uid->email_part) - uid->email_part = tail; - if (!uid->comment_part) - uid->comment_part = tail; -} - -static void -parse_x509_user_id (struct user_id_s *uid, char *tail) -{ - const char *s; - - s=uid->name; - if (*s == '<' && s[strlen (s) - 1] == '>') - uid->email_part = s; - - /* Let unused parts point to an EOS. */ - tail--; - if (!uid->name_part) - uid->name_part = tail; - if (!uid->email_part) - uid->email_part = tail; - if (!uid->comment_part) - uid->comment_part = tail; -} - -/* - * Take a name from the --with-colon listing, remove certain escape sequences - * sequences and put it into the list of UIDs - */ + +/* Take a name from the --with-colon listing, remove certain escape + sequences sequences and put it into the list of UIDs. */ GpgmeError -_gpgme_key_append_name (GpgmeKey key, const char *s) +_gpgme_key_append_name (GpgmeKey key, const char *src) { struct user_id_s *uid; - char *d; + char *dst; + int src_len = strlen (src); assert (key); /* We can malloc a buffer of the same length, because the converted string will never be larger. Actually we allocate it twice the size, so that we are able to store the parsed stuff there too. */ - uid = malloc (sizeof *uid + 2*strlen (s)+3); + uid = malloc (sizeof (*uid) + 2 * src_len + 3); if (!uid) return mk_error (Out_Of_Core); memset (uid, 0, sizeof *uid); - d = uid->name; - while (*s) - { - if (*s != '\\') - *d++ = *s++; - else if (s[1] == '\\') - { - s++; - *d++ = *s++; - } - else if (s[1] == 'n') - { - s += 2; - *d++ = '\n'; - } - else if (s[1] == 'r') - { - s += 2; - *d++ = '\r'; - } - else if (s[1] == 'v') - { - s += 2; - *d++ = '\v'; - } - else if (s[1] == 'b') - { - s += 2; - *d++ = '\b'; - } - else if (s[1] == '0') - { - /* Hmmm: no way to express this */ - s += 2; - *d++ = '\\'; - *d++ = '\0'; - } - else if (s[1] == 'x' && isxdigit (s[2]) && isxdigit (s[3])) - { - int val = _gpgme_hextobyte (&s[2]); - if (val == -1) - { - /* Should not happen. */ - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - else - { - if (!val) - { - *d++ = '\\'; - *d++ = '\0'; - } - else - *(byte*)d++ = val; - s += 4; - } - } - else - { - /* should not happen */ - s++; - *d++ = '\\'; - *d++ = *s++; - } - } - *d++ = 0; + dst = uid->name; + _gpgme_decode_c_string (src, &dst, src_len + 1); + + dst += src_len + 1; if (key->x509) - parse_x509_user_id (uid, d); + parse_x509_user_id (src, &uid->name_part, &uid->email_part, + &uid->comment_part, dst); else - parse_user_id (uid, d); + parse_user_id (src, &uid->name_part, &uid->email_part, + &uid->comment_part, dst); - if (key->uids) - { - struct user_id_s *u = key->uids; - while (u->next) - u = u->next; - u->next = uid; - } - else + if (!key->uids) key->uids = uid; + if (key->last_uid) + key->last_uid->next = uid; + key->last_uid = uid; return 0; } @@ -662,6 +635,7 @@ add_otag (GpgmeData d, const char *tag) _gpgme_data_append_string (d, ">"); } + static void add_ctag (GpgmeData d, const char *tag) { @@ -670,6 +644,7 @@ add_ctag (GpgmeData d, const char *tag) _gpgme_data_append_string (d, ">\n"); } + static void add_tag_and_string (GpgmeData d, const char *tag, const char *string) { @@ -678,6 +653,7 @@ add_tag_and_string (GpgmeData d, const char *tag, const char *string) add_ctag (d, tag); } + static void add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val) { @@ -686,6 +662,7 @@ add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val) add_tag_and_string (d, tag, buf); } + static void add_tag_and_time (GpgmeData d, const char *tag, time_t val) { @@ -697,22 +674,55 @@ add_tag_and_time (GpgmeData d, const char *tag, time_t val) add_tag_and_string (d, tag, buf); } + static void -one_uid_as_xml (GpgmeData d, struct user_id_s *u) +one_certsig_as_xml (GpgmeData data, struct certsig_s *certsig) { - _gpgme_data_append_string (d, " \n"); - if (u->invalid) - _gpgme_data_append_string (d, " \n"); - if (u->revoked) - _gpgme_data_append_string (d, " \n"); - add_tag_and_string (d, "raw", u->name); - if (*u->name_part) - add_tag_and_string (d, "name", u->name_part); - if (*u->email_part) - add_tag_and_string (d, "email", u->email_part); - if (*u->comment_part) - add_tag_and_string (d, "comment", u->comment_part); - _gpgme_data_append_string (d, " \n"); + _gpgme_data_append_string (data, " \n"); + if (certsig->flags.invalid) + _gpgme_data_append_string (data, " \n"); + if (certsig->flags.revoked) + _gpgme_data_append_string (data, " \n"); + if (certsig->flags.expired) + _gpgme_data_append_string (data, " \n"); + add_tag_and_string (data, "keyid", certsig->keyid); + add_tag_and_uint (data, "algo", certsig->algo); + add_tag_and_time (data, "created", certsig->timestamp); + add_tag_and_time (data, "expire", certsig->expires_at); + if (*certsig->name) + add_tag_and_string (data, "raw", certsig->name); + if (*certsig->name_part) + add_tag_and_string (data, "name", certsig->name_part); + if (*certsig->email_part) + add_tag_and_string (data, "email", certsig->email_part); + if (*certsig->comment_part) + add_tag_and_string (data, "comment", certsig->comment_part); + _gpgme_data_append_string (data, " \n"); +} + + +static void +one_uid_as_xml (GpgmeData data, struct user_id_s *uid) +{ + struct certsig_s *certsig; + + _gpgme_data_append_string (data, " \n"); + if (uid->invalid) + _gpgme_data_append_string (data, " \n"); + if (uid->revoked) + _gpgme_data_append_string (data, " \n"); + add_tag_and_string (data, "raw", uid->name); + if (*uid->name_part) + add_tag_and_string (data, "name", uid->name_part); + if (*uid->email_part) + add_tag_and_string (data, "email", uid->email_part); + if (*uid->comment_part) + add_tag_and_string (data, "comment", uid->comment_part); + + /* Now the signatures. */ + for (certsig = uid->certsigs; certsig; certsig = certsig->next) + one_certsig_as_xml (data, certsig); + _gpgme_data_append_string (data, " \n"); } @@ -817,8 +827,8 @@ capabilities_to_string (struct subkey_s *k) "esc" }; return strings[(!!k->flags.can_encrypt << 2) - | (!!k->flags.can_sign << 1) - | (!!k->flags.can_certify )]; + | (!!k->flags.can_sign << 1) + | (!!k->flags.can_certify)]; } @@ -972,6 +982,7 @@ gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what, case GPGME_ATTR_SIG_STATUS: case GPGME_ATTR_SIG_SUMMARY: case GPGME_ATTR_ERRTOK: + case GPGME_ATTR_SIG_CLASS: /* Not of any use here. */ break; } @@ -1102,3 +1113,154 @@ gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what, return val; } + +static struct certsig_s * +get_certsig (GpgmeKey key, int uid_idx, int idx) +{ + struct user_id_s *uid; + struct certsig_s *certsig; + + if (!key || uid_idx < 0 || idx < 0) + return NULL; + + uid = key->uids; + while (uid && uid_idx > 0) + { + uid = uid->next; + uid_idx--; + } + if (!uid) + return NULL; + + certsig = uid->certsigs; + while (certsig && idx > 0) + { + certsig = certsig->next; + idx--; + } + return certsig; +} + + +const char * +gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, GpgmeAttr what, + const void *reserved, int idx) +{ + struct certsig_s *certsig = get_certsig (key, uid_idx, idx); + + if (!certsig || reserved) + return NULL; + + switch (what) + { + case GPGME_ATTR_KEYID: + return certsig->keyid; + + case GPGME_ATTR_ALGO: + return pkalgo_to_string (certsig->algo); + + case GPGME_ATTR_USERID: + return certsig->name; + + case GPGME_ATTR_NAME: + return certsig->name_part; + + case GPGME_ATTR_EMAIL: + return certsig->email_part; + + case GPGME_ATTR_COMMENT: + return certsig->comment_part; + + default: + return NULL; + } +} + + +unsigned long +gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what, + const void *reserved, int idx) +{ + struct certsig_s *certsig = get_certsig (key, uid_idx, idx); + + if (!certsig || reserved) + return 0; + + switch (what) + { + case GPGME_ATTR_ALGO: + return (unsigned long) certsig->algo; + + case GPGME_ATTR_CREATED: + return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp; + + case GPGME_ATTR_EXPIRE: + return certsig->expires_at < 0 ? 0L : (unsigned long) certsig->expires_at; + + case GPGME_ATTR_KEY_REVOKED: + return certsig->flags.revoked; + + case GPGME_ATTR_KEY_INVALID: + return certsig->flags.invalid; + + case GPGME_ATTR_KEY_EXPIRED: + return certsig->flags.expired; + + case GPGME_ATTR_SIG_CLASS: + return certsig->sig_class; + + case GPGME_ATTR_SIG_STATUS: + return certsig->sig_stat; + + default: + return 0; + } +} + + +/* Get the key with the fingerprint FPR from the key cache or from the + crypto backend. If FORCE_UPDATE is true, force a refresh of the + key from the crypto backend and replace the key in the cache, if + any. If SECRET is true, get the secret key. */ +GpgmeError +gpgme_get_key (GpgmeCtx ctx, const char *fpr, GpgmeKey *r_key, + int secret, int force_update) +{ + GpgmeCtx listctx; + GpgmeError err; + + if (!ctx || !r_key) + return mk_error (Invalid_Value); + if (ctx->pending) + return mk_error (Busy); + + if (strlen (fpr) < 16) /* We have at least a key ID. */ + return mk_error (Invalid_Key); + + if (!force_update) + { + *r_key = _gpgme_key_cache_get (fpr); + if (*r_key) + { + /* If the primary UID (if available) has no signatures, and + we are in the signature listing keylist mode, then try to + update the key below before returning. */ + if (!((ctx->keylist_mode & GPGME_KEYLIST_MODE_SIGS) + && (*r_key)->uids && !(*r_key)->uids->certsigs)) + return 0; + } + } + + /* Fixme: This can be optimized by keeping an internal context + used for such key listings. */ + err = gpgme_new (&listctx); + if (err) + return err; + gpgme_set_protocol (listctx, gpgme_get_protocol (ctx)); + gpgme_set_keylist_mode (listctx, ctx->keylist_mode); + err = gpgme_op_keylist_start (listctx, fpr, secret); + if (!err) + err = gpgme_op_keylist_next (listctx, r_key); + gpgme_release (listctx); + return err; +} diff --git a/gpgme/key.h b/gpgme/key.h index 2c1114e0..f2c4025a 100644 --- a/gpgme/key.h +++ b/gpgme/key.h @@ -1,23 +1,22 @@ -/* key.h - * 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 - */ +/* key.h - Key handling interface. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef KEY_H #define KEY_H @@ -26,68 +25,86 @@ #include "types.h" #include "context.h" -struct certsig_s { + +struct certsig_s +{ struct certsig_s *next; - struct { - unsigned int revoked:1 ; - unsigned int expired:1 ; - unsigned int invalid:1 ; + struct + { + unsigned int revoked : 1; + unsigned int expired : 1; + unsigned int invalid : 1; + unsigned int exportable : 1; } flags; - char keyid[16+1]; - time_t timestamp; /* -1 for invalid, 0 for not available */ - time_t expires_at; /* 0 for does not expires */ + unsigned int algo; + char keyid[16 + 1]; + time_t timestamp; /* -1 for invalid, 0 for not available. */ + time_t expires_at; /* 0 for no expiration. */ + GpgmeSigStat sig_stat; + unsigned int sig_class; + const char *name_part; /* All 3 point into strings behind name */ + const char *email_part; /* or to read-only strings. */ + const char *comment_part; + char name[1]; }; -struct subkey_s { + +struct subkey_s +{ struct subkey_s *next; unsigned int secret:1; - struct { - unsigned int revoked:1 ; - unsigned int expired:1 ; - unsigned int disabled:1 ; - unsigned int invalid:1 ; - unsigned int can_encrypt:1; - unsigned int can_sign:1; - unsigned int can_certify:1; + struct + { + unsigned int revoked : 1; + unsigned int expired : 1; + unsigned int disabled : 1; + unsigned int invalid : 1; + unsigned int can_encrypt : 1; + unsigned int can_sign : 1; + unsigned int can_certify : 1; } flags; unsigned int key_algo; unsigned int key_len; - char keyid[16+1]; - char *fingerprint; /* malloced hex digits */ - time_t timestamp; /* -1 for invalid, 0 for not available */ - time_t expires_at; /* 0 for does not expires */ + char keyid[16 + 1]; + char *fingerprint; /* Malloced hex digits. */ + time_t timestamp; /* -1 for invalid, 0 for not available. */ + time_t expires_at; /* 0 for does not expires. */ }; -struct gpgme_key_s { - struct { - unsigned int revoked:1 ; - unsigned int expired:1 ; - unsigned int disabled:1 ; - unsigned int invalid:1 ; - unsigned int can_encrypt:1; - unsigned int can_sign:1; - unsigned int can_certify:1; - } gloflags; + +struct gpgme_key_s +{ + struct + { + unsigned int revoked : 1; + unsigned int expired : 1; + unsigned int disabled : 1; + unsigned int invalid : 1; + unsigned int can_encrypt : 1; + unsigned int can_sign : 1; + unsigned int can_certify : 1; + } gloflags; unsigned int ref_count; - unsigned int secret:1; - unsigned int x509:1; - char *issuer_serial; /* malloced string used only with X.509 */ - char *issuer_name; /* ditto */ - char *chain_id; /* ditto */ - GpgmeValidity otrust; /* only used with OpenPGP */ - struct subkey_s keys; + unsigned int secret : 1; + unsigned int x509 : 1; + char *issuer_serial; /* Malloced string used only with X.509. */ + char *issuer_name; /* Ditto. */ + char *chain_id; /* Ditto. */ + GpgmeValidity otrust; /* Only used with OpenPGP. */ + struct subkey_s keys; struct user_id_s *uids; + struct user_id_s *last_uid; }; + void _gpgme_key_cache_init (void); void _gpgme_key_cache_add (GpgmeKey key); GpgmeKey _gpgme_key_cache_get (const char *fpr); +struct certsig_s *_gpgme_key_add_certsig (GpgmeKey key, char *src); struct subkey_s *_gpgme_key_add_subkey (GpgmeKey key); struct subkey_s *_gpgme_key_add_secret_subkey (GpgmeKey key); -GpgmeError _gpgme_key_append_name ( GpgmeKey key, const char *s ); +GpgmeError _gpgme_key_append_name (GpgmeKey key, const char *str); - - -#endif /* KEY_H */ +#endif /* KEY_H */ diff --git a/gpgme/keylist.c b/gpgme/keylist.c index 8c78a5f1..09cb89f0 100644 --- a/gpgme/keylist.c +++ b/gpgme/keylist.c @@ -1,46 +1,45 @@ -/* keylist.c - key listing - * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001, 2002 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 - */ +/* keylist.c - Listing keys. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H #include +#endif #include #include #include #include #include +#include #include "util.h" #include "context.h" #include "ops.h" #include "key.h" -#define my_isdigit(a) ( (a) >='0' && (a) <= '9' ) - + struct keylist_result_s { int truncated; GpgmeData xmlinfo; }; -static void finish_key ( GpgmeCtx ctx ); - void _gpgme_release_keylist_result (KeylistResult result) @@ -50,6 +49,7 @@ _gpgme_release_keylist_result (KeylistResult result) free (result); } + /* Append some XML info. args is currently ignore but we might want to add more information in the future (like source of the keylisting. With args of NULL the XML structure is closed. */ @@ -78,15 +78,11 @@ append_xml_keylistinfo (GpgmeData *rdh, char *args) return; } - _gpgme_data_append_string (dh, - " \n" - " \n" - ); + _gpgme_data_append_string (dh, " \n \n"); } - static void keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) { @@ -117,405 +113,209 @@ keylist_status_handler (GpgmeCtx ctx, GpgmeStatusCode code, char *args) } } - + static time_t -parse_timestamp (char *p) +parse_timestamp (char *timestamp) { - if (!*p) + if (!*timestamp) return 0; - return (time_t)strtoul (p, NULL, 10); + return (time_t) strtoul (timestamp, NULL, 10); } static void -set_mainkey_trust_info (GpgmeKey key, const char *s) +set_mainkey_trust_info (GpgmeKey key, const char *src) { /* Look at letters and stop at the first digit. */ - for (; *s && !my_isdigit (*s); s++) + while (*src && !isdigit (*src)) { - switch (*s) + switch (*src) { - 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; + 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; } + src++; } } static void -set_userid_flags (GpgmeKey key, const char *s) +set_userid_flags (GpgmeKey key, const char *src) { - struct user_id_s *u = key->uids; - - assert (u); - while (u->next) - u = u->next; + struct user_id_s *uid = key->last_uid; + assert (uid); /* Look at letters and stop at the first digit. */ - for (; *s && !my_isdigit (*s); s++) + while (*src && !isdigit (*src)) { - switch (*s) + switch (*src) { - case 'r': u->revoked = 1; break; - case 'i': u->invalid = 1; break; + case 'r': + uid->revoked = 1; + break; + + case 'i': + uid->invalid = 1; + break; - case 'n': u->validity = GPGME_VALIDITY_NEVER; break; - case 'm': u->validity = GPGME_VALIDITY_MARGINAL; break; - case 'f': u->validity = GPGME_VALIDITY_FULL; break; - case 'u': u->validity = GPGME_VALIDITY_ULTIMATE; break; + case 'n': + uid->validity = GPGME_VALIDITY_NEVER; + break; + + case 'm': + uid->validity = GPGME_VALIDITY_MARGINAL; + break; + + case 'f': + uid->validity = GPGME_VALIDITY_FULL; + break; + + case 'u': + uid->validity = GPGME_VALIDITY_ULTIMATE; + break; } + src++; } } static void -set_subkey_trust_info (struct subkey_s *k, const char *s) +set_subkey_trust_info (struct subkey_s *subkey, const char *src) { /* Look at letters and stop at the first digit. */ - for (; *s && !my_isdigit (*s); s++) + while (*src && !isdigit (*src)) { - switch (*s) + switch (*src) { - 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; + case 'e': + subkey->flags.expired = 1; + break; + + case 'r': + subkey->flags.revoked = 1; + break; + + case 'd': + subkey->flags.disabled = 1; + break; + + case 'i': + subkey->flags.invalid = 1; + break; } + src++; } } static void -set_mainkey_capability (GpgmeKey key, const char *s) +set_mainkey_capability (GpgmeKey key, const char *src) { - for (; *s ; s++) + while (*src) { - switch (*s) + switch (*src) { - 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; + 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; } + src++; } } static void -set_subkey_capability ( struct subkey_s *k, const char *s) +set_subkey_capability (struct subkey_s *subkey, const char *src) { - for (; *s; s++) + while (*src) { - switch (*s) + switch (*src) { - case 'e': k->flags.can_encrypt = 1; break; - case 's': k->flags.can_sign = 1; break; - case 'c': k->flags.can_certify = 1; break; + case 'e': + subkey->flags.can_encrypt = 1; + break; + + case 's': + subkey->flags.can_sign = 1; + break; + + case 'c': + subkey->flags.can_certify = 1; + break; } + src++; } } static void -set_ownertrust (GpgmeKey key, const char *s) +set_ownertrust (GpgmeKey key, const char *src) { /* Look at letters and stop at the first digit. */ - for (; *s && !my_isdigit (*s); s++) + while (*src && !isdigit (*src)) { - switch (*s) + switch (*src) { - case 'n': key->otrust = GPGME_VALIDITY_NEVER; break; - case 'm': key->otrust = GPGME_VALIDITY_MARGINAL; break; - case 'f': key->otrust = GPGME_VALIDITY_FULL; break; - case 'u': key->otrust = GPGME_VALIDITY_ULTIMATE; break; - default : key->otrust = GPGME_VALIDITY_UNKNOWN; break; + case 'n': + key->otrust = GPGME_VALIDITY_NEVER; + break; + + case 'm': + key->otrust = GPGME_VALIDITY_MARGINAL; + break; + + case 'f': + key->otrust = GPGME_VALIDITY_FULL; + break; + + case 'u': + key->otrust = GPGME_VALIDITY_ULTIMATE; + break; + + default: + key->otrust = GPGME_VALIDITY_UNKNOWN; + break; } + src++; } } -/* 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, - RT_CRT, RT_CRS - } - rectype = RT_NONE; - GpgmeKey key = ctx->tmp_key; - int i; - const char *trust_info = NULL; - struct subkey_s *sk = NULL; - - DEBUG3 ("keylist_colon_handler ctx=%p, key=%p, line=%s\n", ctx, key, - line? line: "(null)"); - if (ctx->error) - 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->error = mk_error (Out_Of_Core); - return; - } - } - else if (!strcmp (p, "ssb") && key) - { - /* Start a new secret subkey. */ - rectype = RT_SSB; - if (!(sk = _gpgme_key_add_secret_subkey (key))) - { - ctx->error = mk_error (Out_Of_Core); - return; - } - } - else if (!strcmp (p, "pub")) - { - /* Start a new keyblock. */ - if (_gpgme_key_new (&key)) - { - /* The only kind of error we can get. */ - ctx->error = mk_error (Out_Of_Core); - 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)) - { - /* The only kind of error we can get. */ - ctx->error = mk_error (Out_Of_Core); - return; - } - rectype = RT_SEC; - finish_key (ctx); - assert (!ctx->tmp_key); - ctx->tmp_key = key; - } - else if (!strcmp (p, "crt")) - { - /* Start a new certificate. */ - if (_gpgme_key_new (&key)) - { - /* The only kind of error we can get. */ - ctx->error = mk_error (Out_Of_Core); - return; - } - key->x509 = 1; - rectype = RT_CRT; - finish_key (ctx); - assert (!ctx->tmp_key); - ctx->tmp_key = key; - } - else if (!strcmp (p, "crs")) - { - /* Start a new certificate. */ - if (_gpgme_key_new_secret (&key)) - { - /* The only kind of error we can get. */ - ctx->error = mk_error (Out_Of_Core); - return; - } - key->x509 = 1; - rectype = RT_CRS; - 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 - || rectype == RT_CRT || rectype == RT_CRS) - { - 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: /* expiration time (seconds) */ - key->keys.expires_at = parse_timestamp (p); - break; - case 8: /* X.509 serial number */ - if (rectype == RT_CRT || rectype == RT_CRS) - { - key->issuer_serial = strdup (p); - if (!key->issuer_serial) - ctx->error = mk_error (Out_Of_Core); - } - break; - case 9: /* ownertrust */ - set_ownertrust (key, p); - break; - case 10: - /* Not used for gpg due to --fixed-list-mode option but - GPGSM stores the issuer name. */ - if (rectype == RT_CRT || rectype == RT_CRS) - if (_gpgme_decode_c_string (p, &key->issuer_name)) - ctx->error = mk_error (Out_Of_Core); - 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: /* expiration time (seconds) */ - sk->expires_at = parse_timestamp (p); - 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)) - /* The only kind of error we can get*/ - ctx->error = mk_error (Out_Of_Core); - 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 = strdup (p); - if (!key->keys.fingerprint) - ctx->error = mk_error (Out_Of_Core); - } - break; - case 13: /* gpgsm chain ID (take only the first one)*/ - if (!key->chain_id && *p) - { - key->chain_id = strdup (p); - if (!key->chain_id) - ctx->error = mk_error (Out_Of_Core); - } - 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. - */ +/* 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) { @@ -528,6 +328,365 @@ finish_key (GpgmeCtx ctx) } +/* Note: We are allowed to modify LINE. */ +static void +keylist_colon_handler (GpgmeCtx ctx, char *line) +{ + enum + { + RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_SSB, RT_SEC, + RT_CRT, RT_CRS, RT_REV + } + rectype = RT_NONE; +#define NR_FIELDS 13 + char *field[NR_FIELDS]; + int fields = 0; + GpgmeKey key = ctx->tmp_key; + struct subkey_s *subkey = NULL; + struct certsig_s *certsig = NULL; + + DEBUG3 ("keylist_colon_handler ctx = %p, key = %p, line = %s\n", + ctx, key, line ? line : "(null)"); + + if (ctx->error) + return; + + if (!line) + { + /* End Of File. */ + finish_key (ctx); + return; + } + + while (line && fields < NR_FIELDS) + { + field[fields++] = line; + line = strchr (line, ':'); + if (line) + *(line++) = '\0'; + } + + if (!strcmp (field[0], "sig")) + rectype = RT_SIG; + else if (!strcmp (field[0], "rev")) + rectype = RT_REV; + else if (!strcmp (field[0], "uid") && key) + rectype = RT_UID; + else if (!strcmp (field[0], "sub") && key) + { + /* Start a new subkey. */ + rectype = RT_SUB; + if (!(subkey = _gpgme_key_add_subkey (key))) + { + ctx->error = mk_error (Out_Of_Core); + return; + } + } + else if (!strcmp (field[0], "ssb") && key) + { + /* Start a new secret subkey. */ + rectype = RT_SSB; + if (!(subkey = _gpgme_key_add_secret_subkey (key))) + { + ctx->error = mk_error (Out_Of_Core); + return; + } + } + else if (!strcmp (field[0], "pub")) + { + /* Start a new keyblock. */ + if (_gpgme_key_new (&key)) + { + /* The only kind of error we can get. */ + ctx->error = mk_error (Out_Of_Core); + return; + } + rectype = RT_PUB; + finish_key (ctx); + assert (!ctx->tmp_key); + ctx->tmp_key = key; + } + else if (!strcmp (field[0], "sec")) + { + /* Start a new keyblock, */ + if (_gpgme_key_new_secret (&key)) + { + /* The only kind of error we can get. */ + ctx->error = mk_error (Out_Of_Core); + return; + } + rectype = RT_SEC; + finish_key (ctx); + assert (!ctx->tmp_key); + ctx->tmp_key = key; + } + else if (!strcmp (field[0], "crt")) + { + /* Start a new certificate. */ + if (_gpgme_key_new (&key)) + { + /* The only kind of error we can get. */ + ctx->error = mk_error (Out_Of_Core); + return; + } + key->x509 = 1; + rectype = RT_CRT; + finish_key (ctx); + assert (!ctx->tmp_key); + ctx->tmp_key = key; + } + else if (!strcmp (field[0], "crs")) + { + /* Start a new certificate. */ + if (_gpgme_key_new_secret (&key)) + { + /* The only kind of error we can get. */ + ctx->error = mk_error (Out_Of_Core); + return; + } + key->x509 = 1; + rectype = RT_CRS; + finish_key (ctx); + assert (!ctx->tmp_key); + ctx->tmp_key = key; + } + else if (!strcmp (field[0], "fpr") && key) + rectype = RT_FPR; + else + rectype = RT_NONE; + + /* Only look at signatures immediately following a user ID. For + this, clear the user ID pointer when encountering anything but a + signature. */ + if (rectype != RT_SIG && rectype != RT_REV) + ctx->tmp_uid = NULL; + + switch (rectype) + { + case RT_CRT: + case RT_CRS: + /* Field 8 has the X.509 serial number. */ + if (fields >= 8) + { + key->issuer_serial = strdup (field[7]); + if (!key->issuer_serial) + ctx->error = mk_error (Out_Of_Core); + } + + /* Field 10 is not used for gpg due to --fixed-list-mode option + but GPGSM stores the issuer name. */ + if (fields >= 10 && _gpgme_decode_c_string (field[9], + &key->issuer_name, 0)) + ctx->error = mk_error (Out_Of_Core); + /* Fall through! */ + + case RT_PUB: + case RT_SEC: + /* Field 2 has the trust info. */ + if (fields >= 2) + set_mainkey_trust_info (key, field[1]); + + /* Field 3 has the key length. */ + if (fields >= 3) + { + int i = atoi (field[2]); + /* Ignore invalid values. */ + if (i > 1) + key->keys.key_len = i; + } + + /* Field 4 has the public key algorithm. */ + if (fields >= 4) + { + int i = atoi (field[3]); + if (i >= 1 && i < 128) + key->keys.key_algo = i; + } + + /* Field 5 has the long keyid. */ + if (fields >= 5 && strlen (field[4]) == DIM(key->keys.keyid) - 1) + strcpy (key->keys.keyid, field[4]); + + /* Field 6 has the timestamp (seconds). */ + if (fields >= 6) + key->keys.timestamp = parse_timestamp (field[5]); + + /* Field 7 has the expiration time (seconds). */ + if (fields >= 7) + key->keys.expires_at = parse_timestamp (field[6]); + + /* Field 9 has the ownertrust. */ + if (fields >= 9) + set_ownertrust (key, field[8]); + + /* Field 11 has the signature class. */ + + /* Field 12 has the capabilities. */ + if (fields >= 12) + set_mainkey_capability (key, field[11]); + break; + + case RT_SUB: + case RT_SSB: + /* Field 2 has the trust info. */ + if (fields >= 2) + set_subkey_trust_info (subkey, field[1]); + + /* Field 3 has the key length. */ + if (fields >= 3) + { + int i = atoi (field[2]); + /* Ignore invalid values. */ + if (i > 1) + subkey->key_len = i; + } + + /* Field 4 has the public key algorithm. */ + if (fields >= 4) + { + int i = atoi (field[3]); + if (i >= 1 && i < 128) + subkey->key_algo = i; + } + + /* Field 5 has the long keyid. */ + if (fields >= 5 && strlen (field[4]) == DIM(subkey->keyid) - 1) + strcpy (subkey->keyid, field[4]); + + /* Field 6 has the timestamp (seconds). */ + if (fields >= 6) + subkey->timestamp = parse_timestamp (field[5]); + + /* Field 7 has the expiration time (seconds). */ + if (fields >= 7) + subkey->expires_at = parse_timestamp (field[6]); + + /* Field 8 is reserved (LID). */ + /* Field 9 has the ownertrust. */ + /* Field 10, the user ID, is n/a for a subkey. */ + + /* Field 11 has the signature class. */ + + /* Field 12 has the capabilities. */ + if (fields >= 12) + set_subkey_capability (subkey, field[11]); + break; + + case RT_UID: + /* Field 2 has the trust info, and field 10 has the user ID. */ + if (fields >= 10) + { + if (_gpgme_key_append_name (key, field[9])) + ctx->error = mk_error (Out_Of_Core); + else + { + if (field[1]) + set_userid_flags (key, field[1]); + ctx->tmp_uid = key->last_uid; + } + } + break; + + case RT_FPR: + /* Field 10 has the fingerprint (take only the first one). */ + if (fields >= 10 && !key->keys.fingerprint && field[9] && *field[9]) + { + key->keys.fingerprint = strdup (field[9]); + if (!key->keys.fingerprint) + ctx->error = mk_error (Out_Of_Core); + } + + /* Field 13 has the gpgsm chain ID (take only the first one). */ + if (fields >= 13 && !key->chain_id && *field[12]) + { + key->chain_id = strdup (field[12]); + if (!key->chain_id) + ctx->error = mk_error (Out_Of_Core); + } + break; + + case RT_SIG: + case RT_REV: + if (!ctx->tmp_uid) + return; + + /* Start a new (revoked) signature. */ + assert (ctx->tmp_uid == key->last_uid); + certsig = _gpgme_key_add_certsig (key, (fields >= 10) ? field[9] : NULL); + if (!certsig) + { + ctx->error = mk_error (Out_Of_Core); + return; + } + + /* Field 2 has the calculated trust ('!', '-', '?', '%'). */ + if (fields >= 2) + switch (field[1][0]) + { + case '!': + certsig->sig_stat = GPGME_SIG_STAT_GOOD; + break; + + case '-': + certsig->sig_stat = GPGME_SIG_STAT_BAD; + break; + + case '?': + certsig->sig_stat = GPGME_SIG_STAT_NOKEY; + break; + + case '%': + certsig->sig_stat = GPGME_SIG_STAT_ERROR; + break; + + default: + certsig->sig_stat = GPGME_SIG_STAT_NONE; + break; + } + + /* Field 4 has the public key algorithm. */ + if (fields >= 4) + { + int i = atoi (field[3]); + if (i >= 1 && i < 128) + certsig->algo = i; + } + + /* Field 5 has the long keyid. */ + if (fields >= 5 && strlen (field[4]) == DIM(certsig->keyid) - 1) + strcpy (certsig->keyid, field[4]); + + /* Field 6 has the timestamp (seconds). */ + if (fields >= 6) + certsig->timestamp = parse_timestamp (field[5]); + + /* Field 7 has the expiration time (seconds). */ + if (fields >= 7) + certsig->expires_at = parse_timestamp (field[6]); + + /* Field 11 has the signature class (eg, 0x30 means revoked). */ + if (fields >= 11) + if (field[10][0] && field[10][1]) + { + int class = _gpgme_hextobyte (field[10]); + if (class >= 0) + { + certsig->sig_class = class; + if (class == 0x30) + certsig->flags.revoked = 1; + } + if (field[10][2] == 'x') + certsig->flags.exportable = 1; + } + break; + + case RT_NONE: + /* Unknown record. */ + break; + } +} + + void _gpgme_op_keylist_event_cb (void *data, GpgmeEventIO type, void *type_data) { @@ -593,9 +752,9 @@ gpgme_op_keylist_start (GpgmeCtx ctx, const char *pattern, int secret_only) if (err) goto leave; - /* We don't want to use the verbose mode as this will also print - the key signatures which is in most cases not needed and furthermore we - just ignore those lines - This should speed up things */ + /* We don't want to use the verbose mode as this will also print the + key signatures which is in most cases not needed and furthermore + we just ignore those lines - This should speed up things. */ _gpgme_engine_set_verbosity (ctx->engine, 0); err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only, @@ -647,15 +806,16 @@ gpgme_op_keylist_ext_start (GpgmeCtx ctx, const char *pattern[], if (err) goto leave; - /* We don't want to use the verbose mode as this will also print - the key signatures which is in most cases not needed and furthermore we - just ignore those lines - This should speed up things */ + /* We don't want to use the verbose mode as this will also print the + key signatures which is in most cases not needed and furthermore + we just ignore those lines - This should speed up things. */ _gpgme_engine_set_verbosity (ctx->engine, 0); err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only, reserved, ctx->keylist_mode); - if (!err) /* And kick off the process. */ + /* And kick off the process. */ + if (!err) err = _gpgme_engine_start (ctx->engine, ctx); leave: diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index 9a556cee..88afaea9 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1486,10 +1486,9 @@ gpg_keylist (void *engine, const char *pattern, int secret_only, if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) - err = add_arg (gpg, (keylist_mode & GPGME_KEYLIST_MODE_SIGS) ? - "--check-sigs" : - secret_only ? "--list-secret-keys" - : "--list-keys"); + err = add_arg (gpg, secret_only ? "--list-secret-keys" + : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS) + ? "--check-sigs" : "--list-keys")); /* Tell the gpg object about the data. */ if (!err) @@ -1519,8 +1518,10 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) - err = add_arg (gpg, secret_only ? "--list-secret-keys" : "--list-keys"); - + err = add_arg (gpg, secret_only ? "--list-secret-keys" + : ((keylist_mode & GPGME_KEYLIST_MODE_SIGS) + ? "--check-sigs" : "--list-keys")); + /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); diff --git a/gpgme/util.h b/gpgme/util.h index c28c1cb7..6d99cad3 100644 --- a/gpgme/util.h +++ b/gpgme/util.h @@ -85,7 +85,13 @@ FILE *fopencookie (void *cookie, const char *opentype, /*-- conversion.c --*/ -GpgmeError _gpgme_decode_c_string (const char *src, char **destp); +/* Decode the C formatted string SRC and store the result in the + buffer *DESTP which is LEN bytes long. If LEN is zero, then a + large enough buffer is allocated with malloc and *DESTP is set to + the result. Currently, LEN is only used to specify if allocation + is desired or not, the caller is expected to make sure that *DESTP + is large enough if LEN is not zero. */ +GpgmeError _gpgme_decode_c_string (const char *src, char **destp, int len); int _gpgme_hextobyte (const byte *str); #endif /* UTIL_H */ diff --git a/gpgme/verify.c b/gpgme/verify.c index 1dcc0eec..bc2a787b 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -643,42 +643,20 @@ gpgme_get_sig_ulong_attr (GpgmeCtx c, int idx, GpgmeAttr what, int reserved) * indicate that there are no more signatures. **/ GpgmeError -gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key) +gpgme_get_sig_key (GpgmeCtx ctx, int idx, GpgmeKey *r_key) { VerifyResult result; - GpgmeError err = 0; - if (!c || !r_key) + if (!ctx || !r_key) return mk_error (Invalid_Value); - if (c->pending || !c->result.verify) + if (ctx->pending || !ctx->result.verify) return mk_error (Busy); - for (result = c->result.verify; + for (result = ctx->result.verify; result && idx > 0; result = result->next, idx--) ; if (!result) return mk_error (EOF); - - if (strlen(result->fpr) < 16) /* We have at least a key ID. */ - return mk_error (Invalid_Key); - - *r_key = _gpgme_key_cache_get (result->fpr); - if (!*r_key) - { - GpgmeCtx listctx; - - /* Fixme: This can be optimized by keeping an internal context - used for such key listings. */ - err = gpgme_new (&listctx); - if (err) - return err; - gpgme_set_protocol (listctx, gpgme_get_protocol (c)); - gpgme_set_keylist_mode (listctx, c->keylist_mode); - err = gpgme_op_keylist_start (listctx, result->fpr, 0); - if (!err) - err = gpgme_op_keylist_next (listctx, r_key); - gpgme_release (listctx); - } - return err; -} + return gpgme_get_key (ctx, result->fpr, r_key, 0, 0); +}