NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
pgpkey.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdbool.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <string.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include "private.h"
39#include "mutt/lib.h"
40#include "address/lib.h"
41#include "config/lib.h"
42#include "email/lib.h"
43#include "core/lib.h"
44#include "mutt.h"
45#include "pgpkey.h"
46#include "lib.h"
47#include "enter/lib.h"
48#include "send/lib.h"
49#include "crypt.h"
50#include "globals.h" // IWYU pragma: keep
51#include "gnupgparse.h"
52#include "mutt_logging.h"
53#include "muttlib.h"
54#include "pgpinvoke.h"
55#ifdef CRYPT_BACKEND_CLASSIC_PGP
56#include "pgp.h"
57#include "pgplib.h"
58#endif
59
64{
65 char *what;
66 char *dflt;
67 struct PgpCache *next;
68};
69
70static struct PgpCache *id_defaults = NULL;
71
72// clang-format off
73typedef uint8_t PgpKeyValidFlags;
74#define PGP_KV_NO_FLAGS 0
75#define PGP_KV_VALID (1 << 0)
76#define PGP_KV_ADDR (1 << 1)
77#define PGP_KV_STRING (1 << 2)
78#define PGP_KV_STRONGID (1 << 3)
79// clang-format on
80
81#define PGP_KV_MATCH (PGP_KV_ADDR | PGP_KV_STRING)
82
89{
90 if (key->flags & KEYFLAG_SUBKEY && key->parent)
91 return key->parent;
92 return key;
93}
94
101{
102 struct PgpKeyInfo *pk = pgp_principal_key(k);
103 if (k->flags & KEYFLAG_CANTUSE)
104 return false;
105 if (pk->flags & KEYFLAG_CANTUSE)
106 return false;
107
108 return true;
109}
110
116bool pgp_id_is_strong(struct PgpUid *uid)
117{
118 if ((uid->trust & 3) < 3)
119 return false;
120 /* else */
121 return true;
122}
123
129bool pgp_id_is_valid(struct PgpUid *uid)
130{
131 if (!pgp_key_is_valid(uid->parent))
132 return false;
133 if (uid->flags & KEYFLAG_CANTUSE)
134 return false;
135 /* else */
136 return true;
137}
138
147 struct Address *u_addr, struct PgpUid *uid)
148{
150
151 if (pgp_id_is_valid(uid))
153
154 if (pgp_id_is_strong(uid))
156
157 if (addr->mailbox && u_addr->mailbox && mutt_istr_equal(addr->mailbox, u_addr->mailbox))
158 {
160 }
161
162 if (addr->personal && u_addr->personal &&
163 mutt_istr_equal(addr->personal, u_addr->personal))
164 {
166 }
167
168 return flags;
169}
170
179struct PgpKeyInfo *pgp_ask_for_key(char *tag, char *whatfor, KeyFlags abilities,
180 enum PgpRing keyring)
181{
182 struct PgpKeyInfo *key = NULL;
183 struct PgpCache *l = NULL;
184 struct Buffer *resp = mutt_buffer_pool_get();
185
187
188 if (whatfor)
189 {
190 for (l = id_defaults; l; l = l->next)
191 {
192 if (mutt_istr_equal(whatfor, l->what))
193 {
194 mutt_buffer_strcpy(resp, l->dflt);
195 break;
196 }
197 }
198 }
199
200 while (true)
201 {
202 mutt_buffer_reset(resp);
203 if (mutt_buffer_get_field(tag, resp, MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) != 0)
204 {
205 goto done;
206 }
207
208 if (whatfor)
209 {
210 if (l)
212 else
213 {
214 l = mutt_mem_malloc(sizeof(struct PgpCache));
215 l->next = id_defaults;
216 id_defaults = l;
217 l->what = mutt_str_dup(whatfor);
218 l->dflt = mutt_buffer_strdup(resp);
219 }
220 }
221
222 key = pgp_getkeybystr(mutt_buffer_string(resp), abilities, keyring);
223 if (key)
224 goto done;
225
226 mutt_error(_("No matching keys found for \"%s\""), mutt_buffer_string(resp));
227 }
228
229done:
231 return key;
232}
233
238{
239 struct Body *att = NULL;
240 char buf[1024] = { 0 };
241 char tmp[256] = { 0 };
242 struct stat st = { 0 };
243 pid_t pid;
244 OptPgpCheckTrust = false;
245 struct Buffer *tempf = NULL;
246
247 struct PgpKeyInfo *key = pgp_ask_for_key(_("Please enter the key ID: "), NULL,
249
250 if (!key)
251 return NULL;
252
253 snprintf(tmp, sizeof(tmp), "0x%s", pgp_fpr_or_lkeyid(pgp_principal_key(key)));
254 pgp_key_free(&key);
255
256 tempf = mutt_buffer_pool_get();
257 mutt_buffer_mktemp(tempf);
258 FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tempf), "w");
259 if (!fp_tmp)
260 {
261 mutt_perror(_("Can't create temporary file"));
262 goto cleanup;
263 }
264
265 FILE *fp_null = fopen("/dev/null", "w");
266 if (!fp_null)
267 {
268 mutt_perror(_("Can't open /dev/null"));
269 mutt_file_fclose(&fp_tmp);
270 unlink(mutt_buffer_string(tempf));
271 goto cleanup;
272 }
273
274 mutt_message(_("Invoking PGP..."));
275
276 pid = pgp_invoke_export(NULL, NULL, NULL, -1, fileno(fp_tmp), fileno(fp_null), tmp);
277 if (pid == -1)
278 {
279 mutt_perror(_("Can't create filter"));
280 unlink(mutt_buffer_string(tempf));
281 mutt_file_fclose(&fp_tmp);
282 mutt_file_fclose(&fp_null);
283 goto cleanup;
284 }
285
286 filter_wait(pid);
287
288 mutt_file_fclose(&fp_tmp);
289 mutt_file_fclose(&fp_null);
290
291 att = mutt_body_new();
292 att->filename = mutt_buffer_strdup(tempf);
293 att->unlink = true;
294 att->use_disp = false;
295 att->type = TYPE_APPLICATION;
296 att->subtype = mutt_str_dup("pgp-keys");
297 snprintf(buf, sizeof(buf), _("PGP Key %s"), tmp);
298 att->description = mutt_str_dup(buf);
300
301 stat(mutt_buffer_string(tempf), &st);
302 att->length = st.st_size;
303
304cleanup:
306 return att;
307}
308
317static void pgp_add_string_to_hints(const char *str, struct ListHead *hints)
318{
319 char *scratch = mutt_str_dup(str);
320 if (!scratch)
321 return;
322
323 for (char *t = strtok(scratch, " ,.:\"()<>\n"); t; t = strtok(NULL, " ,.:\"()<>\n"))
324 {
325 if (strlen(t) > 3)
327 }
328
329 FREE(&scratch);
330}
331
337static struct PgpKeyInfo **pgp_get_lastp(struct PgpKeyInfo *p)
338{
339 for (; p; p = p->next)
340 if (!p->next)
341 return &p->next;
342
343 return NULL;
344}
345
354struct PgpKeyInfo *pgp_getkeybyaddr(struct Address *a, KeyFlags abilities,
355 enum PgpRing keyring, bool oppenc_mode)
356{
357 if (!a)
358 return NULL;
359
360 struct ListHead hints = STAILQ_HEAD_INITIALIZER(hints);
361
362 bool multi = false;
363
364 struct PgpKeyInfo *keys = NULL, *k = NULL, *kn = NULL;
365 struct PgpKeyInfo *the_strong_valid_key = NULL;
366 struct PgpKeyInfo *a_valid_addrmatch_key = NULL;
367 struct PgpKeyInfo *matches = NULL;
368 struct PgpKeyInfo **last = &matches;
369 struct PgpUid *q = NULL;
370
371 if (a->mailbox)
373 if (a->personal)
375
376 if (!oppenc_mode)
377 mutt_message(_("Looking for keys matching \"%s\"..."), a->mailbox);
378 keys = pgp_get_candidates(keyring, &hints);
379
380 mutt_list_free(&hints);
381
382 if (!keys)
383 return NULL;
384
385 mutt_debug(LL_DEBUG5, "looking for %s <%s>\n", NONULL(a->personal), NONULL(a->mailbox));
386
387 for (k = keys; k; k = kn)
388 {
389 kn = k->next;
390
391 mutt_debug(LL_DEBUG5, " looking at key: %s\n", pgp_keyid(k));
392
393 if (abilities && !(k->flags & abilities))
394 {
395 mutt_debug(LL_DEBUG3, " insufficient abilities: Has %x, want %x\n", k->flags, abilities);
396 continue;
397 }
398
399 bool match = false; /* any match */
400
401 for (q = k->address; q; q = q->next)
402 {
403 struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
405 struct Address *qa = NULL;
406 TAILQ_FOREACH(qa, &al, entries)
407 {
408 PgpKeyValidFlags validity = pgp_id_matches_addr(a, qa, q);
409
410 if (validity & PGP_KV_MATCH) /* something matches */
411 match = true;
412
413 if ((validity & PGP_KV_VALID) && (validity & PGP_KV_ADDR))
414 {
415 if (validity & PGP_KV_STRONGID)
416 {
417 if (the_strong_valid_key && (the_strong_valid_key != k))
418 multi = true;
419 the_strong_valid_key = k;
420 }
421 else
422 {
423 a_valid_addrmatch_key = k;
424 }
425 }
426 }
427
429 }
430
431 if (match)
432 {
433 *last = pgp_principal_key(k);
434 kn = pgp_remove_key(&keys, *last);
435 last = pgp_get_lastp(k);
436 }
437 }
438
439 pgp_key_free(&keys);
440
441 if (matches)
442 {
443 if (oppenc_mode)
444 {
445 const bool c_crypt_opportunistic_encrypt_strong_keys =
446 cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt_strong_keys");
447 if (the_strong_valid_key)
448 {
449 pgp_remove_key(&matches, the_strong_valid_key);
450 k = the_strong_valid_key;
451 }
452 else if (a_valid_addrmatch_key && !c_crypt_opportunistic_encrypt_strong_keys)
453 {
454 pgp_remove_key(&matches, a_valid_addrmatch_key);
455 k = a_valid_addrmatch_key;
456 }
457 else
458 k = NULL;
459 }
460 else if (the_strong_valid_key && !multi)
461 {
462 /* There was precisely one strong match on a valid ID.
463 * Proceed without asking the user. */
464 pgp_remove_key(&matches, the_strong_valid_key);
465 k = the_strong_valid_key;
466 }
467 else
468 {
469 /* Else: Ask the user. */
470 k = dlg_select_pgp_key(matches, a, NULL);
471 if (k)
472 pgp_remove_key(&matches, k);
473 }
474
475 pgp_key_free(&matches);
476
477 return k;
478 }
479
480 return NULL;
481}
482
490struct PgpKeyInfo *pgp_getkeybystr(const char *cp, KeyFlags abilities, enum PgpRing keyring)
491{
492 struct ListHead hints = STAILQ_HEAD_INITIALIZER(hints);
493 struct PgpKeyInfo *keys = NULL;
494 struct PgpKeyInfo *matches = NULL;
495 struct PgpKeyInfo **last = &matches;
496 struct PgpKeyInfo *k = NULL, *kn = NULL;
497 struct PgpUid *a = NULL;
498 size_t l;
499 const char *ps = NULL, *pl = NULL, *pfcopy = NULL, *phint = NULL;
500
501 char *p = strdup(cp); // mutt_str_dup converts "" into NULL, see #1809
502 l = mutt_str_len(p);
503 if ((l > 0) && (p[l - 1] == '!'))
504 p[l - 1] = 0;
505
506 mutt_message(_("Looking for keys matching \"%s\"..."), p);
507
508 pfcopy = crypt_get_fingerprint_or_id(p, &phint, &pl, &ps);
509 pgp_add_string_to_hints(phint, &hints);
510 keys = pgp_get_candidates(keyring, &hints);
511 mutt_list_free(&hints);
512
513 for (k = keys; k; k = kn)
514 {
515 kn = k->next;
516 if (abilities && !(k->flags & abilities))
517 continue;
518
519 /* This shouldn't happen, but keys without any addresses aren't selectable
520 * in dlg_select_pgp_key(). */
521 if (!k->address)
522 continue;
523
524 bool match = false;
525
526 mutt_debug(LL_DEBUG5, "matching \"%s\" against key %s:\n", p, pgp_long_keyid(k));
527
528 if ((*p == '\0') || (pfcopy && mutt_istr_equal(pfcopy, k->fingerprint)) ||
529 (pl && mutt_istr_equal(pl, pgp_long_keyid(k))) ||
530 (ps && mutt_istr_equal(ps, pgp_short_keyid(k))))
531 {
532 mutt_debug(LL_DEBUG5, " match #1\n");
533 match = true;
534 }
535 else
536 {
537 for (a = k->address; a; a = a->next)
538 {
539 mutt_debug(LL_DEBUG5, "matching \"%s\" against key %s, \"%s\":\n", p,
540 pgp_long_keyid(k), NONULL(a->addr));
541 if (mutt_istr_find(a->addr, p))
542 {
543 mutt_debug(LL_DEBUG5, " match #2\n");
544 match = true;
545 break;
546 }
547 }
548 }
549
550 if (match)
551 {
552 *last = pgp_principal_key(k);
553 kn = pgp_remove_key(&keys, *last);
554 last = pgp_get_lastp(k);
555 }
556 }
557
558 pgp_key_free(&keys);
559
560 if (matches)
561 {
562 k = dlg_select_pgp_key(matches, NULL, p);
563 if (k)
564 pgp_remove_key(&matches, k);
565 pgp_key_free(&matches);
566 }
567 else
568 {
569 k = NULL;
570 }
571
572 FREE(&pfcopy);
573 FREE(&p);
574 return k;
575}
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1435
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
Email Address Handling.
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:365
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:485
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
const char * crypt_get_fingerprint_or_id(const char *p, const char **pphint, const char **ppl, const char **pps)
Get the fingerprint or long key ID.
Definition: crypt.c:1262
Signing/encryption multiplexor.
struct PgpKeyInfo * dlg_select_pgp_key(struct PgpKeyInfo *keys, struct Address *p, const char *s)
Let the user select a key to use.
Definition: dlg_pgp.c:586
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
Structs that make up an email.
Enter a string.
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: window.c:178
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
bool OptPgpCheckTrust
(pseudo) used by dlg_select_pgp_key()
Definition: globals.c:82
struct PgpKeyInfo * pgp_get_candidates(enum PgpRing keyring, struct ListHead *hints)
Find PGP keys matching a list of hints.
Definition: gnupgparse.c:415
Parse the output of CLI PGP programinclude "pgpkey.h".
struct Body * pgp_class_make_key_attachment(void)
Implements CryptModuleSpecs::pgp_make_key_attachment() -.
Definition: pgpkey.c:237
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:592
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
Many unsorted constants and some structs.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:55
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
uint16_t KeyFlags
Flags describing PGP/SMIME keys, e.g. KEYFLAG_CANSIGN.
Definition: lib.h:125
#define KEYFLAG_SUBKEY
Key is a subkey.
Definition: lib.h:134
#define KEYFLAG_CANTUSE
Definition: lib.h:139
#define KEYFLAG_NO_FLAGS
No flags are set.
Definition: lib.h:126
char * pgp_long_keyid(struct PgpKeyInfo *k)
Get a key's long id.
Definition: pgp.c:165
char * pgp_keyid(struct PgpKeyInfo *k)
Get the ID of the main (parent) key.
Definition: pgp.c:204
char * pgp_fpr_or_lkeyid(struct PgpKeyInfo *k)
Get the fingerprint or long keyid.
Definition: pgp.c:234
char * pgp_short_keyid(struct PgpKeyInfo *k)
Get a key's short id.
Definition: pgp.c:177
PGP sign, encrypt, check routines.
pid_t pgp_invoke_export(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err, int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, const char *uids)
Use PGP to export a key from the user's keyring.
Definition: pgpinvoke.c:495
Wrapper around calls to external PGP program.
bool pgp_id_is_valid(struct PgpUid *uid)
Is a PGP key valid.
Definition: pgpkey.c:129
#define PGP_KV_NO_FLAGS
No flags are set.
Definition: pgpkey.c:74
struct PgpKeyInfo * pgp_ask_for_key(char *tag, char *whatfor, KeyFlags abilities, enum PgpRing keyring)
Ask the user for a PGP key.
Definition: pgpkey.c:179
#define PGP_KV_STRONGID
PGP Key is strong.
Definition: pgpkey.c:78
static void pgp_add_string_to_hints(const char *str, struct ListHead *hints)
Split a string and add the parts to a List.
Definition: pgpkey.c:317
#define PGP_KV_MATCH
Definition: pgpkey.c:81
static PgpKeyValidFlags pgp_id_matches_addr(struct Address *addr, struct Address *u_addr, struct PgpUid *uid)
Does the key ID match the address.
Definition: pgpkey.c:146
static struct PgpCache * id_defaults
Definition: pgpkey.c:70
#define PGP_KV_VALID
PGP Key ID is valid.
Definition: pgpkey.c:75
#define PGP_KV_STRING
PGP Key name string is valid.
Definition: pgpkey.c:77
#define PGP_KV_ADDR
PGP Key address is valid.
Definition: pgpkey.c:76
static struct PgpKeyInfo ** pgp_get_lastp(struct PgpKeyInfo *p)
Get the last PGP key in a list.
Definition: pgpkey.c:337
bool pgp_id_is_strong(struct PgpUid *uid)
Is a PGP key strong?
Definition: pgpkey.c:116
struct PgpKeyInfo * pgp_getkeybyaddr(struct Address *a, KeyFlags abilities, enum PgpRing keyring, bool oppenc_mode)
Find a PGP key by address.
Definition: pgpkey.c:354
bool pgp_key_is_valid(struct PgpKeyInfo *k)
Is a PGP key valid?
Definition: pgpkey.c:100
struct PgpKeyInfo * pgp_getkeybystr(const char *cp, KeyFlags abilities, enum PgpRing keyring)
Find a PGP key by string.
Definition: pgpkey.c:490
uint8_t PgpKeyValidFlags
Flags for valid Pgp Key fields, e.g. PGP_KV_VALID.
Definition: pgpkey.c:73
struct PgpKeyInfo * pgp_principal_key(struct PgpKeyInfo *key)
Get the main (parent) PGP key.
Definition: pgpkey.c:88
PGP key management routines.
PgpRing
PGP ring type.
Definition: pgpkey.h:38
@ PGP_PUBRING
Public keys.
Definition: pgpkey.h:39
void pgp_key_free(struct PgpKeyInfo **kpp)
Free a PGP key info.
Definition: pgplib.c:199
struct PgpKeyInfo * pgp_remove_key(struct PgpKeyInfo **klist, struct PgpKeyInfo *key)
Remove a PGP key from a list.
Definition: pgplib.c:167
Misc PGP helper routines.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
Convenience wrapper for the send headers.
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:411
GUI display the mailboxes in a side panel.
Key value store.
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
char * mailbox
Mailbox and host address.
Definition: address.h:38
char * personal
Real name of address.
Definition: address.h:37
The body of an email.
Definition: body.h:36
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:67
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:47
char * description
content-description
Definition: body.h:55
char * subtype
content-type subtype
Definition: body.h:60
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
String manipulation buffer.
Definition: buffer.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
List of cached PGP keys.
Definition: pgpkey.c:64
char * dflt
Definition: pgpkey.c:66
struct PgpCache * next
Linked list.
Definition: pgpkey.c:67
char * what
Definition: pgpkey.c:65
Information about a PGP key.
Definition: pgplib.h:47
KeyFlags flags
Definition: pgplib.h:51
struct PgpKeyInfo * next
Definition: pgplib.h:57
struct PgpUid * address
Definition: pgplib.h:50
char * fingerprint
Definition: pgplib.h:49
struct PgpKeyInfo * parent
Definition: pgplib.h:56
PGP User ID.
Definition: pgplib.h:35
short trust
Definition: pgplib.h:37
struct PgpKeyInfo * parent
Parent key.
Definition: pgplib.h:39
int flags
Definition: pgplib.h:38
char * addr
Definition: pgplib.h:36
struct PgpUid * next
Linked list.
Definition: pgplib.h:40