Nico Golde:
[apps/madmutt.git] / 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 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <ctype.h>
35
36 #include "mutt.h"
37 #include "pgp.h"
38 #include "charset.h"
39
40 /* for hexval */
41 #include "mime.h"
42
43 /****************
44  * Read the GNUPG keys.  For now we read the complete keyring by
45  * calling gnupg in a special mode.
46  *
47  * The output format of gpgm is colon delimited with these fields:
48  *   - record type ("pub","uid","sig","rev" etc.)
49  *   - trust info
50  *   - key length
51  *   - pubkey algo
52  *   - 16 hex digits with the long keyid.
53  *   - timestamp (1998-02-28)
54  *   - Local id
55  *   - ownertrust
56  *   - name
57  *   - signature class
58  */
59
60 /* decode the backslash-escaped user ids. */
61
62 static char *_chs = 0;
63
64 static void fix_uid (char *uid)
65 {
66   char *s, *d;
67   iconv_t cd;
68
69   for (s = d = uid; *s;) {
70     if (*s == '\\' && *(s + 1) == 'x' && isxdigit ((unsigned char) *(s + 2))
71         && isxdigit ((unsigned char) *(s + 3))) {
72       *d++ = hexval (*(s + 2)) << 4 | hexval (*(s + 3));
73       s += 4;
74     }
75     else
76       *d++ = *s++;
77   }
78   *d = '\0';
79
80   if (_chs && (cd = mutt_iconv_open (_chs, "utf-8", 0)) != (iconv_t) - 1) {
81     int n = s - uid + 1;        /* chars available in original buffer */
82     char *buf;
83     ICONV_CONST char *ib;
84     char *ob;
85     size_t ibl, obl;
86
87     buf = safe_malloc (n + 1);
88     ib = uid, ibl = d - uid + 1, ob = buf, obl = n;
89     iconv (cd, &ib, &ibl, &ob, &obl);
90     if (!ibl) {
91       if (ob - buf < n) {
92         memcpy (uid, buf, ob - buf);
93         uid[ob - buf] = '\0';
94       }
95       else if (ob - buf == n && (buf[n] = 0, mutt_strlen (buf) < n))
96         memcpy (uid, buf, n);
97     }
98     FREE (&buf);
99     iconv_close (cd);
100   }
101 }
102
103 static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k)
104 {
105   pgp_uid_t *uid = NULL;
106   int field = 0, is_uid = 0;
107   char *pend, *p;
108   int trust = 0;
109   int flags = 0;
110
111   *is_subkey = 0;
112   if (!*buf)
113     return NULL;
114
115   dprint (2, (debugfile, "parse_pub_line: buf = `%s'\n", buf));
116
117   for (p = buf; p; p = pend) {
118     if ((pend = strchr (p, ':')))
119       *pend++ = 0;
120     field++;
121     if (field > 1 && !*p)
122       continue;
123
124     switch (field) {
125     case 1:                    /* record type */
126       {
127         dprint (2, (debugfile, "record type: %s\n", p));
128
129         if (!mutt_strcmp (p, "pub"));
130         else if (!mutt_strcmp (p, "sub"))
131           *is_subkey = 1;
132         else if (!mutt_strcmp (p, "sec"));
133         else if (!mutt_strcmp (p, "ssb"))
134           *is_subkey = 1;
135         else if (!mutt_strcmp (p, "uid"))
136           is_uid = 1;
137         else
138           return NULL;
139
140         if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB))))
141           k = safe_calloc (sizeof *k, 1);
142
143         break;
144       }
145     case 2:                    /* trust info */
146       {
147         dprint (2, (debugfile, "trust info: %s\n", p));
148
149         switch (*p) {           /* look only at the first letter */
150         case 'e':
151           flags |= KEYFLAG_EXPIRED;
152           break;
153         case 'r':
154           flags |= KEYFLAG_REVOKED;
155           break;
156         case 'd':
157           flags |= KEYFLAG_DISABLED;
158           break;
159         case 'n':
160           trust = 1;
161           break;
162         case 'm':
163           trust = 2;
164           break;
165         case 'f':
166           trust = 3;
167           break;
168         case 'u':
169           trust = 3;
170           break;
171         }
172
173         if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB)))
174           k->flags |= flags;
175
176         break;
177       }
178     case 3:                    /* key length  */
179       {
180
181         dprint (2, (debugfile, "key len: %s\n", p));
182
183         if (!(*is_subkey && option (OPTPGPIGNORESUB)))
184           k->keylen = atoi (p); /* fixme: add validation checks */
185         break;
186       }
187     case 4:                    /* pubkey algo */
188       {
189
190         dprint (2, (debugfile, "pubkey algorithm: %s\n", p));
191
192         if (!(*is_subkey && option (OPTPGPIGNORESUB))) {
193           k->numalg = atoi (p);
194           k->algorithm = pgp_pkalgbytype (atoi (p));
195         }
196         break;
197       }
198     case 5:                    /* 16 hex digits with the long keyid. */
199       {
200         dprint (2, (debugfile, "key id: %s\n", p));
201
202         if (!(*is_subkey && option (OPTPGPIGNORESUB)))
203           mutt_str_replace (&k->keyid, p);
204         break;
205
206       }
207     case 6:                    /* timestamp (1998-02-28) */
208       {
209         char tstr[11];
210         struct tm time;
211
212         dprint (2, (debugfile, "time stamp: %s\n", p));
213
214         if (!p)
215           break;
216         time.tm_sec = 0;
217         time.tm_min = 0;
218         time.tm_hour = 12;
219         strncpy (tstr, p, 11);
220         tstr[4] = '\0';
221         time.tm_year = atoi (tstr) - 1900;
222         tstr[7] = '\0';
223         time.tm_mon = (atoi (tstr + 5)) - 1;
224         time.tm_mday = atoi (tstr + 8);
225         k->gen_time = mutt_mktime (&time, 0);
226         break;
227       }
228     case 7:                    /* valid for n days */
229       break;
230     case 8:                    /* Local id         */
231       break;
232     case 9:                    /* ownertrust       */
233       break;
234     case 10:                   /* name             */
235       {
236         if (!pend || !*p)
237           break;                /* empty field or no trailing colon */
238
239         /* ignore user IDs on subkeys */
240         if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB)))
241           break;
242
243         dprint (2, (debugfile, "user ID: %s\n", p));
244
245         uid = safe_calloc (sizeof (pgp_uid_t), 1);
246         fix_uid (p);
247         uid->addr = safe_strdup (p);
248         uid->trust = trust;
249         uid->flags |= flags;
250         uid->parent = k;
251         uid->next = k->address;
252         k->address = uid;
253
254         if (strstr (p, "ENCR"))
255           k->flags |= KEYFLAG_PREFER_ENCRYPTION;
256         if (strstr (p, "SIGN"))
257           k->flags |= KEYFLAG_PREFER_SIGNING;
258
259         break;
260       }
261     case 11:                   /* signature class  */
262       break;
263     case 12:                   /* key capabilities */
264       dprint (2, (debugfile, "capabilities info: %s\n", p));
265
266       while (*p) {
267         switch (*p++) {
268         case 'D':
269           flags |= KEYFLAG_DISABLED;
270           break;
271
272         case 'e':
273           flags |= KEYFLAG_CANENCRYPT;
274           break;
275
276         case 's':
277           flags |= KEYFLAG_CANSIGN;
278           break;
279         }
280       }
281
282       if (!is_uid && (!*is_subkey || !option (OPTPGPIGNORESUB)
283                       || !((flags & KEYFLAG_DISABLED)
284                            || (flags & KEYFLAG_REVOKED)
285                            || (flags & KEYFLAG_EXPIRED))))
286         k->flags |= flags;
287
288       break;
289
290     default:
291       break;
292     }
293   }
294   return k;
295 }
296
297 pgp_key_t pgp_get_candidates (pgp_ring_t keyring, LIST * hints)
298 {
299   FILE *fp;
300   pid_t thepid;
301   char buf[LONG_STRING];
302   pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL;
303   int is_sub;
304   int devnull;
305
306   if ((devnull = open ("/dev/null", O_RDWR)) == -1)
307     return NULL;
308
309   mutt_str_replace (&_chs, Charset);
310
311   thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull,
312                                  keyring, hints);
313   if (thepid == -1) {
314     close (devnull);
315     return NULL;
316   }
317
318   kend = &db;
319   k = NULL;
320   while (fgets (buf, sizeof (buf) - 1, fp)) {
321     if (!(kk = parse_pub_line (buf, &is_sub, k)))
322       continue;
323
324     /* Only append kk to the list if it's new. */
325     if (kk != k) {
326       if (k)
327         kend = &k->next;
328       *kend = k = kk;
329
330       if (is_sub) {
331         pgp_uid_t **l;
332
333         k->flags |= KEYFLAG_SUBKEY;
334         k->parent = mainkey;
335         for (l = &k->address; *l; l = &(*l)->next);
336         *l = pgp_copy_uids (mainkey->address, k);
337       }
338       else
339         mainkey = k;
340     }
341   }
342
343   if (ferror (fp))
344     mutt_perror ("fgets");
345
346   fclose (fp);
347   mutt_wait_filter (thepid);
348
349   close (devnull);
350
351   return db;
352 }