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