--- /dev/null
+/*
+ * Copyright notice from original mutt:
+ * Copyright (C) 1998-2000 Werner Koch <werner.koch@guug.de>
+ * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
+ *
+ * This file is part of mutt-ng, see http://www.muttng.org/.
+ * It's licensed under the GNU General Public License,
+ * please see the file GPL in the top level source directory.
+ */
+
+/*
+ * NOTE
+ *
+ * This code used to be the parser for GnuPG's output.
+ *
+ * Nowadays, we are using an external pubring lister with PGP which mimics
+ * gpg's output format.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+
+#include <lib-lib/mem.h>
+#include <lib-lib/str.h>
+
+#include <lib-mime/mime.h>
+
+#include "mutt.h"
+#include "pgp.h"
+#include "charset.h"
+
+#include "lib/debug.h"
+
+/****************
+ * Read the GNUPG keys. For now we read the complete keyring by
+ * calling gnupg in a special mode.
+ *
+ * The output format of gpgm is colon delimited with these fields:
+ * - record type ("pub","uid","sig","rev" etc.)
+ * - trust info
+ * - key length
+ * - pubkey algo
+ * - 16 hex digits with the long keyid.
+ * - timestamp (1998-02-28)
+ * - Local id
+ * - ownertrust
+ * - name
+ * - signature class
+ */
+
+/* decode the backslash-escaped user ids. */
+
+static char *_chs = 0;
+
+static void fix_uid (char *uid)
+{
+ char *s, *d;
+ iconv_t cd;
+
+ for (s = d = uid; *s;) {
+ if (*s == '\\' && *(s + 1) == 'x' && isxdigit ((unsigned char) *(s + 2))
+ && isxdigit ((unsigned char) *(s + 3))) {
+ *d++ = hexval (*(s + 2)) << 4 | hexval (*(s + 3));
+ s += 4;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = '\0';
+
+ if (_chs && (cd = mutt_iconv_open (_chs, "utf-8", 0)) != (iconv_t) - 1) {
+ int n = s - uid + 1; /* chars available in original buffer */
+ char *buf;
+ const char *ib;
+ char *ob;
+ ssize_t ibl, obl;
+
+ buf = p_new(char, n + 1);
+ ib = uid, ibl = d - uid + 1, ob = buf, obl = n;
+ my_iconv(cd, &ib, &ibl, &ob, &obl);
+ if (!ibl) {
+ if (ob - buf < n) {
+ memcpy (uid, buf, ob - buf);
+ uid[ob - buf] = '\0';
+ }
+ else if (ob - buf == n && (buf[n] = 0, m_strlen(buf) < n))
+ memcpy (uid, buf, n);
+ }
+ p_delete(&buf);
+ iconv_close (cd);
+ }
+}
+
+static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k)
+{
+ pgp_uid_t *uid = NULL;
+ int field = 0, is_uid = 0;
+ char *pend, *p;
+ int trust = 0;
+ int flags = 0;
+
+ *is_subkey = 0;
+ if (!*buf)
+ return NULL;
+
+ debug_print (2, ("buf = `%s'\n", buf));
+
+ for (p = buf; p; p = pend) {
+ if ((pend = strchr (p, ':')))
+ *pend++ = 0;
+ field++;
+ if (field > 1 && !*p)
+ continue;
+
+ switch (field) {
+ case 1: /* record type */
+ {
+ debug_print (2, ("record type: %s\n", p));
+
+ if (!m_strcmp(p, "pub"));
+ else if (!m_strcmp(p, "sub"))
+ *is_subkey = 1;
+ else if (!m_strcmp(p, "sec"));
+ else if (!m_strcmp(p, "ssb"))
+ *is_subkey = 1;
+ else if (!m_strcmp(p, "uid"))
+ is_uid = 1;
+ else
+ return NULL;
+
+ if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB))))
+ k = pgp_new_keyinfo();
+
+ break;
+ }
+ case 2: /* trust info */
+ {
+ debug_print (2, ("trust info: %s\n", p));
+
+ switch (*p) { /* look only at the first letter */
+ case 'e':
+ flags |= KEYFLAG_EXPIRED;
+ break;
+ case 'r':
+ flags |= KEYFLAG_REVOKED;
+ break;
+ case 'd':
+ flags |= KEYFLAG_DISABLED;
+ break;
+ case 'n':
+ trust = 1;
+ break;
+ case 'm':
+ trust = 2;
+ break;
+ case 'f':
+ trust = 3;
+ break;
+ case 'u':
+ trust = 3;
+ break;
+ }
+
+ if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB)))
+ k->flags |= flags;
+
+ break;
+ }
+ case 3: /* key length */
+ {
+
+ debug_print (2, ("key len: %s\n", p));
+
+ if (!(*is_subkey && option (OPTPGPIGNORESUB)))
+ k->keylen = atoi (p); /* fixme: add validation checks */
+ break;
+ }
+ case 4: /* pubkey algo */
+ {
+
+ debug_print (2, ("pubkey algorithm: %s\n", p));
+
+ if (!(*is_subkey && option (OPTPGPIGNORESUB))) {
+ k->numalg = atoi (p);
+ k->algorithm = pgp_pkalgbytype (atoi (p));
+ }
+ break;
+ }
+ case 5: /* 16 hex digits with the long keyid. */
+ {
+ debug_print (2, ("key id: %s\n", p));
+
+ if (!(*is_subkey && option (OPTPGPIGNORESUB)))
+ m_strreplace(&k->keyid, p);
+ break;
+
+ }
+ case 6: /* timestamp (1998-02-28) */
+ {
+ char tstr[11];
+ struct tm st_time;
+
+ debug_print (2, ("time stamp: %s\n", p));
+
+ if (!p)
+ break;
+ st_time.tm_sec = 0;
+ st_time.tm_min = 0;
+ st_time.tm_hour = 12;
+ m_strcpy(tstr, sizeof(tstr), p);
+ tstr[4] = '\0';
+ st_time.tm_year = atoi (tstr) - 1900;
+ tstr[7] = '\0';
+ st_time.tm_mon = (atoi (tstr + 5)) - 1;
+ st_time.tm_mday = atoi (tstr + 8);
+ k->gen_time = mutt_mktime (&st_time, 0);
+ break;
+ }
+ case 7: /* valid for n days */
+ break;
+ case 8: /* Local id */
+ break;
+ case 9: /* ownertrust */
+ break;
+ case 10: /* name */
+ {
+ if (!pend || !*p)
+ break; /* empty field or no trailing colon */
+
+ /* ignore user IDs on subkeys */
+ if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB)))
+ break;
+
+ debug_print (2, ("user ID: %s\n", p));
+
+ uid = p_new(pgp_uid_t, 1);
+ fix_uid (p);
+ uid->addr = m_strdup(p);
+ uid->trust = trust;
+ uid->flags |= flags;
+ uid->parent = k;
+ uid->next = k->address;
+ k->address = uid;
+
+ if (strstr (p, "ENCR"))
+ k->flags |= KEYFLAG_PREFER_ENCRYPTION;
+ if (strstr (p, "SIGN"))
+ k->flags |= KEYFLAG_PREFER_SIGNING;
+
+ break;
+ }
+ case 11: /* signature class */
+ break;
+ case 12: /* key capabilities */
+ debug_print (2, ("capabilities info: %s\n", p));
+
+ while (*p) {
+ switch (*p++) {
+ case 'D':
+ flags |= KEYFLAG_DISABLED;
+ break;
+
+ case 'e':
+ flags |= KEYFLAG_CANENCRYPT;
+ break;
+
+ case 's':
+ flags |= KEYFLAG_CANSIGN;
+ break;
+ }
+ }
+
+ if (!is_uid && (!*is_subkey || !option (OPTPGPIGNORESUB)
+ || !((flags & KEYFLAG_DISABLED)
+ || (flags & KEYFLAG_REVOKED)
+ || (flags & KEYFLAG_EXPIRED))))
+ k->flags |= flags;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ return k;
+}
+
+pgp_key_t pgp_get_candidates (pgp_ring_t keyring, LIST * hints)
+{
+ FILE *fp;
+ pid_t thepid;
+ char buf[LONG_STRING];
+ pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL;
+ int is_sub;
+ int devnull;
+
+ if ((devnull = open ("/dev/null", O_RDWR)) == -1)
+ return NULL;
+
+ m_strreplace(&_chs, Charset);
+
+ thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull,
+ keyring, hints);
+ if (thepid == -1) {
+ close (devnull);
+ return NULL;
+ }
+
+ kend = &db;
+ k = NULL;
+ while (fgets (buf, sizeof (buf) - 1, fp)) {
+ if (!(kk = parse_pub_line (buf, &is_sub, k)))
+ continue;
+
+ /* Only append kk to the list if it's new. */
+ if (kk != k) {
+ if (k)
+ kend = &k->next;
+ *kend = k = kk;
+
+ if (is_sub) {
+ pgp_uid_t **l;
+
+ k->flags |= KEYFLAG_SUBKEY;
+ k->parent = mainkey;
+ for (l = &k->address; *l; l = &(*l)->next);
+ *l = pgp_copy_uids (mainkey->address, k);
+ }
+ else
+ mainkey = k;
+ }
+ }
+
+ if (ferror (fp))
+ mutt_perror ("fgets");
+
+ fclose (fp);
+ mutt_wait_filter (thepid);
+
+ close (devnull);
+
+ return db;
+}