b8c31b7a3924df4a83dc41e17cdc2753fd539c72
[gnupg.git] / g10 / kbnode.c
1 /* kbnode.c -  keyblock node utility functions
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002,
3  *               2005, 2010 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "gpg.h"
27 #include "../common/util.h"
28 #include "../common/init.h"
29 #include "packet.h"
30 #include "keydb.h"
31
32 #define USE_UNUSED_NODES 1
33
34 static int cleanup_registered;
35 static KBNODE unused_nodes;
36
37 static void
38 release_unused_nodes (void)
39 {
40 #if USE_UNUSED_NODES
41   while (unused_nodes)
42     {
43       kbnode_t next = unused_nodes->next;
44       xfree (unused_nodes);
45       unused_nodes = next;
46     }
47 #endif /*USE_UNUSED_NODES*/
48 }
49
50
51 static kbnode_t
52 alloc_node (void)
53 {
54   kbnode_t n;
55
56   n = unused_nodes;
57   if (n)
58     unused_nodes = n->next;
59   else
60     {
61       if (!cleanup_registered)
62         {
63           cleanup_registered = 1;
64           register_mem_cleanup_func (release_unused_nodes);
65         }
66       n = xmalloc (sizeof *n);
67     }
68   n->next = NULL;
69   n->pkt = NULL;
70   n->flag = 0;
71   n->private_flag=0;
72   n->recno = 0;
73   return n;
74 }
75
76 static void
77 free_node( KBNODE n )
78 {
79   if (n)
80     {
81 #if USE_UNUSED_NODES
82       n->next = unused_nodes;
83       unused_nodes = n;
84 #else
85       xfree (n);
86 #endif
87     }
88 }
89
90
91
92 KBNODE
93 new_kbnode( PACKET *pkt )
94 {
95     KBNODE n = alloc_node();
96     n->pkt = pkt;
97     return n;
98 }
99
100
101 KBNODE
102 clone_kbnode( KBNODE node )
103 {
104     KBNODE n = alloc_node();
105
106     n->pkt = node->pkt;
107     n->private_flag = node->private_flag | 2; /* mark cloned */
108     return n;
109 }
110
111
112 void
113 release_kbnode( KBNODE n )
114 {
115     KBNODE n2;
116
117     while( n ) {
118         n2 = n->next;
119         if( !is_cloned_kbnode(n) ) {
120             free_packet( n->pkt );
121             xfree( n->pkt );
122         }
123         free_node( n );
124         n = n2;
125     }
126 }
127
128
129 /****************
130  * Delete NODE.
131  * Note: This only works with walk_kbnode!!
132  */
133 void
134 delete_kbnode( KBNODE node )
135 {
136     node->private_flag |= 1;
137 }
138
139 /****************
140  * Append NODE to ROOT.  ROOT must exist!
141  */
142 void
143 add_kbnode( KBNODE root, KBNODE node )
144 {
145     KBNODE n1;
146
147     for(n1=root; n1->next; n1 = n1->next)
148         ;
149     n1->next = node;
150 }
151
152 /****************
153  * Insert NODE into the list after root but before a packet which is not of
154  * type PKTTYPE
155  * (only if PKTTYPE != 0)
156  */
157 void
158 insert_kbnode( KBNODE root, KBNODE node, int pkttype )
159 {
160     if( !pkttype ) {
161         node->next = root->next;
162         root->next = node;
163     }
164     else {
165         KBNODE n1;
166
167         for(n1=root; n1->next;  n1 = n1->next)
168             if( pkttype != n1->next->pkt->pkttype ) {
169                 node->next = n1->next;
170                 n1->next = node;
171                 return;
172             }
173         /* no such packet, append */
174         node->next = NULL;
175         n1->next = node;
176     }
177 }
178
179
180 /****************
181  * Find the previous node (if PKTTYPE = 0) or the previous node
182  * with pkttype PKTTYPE in the list starting with ROOT of NODE.
183  */
184 KBNODE
185 find_prev_kbnode( KBNODE root, KBNODE node, int pkttype )
186 {
187     KBNODE n1;
188
189     for (n1=NULL; root && root != node; root = root->next ) {
190         if (!pkttype ||root->pkt->pkttype == pkttype)
191             n1 = root;
192     }
193     return n1;
194 }
195
196 /****************
197  * Ditto, but find the next packet.  The behaviour is trivial if
198  * PKTTYPE is 0 but if it is specified, the next node with a packet
199  * of this type is returned.  The function has some knowledge about
200  * the valid ordering of packets: e.g. if the next signature packet
201  * is requested, the function will not return one if it encounters
202  * a user-id.
203  */
204 KBNODE
205 find_next_kbnode( KBNODE node, int pkttype )
206 {
207     for( node=node->next ; node; node = node->next ) {
208         if( !pkttype )
209             return node;
210         else if( pkttype == PKT_USER_ID
211                  && (   node->pkt->pkttype == PKT_PUBLIC_KEY
212                      || node->pkt->pkttype == PKT_SECRET_KEY ) )
213             return NULL;
214         else if( pkttype == PKT_SIGNATURE
215                  && (   node->pkt->pkttype == PKT_USER_ID
216                      || node->pkt->pkttype == PKT_PUBLIC_KEY
217                      || node->pkt->pkttype == PKT_SECRET_KEY ) )
218             return NULL;
219         else if( node->pkt->pkttype == pkttype )
220             return node;
221     }
222     return NULL;
223 }
224
225
226 KBNODE
227 find_kbnode( KBNODE node, int pkttype )
228 {
229     for( ; node; node = node->next ) {
230         if( node->pkt->pkttype == pkttype )
231             return node;
232     }
233     return NULL;
234 }
235
236
237
238 /****************
239  * Walk through a list of kbnodes. This function returns
240  * the next kbnode for each call; before using the function the first
241  * time, the caller must set CONTEXT to NULL (This has simply the effect
242  * to start with ROOT).
243  */
244 KBNODE
245 walk_kbnode( KBNODE root, KBNODE *context, int all )
246 {
247     KBNODE n;
248
249     do {
250         if( !*context ) {
251             *context = root;
252             n = root;
253         }
254         else {
255             n = (*context)->next;
256             *context = n;
257         }
258     } while( !all && n && is_deleted_kbnode(n) );
259
260     return n;
261 }
262
263 void
264 clear_kbnode_flags( KBNODE n )
265 {
266     for( ; n; n = n->next ) {
267         n->flag = 0;
268     }
269 }
270
271
272 /****************
273  * Commit changes made to the kblist at ROOT. Note that ROOT my change,
274  * and it is therefore passed by reference.
275  * The function has the effect of removing all nodes marked as deleted.
276  * returns true if any node has been changed
277  */
278 int
279 commit_kbnode( KBNODE *root )
280 {
281     KBNODE n, nl;
282     int changed = 0;
283
284     for( n = *root, nl=NULL; n; n = nl->next ) {
285         if( is_deleted_kbnode(n) ) {
286             if( n == *root )
287                 *root = nl = n->next;
288             else
289                 nl->next = n->next;
290             if( !is_cloned_kbnode(n) ) {
291                 free_packet( n->pkt );
292                 xfree( n->pkt );
293             }
294             free_node( n );
295             changed = 1;
296         }
297         else
298             nl = n;
299     }
300     return changed;
301 }
302
303 void
304 remove_kbnode( KBNODE *root, KBNODE node )
305 {
306     KBNODE n, nl;
307
308     for( n = *root, nl=NULL; n; n = nl->next ) {
309         if( n == node ) {
310             if( n == *root )
311                 *root = nl = n->next;
312             else
313                 nl->next = n->next;
314             if( !is_cloned_kbnode(n) ) {
315                 free_packet( n->pkt );
316                 xfree( n->pkt );
317             }
318             free_node( n );
319         }
320         else
321             nl = n;
322     }
323 }
324
325
326 /****************
327  * Move NODE behind right after WHERE or to the beginning if WHERE is NULL.
328  */
329 void
330 move_kbnode( KBNODE *root, KBNODE node, KBNODE where )
331 {
332     KBNODE tmp, prev;
333
334     if( !root || !*root || !node )
335         return;  /* sanity check */
336     for( prev = *root; prev && prev->next != node; prev = prev->next )
337         ;
338     if( !prev )
339         return; /* node is not in the list */
340
341     if( !where ) {  /* move node before root */
342         if( node == *root ) /* move to itself */
343             return;
344         prev->next = node->next;
345         node->next = *root;
346         *root = node;
347         return;
348     }
349     /* move it after where */
350     if( node == where )
351         return;
352     tmp = node->next;
353     node->next = where->next;
354     where->next = node;
355     prev->next = tmp;
356 }
357
358
359
360
361 void
362 dump_kbnode (KBNODE node)
363 {
364   for (; node; node = node->next )
365     {
366       const char *s;
367       switch (node->pkt->pkttype)
368         {
369         case 0:         s="empty"; break;
370         case PKT_PUBLIC_KEY:    s="public-key"; break;
371         case PKT_SECRET_KEY:    s="secret-key"; break;
372         case PKT_SECRET_SUBKEY: s= "secret-subkey"; break;
373         case PKT_PUBKEY_ENC:    s="public-enc"; break;
374         case PKT_SIGNATURE:     s="signature"; break;
375         case PKT_ONEPASS_SIG: s="onepass-sig"; break;
376         case PKT_USER_ID:       s="user-id"; break;
377         case PKT_PUBLIC_SUBKEY: s="public-subkey"; break;
378         case PKT_COMMENT:       s="comment"; break;
379         case PKT_RING_TRUST:    s="trust"; break;
380         case PKT_PLAINTEXT:     s="plaintext"; break;
381         case PKT_COMPRESSED:    s="compressed"; break;
382         case PKT_ENCRYPTED:     s="encrypted"; break;
383         case PKT_GPG_CONTROL: s="gpg-control"; break;
384         default:                s="unknown"; break;
385         }
386       log_debug ("node %p %02x/%02x type=%s",
387                  node, node->flag, node->private_flag, s);
388       if (node->pkt->pkttype == PKT_USER_ID)
389         {
390           PKT_user_id *uid = node->pkt->pkt.user_id;
391           log_printf ("  \"");
392           es_write_sanitized (log_get_stream (), uid->name, uid->len,
393                               NULL, NULL);
394           log_printf ("\" %c%c%c%c\n",
395                       uid->flags.expired? 'e':'.',
396                       uid->flags.revoked? 'r':'.',
397                       uid->created?    'v':'.',
398                       uid->flags.primary? 'p':'.' );
399         }
400       else if (node->pkt->pkttype == PKT_SIGNATURE)
401         {
402           log_printf ("  class=%02x keyid=%08lX ts=%lu\n",
403                       node->pkt->pkt.signature->sig_class,
404                       (ulong)node->pkt->pkt.signature->keyid[1],
405                       (ulong)node->pkt->pkt.signature->timestamp);
406         }
407       else if (node->pkt->pkttype == PKT_GPG_CONTROL)
408         {
409           log_printf (" ctrl=%d len=%u\n",
410                       node->pkt->pkt.gpg_control->control,
411                       (unsigned int)node->pkt->pkt.gpg_control->datalen);
412         }
413       else if (node->pkt->pkttype == PKT_PUBLIC_KEY
414                || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
415         {
416           PKT_public_key *pk = node->pkt->pkt.public_key;
417
418           log_printf ("  keyid=%08lX a=%d u=%d %c%c%c%c\n",
419                       (ulong)keyid_from_pk( pk, NULL ),
420                       pk->pubkey_algo, pk->pubkey_usage,
421                       pk->has_expired? 'e':'.',
422                       pk->flags.revoked? 'r':'.',
423                       pk->flags.valid?    'v':'.',
424                       pk->flags.mdc?   'm':'.');
425         }
426       else
427         log_printf ("\n");
428
429       log_flush ();
430     }
431 }