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