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