let m_strformat accept NULL formats.
[apps/madmutt.git] / lib-crypt / gnupgparse.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1998-2000 Werner Koch <werner.koch@guug.de>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 /*
12  * NOTE
13  * 
14  * This code used to be the parser for GnuPG's output.
15  * 
16  * Nowadays, we are using an external pubring lister with PGP which mimics 
17  * gpg's output format.
18  * 
19  */
20
21 #include <lib-lib/lib-lib.h>
22
23 #include <lib-mime/mime.h>
24 #include <lib-sys/unix.h>
25 #include <lib-ui/curses.h>
26
27 #include "pgp.h"
28 #include "charset.h"
29
30 /****************
31  * Read the GNUPG keys.  For now we read the complete keyring by
32  * calling gnupg in a special mode.
33  *
34  * The output format of gpgm is colon delimited with these fields:
35  *   - record type ("pub","uid","sig","rev" etc.)
36  *   - trust info
37  *   - key length
38  *   - pubkey algo
39  *   - 16 hex digits with the long keyid.
40  *   - timestamp (1998-02-28)
41  *   - Local id
42  *   - ownertrust
43  *   - name
44  *   - signature class
45  */
46
47 /* decode the backslash-escaped user ids. */
48
49 static char *_chs = 0;
50
51 static void fix_uid (char *uid)
52 {
53   char *s, *d;
54   iconv_t cd;
55
56   for (s = d = uid; *s;) {
57     if (*s == '\\' && *(s + 1) == 'x' && isxdigit ((unsigned char) *(s + 2))
58         && isxdigit ((unsigned char) *(s + 3))) {
59       *d++ = hexval (*(s + 2)) << 4 | hexval (*(s + 3));
60       s += 4;
61     }
62     else
63       *d++ = *s++;
64   }
65   *d = '\0';
66
67   if (_chs && (cd = mutt_iconv_open (_chs, "utf-8", 0)) != MUTT_ICONV_ERROR) {
68     int n = s - uid + 1;        /* chars available in original buffer */
69     char *buf;
70     const char *ib;
71     char *ob;
72     ssize_t ibl, obl;
73
74     buf = p_new(char, n + 1);
75     ib = uid, ibl = d - uid + 1, ob = buf, obl = n;
76     my_iconv(cd, &ib, &ibl, &ob, &obl);
77     if (!ibl) {
78       if (ob - buf < n) {
79         memcpy (uid, buf, ob - buf);
80         uid[ob - buf] = '\0';
81       }
82       else if (ob - buf == n && (buf[n] = 0, m_strlen(buf) < n))
83         memcpy (uid, buf, n);
84     }
85     p_delete(&buf);
86     iconv_close (cd);
87   }
88 }
89
90 static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k)
91 {
92   pgp_uid_t *uid = NULL;
93   int field = 0, is_uid = 0;
94   char *pend, *p;
95   int trust = 0;
96   int flags = 0;
97
98   *is_subkey = 0;
99   if (!*buf)
100     return NULL;
101
102   for (p = buf; p; p = pend) {
103     if ((pend = strchr (p, ':')))
104       *pend++ = 0;
105     field++;
106     if (field > 1 && !*p)
107       continue;
108
109     switch (field) {
110     case 1:                    /* record type */
111       {
112         if (!m_strcmp(p, "pub"));
113         else if (!m_strcmp(p, "sub"))
114           *is_subkey = 1;
115         else if (!m_strcmp(p, "sec"));
116         else if (!m_strcmp(p, "ssb"))
117           *is_subkey = 1;
118         else if (!m_strcmp(p, "uid"))
119           is_uid = 1;
120         else
121           return NULL;
122
123         if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB))))
124           k = pgp_new_keyinfo();
125
126         break;
127       }
128     case 2:                    /* trust info */
129       {
130         switch (*p) {           /* look only at the first letter */
131         case 'e':
132           flags |= KEYFLAG_EXPIRED;
133           break;
134         case 'r':
135           flags |= KEYFLAG_REVOKED;
136           break;
137         case 'd':
138           flags |= KEYFLAG_DISABLED;
139           break;
140         case 'n':
141           trust = 1;
142           break;
143         case 'm':
144           trust = 2;
145           break;
146         case 'f':
147           trust = 3;
148           break;
149         case 'u':
150           trust = 3;
151           break;
152         }
153
154         if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB)))
155           k->flags |= flags;
156
157         break;
158       }
159     case 3:                    /* key length  */
160       {
161         if (!(*is_subkey && option (OPTPGPIGNORESUB)))
162           k->keylen = atoi (p); /* fixme: add validation checks */
163         break;
164       }
165     case 4:                    /* pubkey algo */
166       {
167         if (!(*is_subkey && option (OPTPGPIGNORESUB))) {
168           k->numalg = atoi (p);
169           k->algorithm = pgp_pkalgbytype (atoi (p));
170         }
171         break;
172       }
173     case 5:                    /* 16 hex digits with the long keyid. */
174       {
175         if (!(*is_subkey && option (OPTPGPIGNORESUB)))
176           m_strreplace(&k->keyid, p);
177         break;
178
179       }
180     case 6:                    /* timestamp (1998-02-28) */
181       {
182         char tstr[11];
183         struct tm st_time;
184
185         if (!p)
186           break;
187         st_time.tm_sec = 0;
188         st_time.tm_min = 0;
189         st_time.tm_hour = 12;
190         m_strcpy(tstr, sizeof(tstr), p);
191         tstr[4] = '\0';
192         st_time.tm_year = atoi (tstr) - 1900;
193         tstr[7] = '\0';
194         st_time.tm_mon = (atoi (tstr + 5)) - 1;
195         st_time.tm_mday = atoi (tstr + 8);
196         k->gen_time = mutt_mktime (&st_time, 0);
197         break;
198       }
199     case 7:                    /* valid for n days */
200       break;
201     case 8:                    /* Local id         */
202       break;
203     case 9:                    /* ownertrust       */
204       break;
205     case 10:                   /* name             */
206       {
207         if (!pend || !*p)
208           break;                /* empty field or no trailing colon */
209
210         /* ignore user IDs on subkeys */
211         if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB)))
212           break;
213
214         uid = p_new(pgp_uid_t, 1);
215         fix_uid (p);
216         uid->addr = m_strdup(p);
217         uid->trust = trust;
218         uid->flags |= flags;
219         uid->parent = k;
220         uid->next = k->address;
221         k->address = uid;
222
223         if (strstr (p, "ENCR"))
224           k->flags |= KEYFLAG_PREFER_ENCRYPTION;
225         if (strstr (p, "SIGN"))
226           k->flags |= KEYFLAG_PREFER_SIGNING;
227
228         break;
229       }
230     case 11:                   /* signature class  */
231       break;
232     case 12:                   /* key capabilities */
233       while (*p) {
234         switch (*p++) {
235         case 'D':
236           flags |= KEYFLAG_DISABLED;
237           break;
238
239         case 'e':
240           flags |= KEYFLAG_CANENCRYPT;
241           break;
242
243         case 's':
244           flags |= KEYFLAG_CANSIGN;
245           break;
246         }
247       }
248
249       if (!is_uid && (!*is_subkey || !option (OPTPGPIGNORESUB)
250                       || !((flags & KEYFLAG_DISABLED)
251                            || (flags & KEYFLAG_REVOKED)
252                            || (flags & KEYFLAG_EXPIRED))))
253         k->flags |= flags;
254
255       break;
256
257     default:
258       break;
259     }
260   }
261   return k;
262 }
263
264 pgp_key_t pgp_get_candidates (pgp_ring_t keyring, string_list_t * hints)
265 {
266   FILE *fp;
267   pid_t thepid;
268   char buf[LONG_STRING];
269   pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL;
270   int is_sub;
271   int devnull;
272
273   if ((devnull = open ("/dev/null", O_RDWR)) == -1)
274     return NULL;
275
276   m_strreplace(&_chs, Charset);
277
278   thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull,
279                                  keyring, hints);
280   if (thepid == -1) {
281     close (devnull);
282     return NULL;
283   }
284
285   kend = &db;
286   k = NULL;
287   while (fgets (buf, sizeof (buf) - 1, fp)) {
288     if (!(kk = parse_pub_line (buf, &is_sub, k)))
289       continue;
290
291     /* Only append kk to the list if it's new. */
292     if (kk != k) {
293       if (k)
294         kend = &k->next;
295       *kend = k = kk;
296
297       if (is_sub) {
298         pgp_uid_t **l;
299
300         k->flags |= KEYFLAG_SUBKEY;
301         k->parent = mainkey;
302         for (l = &k->address; *l; l = &(*l)->next);
303         *l = pgp_copy_uids (mainkey->address, k);
304       }
305       else
306         mainkey = k;
307     }
308   }
309
310   if (ferror (fp))
311     mutt_perror ("fgets");
312
313   m_fclose(&fp);
314   mutt_wait_filter (thepid);
315
316   close (devnull);
317
318   return db;
319 }