some bug fixes
[gnupg.git] / g10 / kbnode.c
1 /* kbnode.c -  keyblock node utility functions
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include "util.h"
27 #include "memory.h"
28 #include "packet.h"
29 #include "keydb.h"
30
31 #define USE_UNUSED_NODES 1
32
33 static KBNODE unused_nodes;
34
35 KBNODE
36 new_kbnode( PACKET *pkt )
37 {
38     KBNODE n;
39
40     n = unused_nodes;
41     if( n )
42         unused_nodes = n->next;
43     else
44         n = m_alloc( sizeof *n );
45     n->next = NULL;
46     n->pkt = pkt;
47     n->flag = 0;
48     n->private_flag=0;
49     n->recno = 0;
50     return n;
51 }
52
53
54 KBNODE
55 clone_kbnode( KBNODE node )
56 {
57     KBNODE n;
58
59     n = unused_nodes;
60     if( n )
61         unused_nodes = n->next;
62     else
63         n = m_alloc( sizeof *n );
64     n->next = NULL;
65     n->pkt = node->pkt;
66     n->flag = 0;
67     n->private_flag = node->private_flag | 2; /* mark cloned */
68     return n;
69 }
70
71
72 void
73 release_kbnode( KBNODE n )
74 {
75     KBNODE n2;
76
77     while( n ) {
78         n2 = n->next;
79         if( !(n->private_flag & 2) ) {
80             free_packet( n->pkt );
81             m_free( n->pkt );
82         }
83       #if USE_UNUSED_NODES
84         n->next = unused_nodes;
85         unused_nodes = n;
86       #else
87         m_free( n );
88       #endif
89         n = n2;
90     }
91 }
92
93
94 /****************
95  * Delete NODE from ROOT.  ROOT must exist!
96  * Note: This only works with walk_kbnode!!
97  */
98 void
99 delete_kbnode( KBNODE node )
100 {
101     node->private_flag |= 1;
102 }
103
104
105 /****************
106  * Append NODE to ROOT.  ROOT must exist!
107  */
108 void
109 add_kbnode( KBNODE root, KBNODE node )
110 {
111     KBNODE n1;
112
113     for(n1=root; n1->next; n1 = n1->next)
114         ;
115     n1->next = node;
116 }
117
118 /****************
119  * Insert NODE into the list after root but before a packet which is not of
120  * type PKTTYPE
121  * (only if PKTTYPE != 0)
122  */
123 void
124 insert_kbnode( KBNODE root, KBNODE node, int pkttype )
125 {
126     if( !pkttype ) {
127         node->next = root->next;
128         root->next = node;
129     }
130     else {
131         KBNODE n1;
132
133         for(n1=root; n1->next;  n1 = n1->next)
134             if( pkttype != n1->next->pkt->pkttype ) {
135                 node->next = n1->next;
136                 n1->next = node;
137                 return;
138             }
139         /* no such packet, append */
140         node->next = NULL;
141         n1->next = node;
142     }
143 }
144
145
146 /****************
147  * Find the previous node (if PKTTYPE = 0) or the previous node
148  * with pkttype PKTTYPE in the list starting with ROOT of NODE.
149  */
150 KBNODE
151 find_prev_kbnode( KBNODE root, KBNODE node, int pkttype )
152 {
153     KBNODE n1;
154
155     for(n1=NULL ; root && root != node; root = root->next )
156         if( !pkttype || root->pkt->pkttype == pkttype )
157             n1 = root;
158     return n1;
159 }
160
161 /****************
162  * Ditto, but find the next packet.  The behaviour is trivial if
163  * PKTTYPE is 0 but if it is specified, the next node with a packet
164  * of this type is returned.  The function has some knowledge about
165  * the valid ordering of packets: e.g. if the next signature packet
166  * is requested, the function will not return one if it encounters
167  * a user-id.
168  */
169 KBNODE
170 find_next_kbnode( KBNODE node, int pkttype )
171 {
172     for( node=node->next ; node; node = node->next ) {
173         if( !pkttype )
174             return node;
175         else if( pkttype == PKT_USER_ID
176                  && (   node->pkt->pkttype == PKT_PUBLIC_KEY
177                      || node->pkt->pkttype == PKT_SECRET_KEY ) )
178             return NULL;
179         else if( pkttype == PKT_SIGNATURE
180                  && (   node->pkt->pkttype == PKT_USER_ID
181                      || node->pkt->pkttype == PKT_PUBLIC_KEY
182                      || node->pkt->pkttype == PKT_SECRET_KEY ) )
183             return NULL;
184         else if( node->pkt->pkttype == pkttype )
185             return node;
186     }
187     return NULL;
188 }
189
190
191 KBNODE
192 find_kbnode( KBNODE node, int pkttype )
193 {
194     for( ; node; node = node->next ) {
195         if( node->pkt->pkttype == pkttype )
196             return node;
197     }
198     return NULL;
199 }
200
201
202
203 /****************
204  * Walk through a list of kbnodes. This function returns
205  * the next kbnode for each call; before using the function the first
206  * time, the caller must set CONTEXT to NULL (This has simply the effect
207  * to start with ROOT).
208  */
209 KBNODE
210 walk_kbnode( KBNODE root, KBNODE *context, int all )
211 {
212     KBNODE n;
213
214     do {
215         if( !*context ) {
216             *context = root;
217             n = root;
218         }
219         else {
220             n = (*context)->next;
221             *context = n;
222         }
223     } while( !all && n && (n->private_flag & 1) );
224
225     return n;
226 }
227
228 void
229 clear_kbnode_flags( KBNODE n )
230 {
231     for( ; n; n = n->next ) {
232         n->flag = 0;
233     }
234 }
235
236
237 /****************
238  * Commit changes made to the kblist at ROOT. Note that ROOT my change,
239  * and it is therefore passed by reference.
240  * The function has the effect of removing all nodes marked as deleted.
241  * returns true if any node has been changed
242  */
243 int
244 commit_kbnode( KBNODE *root )
245 {
246     KBNODE n, nl;
247     int changed = 0;
248
249     for( n = *root, nl=NULL; n; n = nl->next ) {
250         if( (n->private_flag & 1) ) {
251             if( n == *root )
252                 *root = nl = n->next;
253             else
254                 nl->next = n->next;
255             if( !(n->private_flag & 2) ) {
256                 free_packet( n->pkt );
257                 m_free( n->pkt );
258             }
259           #if USE_UNUSED_NODES
260             n->next = unused_nodes;
261             unused_nodes = n;
262           #else
263             m_free( n );
264           #endif
265             changed = 1;
266         }
267         else
268             nl = n;
269     }
270     return changed;
271 }
272
273
274 void
275 dump_kbnode( KBNODE node )
276 {
277     for(; node; node = node->next ) {
278         const char *s;
279         switch( node->pkt->pkttype ) {
280           case 0:               s="empty"; break;
281           case PKT_PUBLIC_KEY:  s="public-key"; break;
282           case PKT_SECRET_KEY:  s="secret-key"; break;
283           case PKT_SECRET_SUBKEY: s= "secret-subkey"; break;
284           case PKT_PUBKEY_ENC:  s="public-enc"; break;
285           case PKT_SIGNATURE:   s="signature"; break;
286           case PKT_ONEPASS_SIG: s="onepass-sig"; break;
287           case PKT_USER_ID:     s="user-id"; break;
288           case PKT_PUBLIC_SUBKEY: s="public-subkey"; break;
289           case PKT_COMMENT:     s="comment"; break;
290           case PKT_RING_TRUST:  s="trust"; break;
291           case PKT_PLAINTEXT:   s="plaintext"; break;
292           case PKT_COMPRESSED:  s="compressed"; break;
293           case PKT_ENCRYPTED:   s="encrypted"; break;
294           default:              s="unknown"; break;
295         }
296         fprintf(stderr, "node %p %02x/%02x type=%s",
297                 node, node->flag, node->private_flag, s);
298         if( node->pkt->pkttype == PKT_USER_ID ) {
299             fputs("  \"", stderr);
300             print_string( stderr, node->pkt->pkt.user_id->name,
301                                   node->pkt->pkt.user_id->len, 0 );
302             fputs("\"\n", stderr);
303         }
304         else if( node->pkt->pkttype == PKT_SIGNATURE ) {
305             fprintf(stderr, "  keyid=%08lX\n",
306                    (ulong)node->pkt->pkt.signature->keyid[1] );
307         }
308         else if( node->pkt->pkttype == PKT_PUBLIC_KEY
309                  || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
310             fprintf(stderr, "  keyid=%08lX\n", (ulong)
311                   keyid_from_pk( node->pkt->pkt.public_key, NULL ));
312         }
313         else
314             fputs("\n", stderr);
315     }
316 }
317