* g10.c (main, rm_group): Add --ungroup command to remove a particular
[gnupg.git] / g10 / keylist.c
1 /* keylist.c
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
3  *               2004 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 2 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, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "options.h"
30 #include "packet.h"
31 #include "errors.h"
32 #include "keydb.h"
33 #include "memory.h"
34 #include "photoid.h"
35 #include "util.h"
36 #include "ttyio.h"
37 #include "trustdb.h"
38 #include "main.h"
39 #include "i18n.h"
40 #include "status.h"
41
42 static void list_all(int);
43 static void list_one( STRLIST names, int secret);
44
45 struct sig_stats
46 {
47   int inv_sigs;
48   int no_key;
49   int oth_err;
50 };
51
52 static FILE *attrib_fp=NULL;
53
54 /****************
55  * List the keys
56  * If list is NULL, all available keys are listed
57  */
58 void
59 public_key_list( STRLIST list )
60 {
61   if(opt.with_colons)
62     {
63       byte trust_model,marginals,completes,cert_depth;
64       ulong created,nextcheck;
65
66       read_trust_options(&trust_model,&created,&nextcheck,
67                          &marginals,&completes,&cert_depth);
68
69       printf("tru:");
70
71       if(nextcheck && nextcheck <= make_timestamp())
72         printf("o");
73       if(trust_model!=opt.trust_model)
74         printf("t");
75       if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
76         {
77           if(marginals!=opt.marginals_needed)
78             printf("m");
79           if(completes!=opt.completes_needed)
80             printf("c");
81           if(cert_depth!=opt.max_cert_depth)
82             printf("d");
83         }
84
85       printf(":%d:%lu:%lu",trust_model,created,nextcheck);
86
87       /* Only show marginals, completes, and cert_depth in the classic
88          or PGP trust models since they are not meaningful
89          otherwise. */
90
91       if(trust_model==TM_PGP || trust_model==TM_CLASSIC)
92         printf(":%d:%d:%d",marginals,completes,cert_depth);
93
94       printf("\n");
95     }
96
97   if( !list )
98     list_all(0);
99   else
100     list_one( list, 0 );
101 }
102
103 void
104 secret_key_list( STRLIST list )
105 {
106     if( !list )
107         list_all(1);
108     else  /* List by user id */
109         list_one( list, 1 );
110 }
111
112 void
113 print_seckey_info (PKT_secret_key *sk)
114 {
115     u32 sk_keyid[2];
116     size_t n;
117     char *p;
118
119     keyid_from_sk (sk, sk_keyid);
120     tty_printf ("\nsec  %4u%c/%08lX %s   ",
121                 nbits_from_sk (sk),
122                 pubkey_letter (sk->pubkey_algo),
123                 (ulong)sk_keyid[1], datestr_from_sk (sk));
124     
125     p = get_user_id (sk_keyid, &n);
126     tty_print_utf8_string (p, n);
127     m_free (p);
128
129     tty_printf ("\n");   
130 }
131
132 /* Print information about the public key.  With FP passed as NULL,
133    the tty output interface is used, otherwise output is directted to
134    the given stream. */
135 void
136 print_pubkey_info (FILE *fp, PKT_public_key *pk)
137 {
138   u32 pk_keyid[2];
139   size_t n;
140   char *p;
141
142   keyid_from_pk (pk, pk_keyid);
143   if (fp)
144     fprintf (fp, "pub  %4u%c/%08lX %s   ",
145              nbits_from_pk (pk),
146              pubkey_letter (pk->pubkey_algo),
147              (ulong)pk_keyid[1], datestr_from_pk (pk));
148   else
149     tty_printf ("\npub  %4u%c/%08lX %s   ",
150                 nbits_from_pk (pk),
151                 pubkey_letter (pk->pubkey_algo),
152                 (ulong)pk_keyid[1], datestr_from_pk (pk));
153
154   p = get_user_id (pk_keyid, &n);
155   if (fp)
156     print_utf8_string2 (fp, p, n, '\n');
157   else
158     tty_print_utf8_string (p, n);
159   m_free (p);
160   
161   if (fp)
162     putc ('\n', fp);
163   else
164     tty_printf ("\n\n"); 
165 }
166
167 /*
168   mode=0 for stdout.
169   mode=1 for log_info + status messages
170   mode=2 for status messages only
171 */
172
173 void
174 show_policy_url(PKT_signature *sig,int indent,int mode)
175 {
176   const byte *p;
177   size_t len;
178   int seq=0,crit;
179   FILE *fp=mode?log_stream():stdout;
180
181   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len,&seq,&crit)))
182     {
183       if(mode!=2)
184         {
185           int i;
186           char *str;
187
188           for(i=0;i<indent;i++)
189             putchar(' ');
190
191           if(crit)
192             str=_("Critical signature policy: ");
193           else
194             str=_("Signature policy: ");
195           if(mode)
196             log_info("%s",str);
197           else
198             printf("%s",str);
199           print_utf8_string(fp,p,len);
200           fprintf(fp,"\n");
201         }
202
203       if(mode)
204         write_status_buffer ( STATUS_POLICY_URL, p, len, 0 );
205     }
206 }
207
208 /*
209   mode=0 for stdout.
210   mode=1 for log_info + status messages
211   mode=2 for status messages only
212 */
213 /* TODO: use this */
214 void
215 show_keyserver_url(PKT_signature *sig,int indent,int mode)
216 {
217   const byte *p;
218   size_t len;
219   int seq=0,crit;
220   FILE *fp=mode?log_stream():stdout;
221
222   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&len,&seq,&crit)))
223     {
224       if(mode!=2)
225         {
226           int i;
227           char *str;
228
229           for(i=0;i<indent;i++)
230             putchar(' ');
231
232           if(crit)
233             str=_("Critical preferred keyserver: ");
234           else
235             str=_("Preferred keyserver: ");
236           if(mode)
237             log_info("%s",str);
238           else
239             printf("%s",str);
240           print_utf8_string(fp,p,len);
241           fprintf(fp,"\n");
242         }
243
244       /* TODO: put in a status-fd tag for preferred keyservers */
245     }
246 }
247
248 /*
249   mode=0 for stdout.
250   mode=1 for log_info + status messages
251   mode=2 for status messages only
252 */
253
254 void
255 show_notation(PKT_signature *sig,int indent,int mode)
256 {
257   const byte *p;
258   size_t len;
259   int seq=0,crit;
260   FILE *fp=mode?log_stream():stdout;
261
262   /* There may be multiple notations in the same sig. */
263
264   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit)))
265     if(len>=8)
266       {
267         int n1,n2;
268
269         n1=(p[4]<<8)|p[5];
270         n2=(p[6]<<8)|p[7];
271
272         if(8+n1+n2!=len)
273           {
274             log_info(_("WARNING: invalid notation data found\n"));
275             return;
276           }
277
278         if(mode!=2)
279           {
280             int i;
281             char *str;
282
283             for(i=0;i<indent;i++)
284               putchar(' ');
285
286             /* This is UTF8 */
287             if(crit)
288               str=_("Critical signature notation: ");
289             else
290               str=_("Signature notation: ");
291             if(mode)
292               log_info("%s",str);
293             else
294               printf("%s",str);
295             print_utf8_string(fp,p+8,n1);
296             fprintf(fp,"=");
297
298             if(*p&0x80)
299               print_utf8_string(fp,p+8+n1,n2);
300             else
301               fprintf(fp,"[ %s ]",_("not human readable"));
302
303             fprintf(fp,"\n");
304           }
305
306         if(mode)
307           {
308             write_status_buffer ( STATUS_NOTATION_NAME, p+8   , n1, 0 );
309             write_status_buffer ( STATUS_NOTATION_DATA, p+8+n1, n2, 50 );
310           }
311       }
312   else
313     log_info(_("WARNING: invalid notation data found\n"));
314 }
315
316 static void
317 print_signature_stats(struct sig_stats *s)
318 {
319   if( s->inv_sigs == 1 )
320     tty_printf(_("1 bad signature\n") );
321   else if( s->inv_sigs )
322     tty_printf(_("%d bad signatures\n"), s->inv_sigs );
323   if( s->no_key == 1 )
324     tty_printf(_("1 signature not checked due to a missing key\n") );
325   else if( s->no_key )
326     tty_printf(_("%d signatures not checked due to missing keys\n"),s->no_key);
327   if( s->oth_err == 1 )
328     tty_printf(_("1 signature not checked due to an error\n") );
329   else if( s->oth_err )
330     tty_printf(_("%d signatures not checked due to errors\n"), s->oth_err );
331 }
332
333 static void
334 list_all( int secret )
335 {
336     KEYDB_HANDLE hd;
337     KBNODE keyblock = NULL;
338     int rc=0;
339     const char *lastresname, *resname;
340     struct sig_stats stats;
341
342     memset(&stats,0,sizeof(stats));
343
344     hd = keydb_new (secret);
345     if (!hd)
346         rc = G10ERR_GENERAL;
347     else
348         rc = keydb_search_first (hd);
349     if( rc ) {
350         if( rc != -1 )
351             log_error("keydb_search_first failed: %s\n", g10_errstr(rc) );
352         goto leave;
353     }
354
355     lastresname = NULL;
356     do {
357         rc = keydb_get_keyblock (hd, &keyblock);
358         if (rc) {
359             log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
360             goto leave;
361         }
362         if(!opt.with_colons)
363           {
364             resname = keydb_get_resource_name (hd);
365             if (lastresname != resname )
366               {
367                 int i;
368
369                 printf("%s\n", resname );
370                 for(i=strlen(resname); i; i-- )
371                   putchar('-');
372                 putchar('\n');
373                 lastresname = resname;
374               }
375           }
376         merge_keys_and_selfsig( keyblock );
377         list_keyblock( keyblock, secret, opt.fingerprint,
378                        opt.check_sigs?&stats:NULL);
379         release_kbnode( keyblock ); 
380         keyblock = NULL;
381     } while (!(rc = keydb_search_next (hd)));
382     if( rc && rc != -1 )
383         log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
384
385     if(opt.check_sigs && !opt.with_colons)
386       print_signature_stats(&stats);
387
388   leave:
389     release_kbnode (keyblock);
390     keydb_release (hd);
391 }
392
393
394 static void
395 list_one( STRLIST names, int secret )
396 {
397     int rc = 0;
398     KBNODE keyblock = NULL;
399     GETKEY_CTX ctx;
400     const char *resname;
401     char *keyring_str = _("Keyring");
402     int i;
403     struct sig_stats stats;
404
405     memset(&stats,0,sizeof(stats));
406
407     /* fixme: using the bynames function has the disadvantage that we
408      * don't know wether one of the names given was not found.  OTOH,
409      * this function has the advantage to list the names in the
410      * sequence as defined by the keyDB and does not duplicate
411      * outputs.  A solution could be do test whether all given have
412      * been listed (this needs a way to use the keyDB search
413      * functions) or to have the search function return indicators for
414      * found names.  Yet another way is to use the keydb search
415      * facilities directly. */
416     if( secret ) {
417         rc = get_seckey_bynames( &ctx, NULL, names, &keyblock );
418         if( rc ) {
419             log_error("error reading key: %s\n",  g10_errstr(rc) );
420             get_seckey_end( ctx );
421             return;
422         }
423         do {
424             if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
425                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
426                 printf("%s: %s\n", keyring_str, resname);
427                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
428                     putchar('-');
429                 putchar('\n');
430             }
431             list_keyblock( keyblock, 1, opt.fingerprint, NULL );
432             release_kbnode( keyblock );
433         } while( !get_seckey_next( ctx, NULL, &keyblock ) );
434         get_seckey_end( ctx );
435     }
436     else {
437         rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock );
438         if( rc ) {
439             log_error("error reading key: %s\n", g10_errstr(rc) );
440             get_pubkey_end( ctx );
441             return;
442         }
443         do {
444           if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
445                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
446                 printf("%s: %s\n", keyring_str, resname);
447                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
448                     putchar('-');
449                 putchar('\n');
450             }
451             list_keyblock( keyblock, 0, opt.fingerprint,
452                            opt.check_sigs?&stats:NULL );
453             release_kbnode( keyblock );
454         } while( !get_pubkey_next( ctx, NULL, &keyblock ) );
455         get_pubkey_end( ctx );
456     }
457
458     if(opt.check_sigs && !opt.with_colons)
459       print_signature_stats(&stats);
460 }
461
462 static void
463 print_key_data( PKT_public_key *pk, u32 *keyid )
464 {
465     int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
466     int i;
467
468     for(i=0; i < n; i++ ) {
469         printf("pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
470         mpi_print(stdout, pk->pkey[i], 1 );
471         putchar(':');
472         putchar('\n');
473     }
474 }
475
476 static void
477 print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock)
478 {
479   if(pk || (sk && sk->protect.s2k.mode!=1001))
480     {
481       unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage;
482     
483       if ( use & PUBKEY_USAGE_ENC )
484         putchar ('e');
485
486       if ( use & PUBKEY_USAGE_SIG )
487         {
488           putchar ('s');
489           if( pk? pk->is_primary : sk->is_primary )
490             putchar ('c');
491         }
492
493       if ( (use & PUBKEY_USAGE_AUTH) )
494         putchar ('a');
495     }
496
497     if ( keyblock ) { /* figure out the usable capabilities */
498         KBNODE k;
499         int enc=0, sign=0, cert=0, auth=0, disabled=0;
500
501         for (k=keyblock; k; k = k->next ) {
502             if ( k->pkt->pkttype == PKT_PUBLIC_KEY 
503                  || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
504                 pk = k->pkt->pkt.public_key;
505
506                 if(pk->is_primary)
507                   disabled=pk_is_disabled(pk);
508
509                 if ( pk->is_valid && !pk->is_revoked && !pk->has_expired ) {
510                     if ( pk->pubkey_usage & PUBKEY_USAGE_ENC )
511                         enc = 1;
512                     if ( pk->pubkey_usage & PUBKEY_USAGE_SIG )
513                       {
514                         sign = 1;
515                         if(pk->is_primary)
516                           cert = 1;
517                       }
518                     if ( (pk->pubkey_usage & PUBKEY_USAGE_AUTH) )
519                       auth = 1;
520                 }
521             }
522             else if ( k->pkt->pkttype == PKT_SECRET_KEY 
523                       || k->pkt->pkttype == PKT_SECRET_SUBKEY ) {
524                 sk = k->pkt->pkt.secret_key;
525                 if ( sk->is_valid && !sk->is_revoked && !sk->has_expired
526                      && sk->protect.s2k.mode!=1001 ) {
527                     if ( sk->pubkey_usage & PUBKEY_USAGE_ENC )
528                         enc = 1;
529                     if ( sk->pubkey_usage & PUBKEY_USAGE_SIG )
530                       {
531                         sign = 1;
532                         if(sk->is_primary)
533                           cert = 1;
534                       }
535                     if ( (sk->pubkey_usage & PUBKEY_USAGE_AUTH) )
536                         auth = 1;
537                 }
538             }
539         }
540         if (enc)
541             putchar ('E');
542         if (sign)
543             putchar ('S');
544         if (cert)
545             putchar ('C');
546         if (auth)
547             putchar ('A');
548         if (disabled)
549             putchar ('D');
550     }
551
552     putchar(':');
553 }
554
555 void
556 dump_attribs(const PKT_user_id *uid,PKT_public_key *pk,PKT_secret_key *sk)
557 {
558   int i;
559
560   if(!attrib_fp)
561     return;
562
563   for(i=0;i<uid->numattribs;i++)
564     {
565       if(is_status_enabled())
566         {
567           byte array[MAX_FINGERPRINT_LEN], *p;
568           char buf[(MAX_FINGERPRINT_LEN*2)+90];
569           size_t j,n;
570
571           if(pk)
572             fingerprint_from_pk( pk, array, &n );
573           else if(sk)
574             fingerprint_from_sk( sk, array, &n );
575           else
576             BUG();
577
578           p = array;
579           for(j=0; j < n ; j++, p++ )
580             sprintf(buf+2*j, "%02X", *p );
581
582           sprintf(buf+strlen(buf)," %lu %u %u %u %lu %lu %u",
583                   (ulong)uid->attribs[i].len,uid->attribs[i].type,i+1,
584                   uid->numattribs,(ulong)uid->created,(ulong)uid->expiredate,
585                   ((uid->is_primary?0x01:0)|
586                    (uid->is_revoked?0x02:0)|
587                    (uid->is_expired?0x04:0)));
588           write_status_text(STATUS_ATTRIBUTE,buf);
589         }
590
591       fwrite(uid->attribs[i].data,uid->attribs[i].len,1,attrib_fp);
592     }
593 }
594
595 static void
596 list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque )
597 {
598     int rc = 0;
599     KBNODE kbctx;
600     KBNODE node;
601     PKT_public_key *pk;
602     PKT_secret_key *sk;
603     u32 keyid[2];
604     int any=0;
605     struct sig_stats *stats=opaque;
606     int skip_sigs=0;
607     int newformat=((opt.list_options&LIST_SHOW_VALIDITY) && !secret)
608       || (opt.list_options & (LIST_SHOW_LONG_KEYIDS|LIST_SHOW_UNUSABLE_UIDS));
609
610     /* get the keyid from the keyblock */
611     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
612     if( !node ) {
613         log_error("Oops; key lost!\n");
614         dump_kbnode( keyblock );
615         return;
616     }
617
618     if( secret )
619       {
620         pk = NULL;
621         sk = node->pkt->pkt.secret_key;
622         keyid_from_sk( sk, keyid );
623
624         printf("sec%c  %4u%c/",(sk->protect.s2k.mode==1001)?'#':
625                                (sk->protect.s2k.mode==1002)?'>':' ',
626                nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ));
627
628         if(opt.list_options&LIST_SHOW_LONG_KEYIDS)
629           printf("%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
630         else
631           printf("%08lX",(ulong)keyid[1]);
632
633         printf(" %s%s",datestr_from_sk( sk ),newformat?"":" " );
634
635         if(newformat && sk->expiredate )
636           printf(_(" [expires: %s]"), expirestr_from_sk( sk ) );
637       }
638     else
639       {
640 #if 0
641         int validity;
642 #endif
643         pk = node->pkt->pkt.public_key;
644         sk = NULL;
645         keyid_from_pk( pk, keyid );
646
647 #if 0
648         validity=get_validity(pk,NULL);
649 #endif
650
651         check_trustdb_stale();
652
653         printf("pub   %4u%c/",
654                nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo));
655
656         if(opt.list_options&LIST_SHOW_LONG_KEYIDS)
657           printf("%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
658         else
659           printf("%08lX",(ulong)keyid[1]);
660
661         printf(" %s%s",datestr_from_pk( pk ),newformat?"":" " );
662
663         /* We didn't include this before in the key listing, but there
664            is room in the new format, so why not? */
665         if(newformat && pk->expiredate)
666           printf(_(" [expires: %s]"), expirestr_from_pk( pk ) );
667
668 #if 0
669         /* I need to think about this some more.  It's easy enough to
670            include, but it looks sort of confusing in the
671            listing... */
672         if(opt.list_options&LIST_SHOW_VALIDITY)
673           printf(" [%s]",trust_value_to_string(validity));
674 #endif
675       }
676
677     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
678         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
679             int indent;
680             PKT_user_id *uid=node->pkt->pkt.user_id;
681
682             if((uid->is_expired || uid->is_revoked)
683                && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS))
684               {
685                 skip_sigs=1;
686                 continue;
687               }
688             else
689               skip_sigs=0;
690
691             if(attrib_fp && uid->attrib_data!=NULL)
692               dump_attribs(uid,pk,sk);
693
694             if(!any && newformat)
695               printf("\n");
696
697             if(uid->is_revoked || uid->is_expired)
698               printf("uid%*s[%s] ",
699                      (opt.list_options&LIST_SHOW_LONG_KEYIDS)?16:8,"",
700                      uid->is_revoked?"revoked":"expired");
701             else if((opt.list_options&LIST_SHOW_VALIDITY) && pk)
702               {
703                 const char *validity=
704                   trust_value_to_string(get_validity(pk,uid));
705
706                 /* Includes the 3 spaces for [, ], and " ". */
707                 indent=((opt.list_options&LIST_SHOW_LONG_KEYIDS)?23:15)
708                   -strlen(validity);
709
710                 if(indent<0)
711                   indent=0;
712
713                 printf("uid%*s[%s] ",indent,"",validity);
714               }
715             else if(newformat)
716               printf("uid%*s",
717                      (opt.list_options&LIST_SHOW_LONG_KEYIDS)?26:18,"");
718             else if(any)
719               printf("uid%*s",29,"");
720
721             print_utf8_string( stdout, uid->name, uid->len );
722             putchar('\n');
723             if( !any ) {
724                 if( fpr )
725                     print_fingerprint( pk, sk, 0 );
726                 if( opt.with_key_data )
727                     print_key_data( pk, keyid );
728                 any = 1;
729             }
730
731             if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL)
732               show_photos(uid->attribs,uid->numattribs,pk,sk);
733         }
734         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
735             u32 keyid2[2];
736             PKT_public_key *pk2 = node->pkt->pkt.public_key;
737
738             if( !any ) {
739                 putchar('\n');
740                 if( fpr )
741                     print_fingerprint( pk, sk, 0 ); /* of the main key */
742                 any = 1;
743             }
744
745             keyid_from_pk( pk2, keyid2 );
746             printf("sub   %4u%c/",
747                    nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ));
748             if(opt.list_options&LIST_SHOW_LONG_KEYIDS)
749               printf("%08lX%08lX",(ulong)keyid2[0],(ulong)keyid2[1]);
750             else
751               printf("%08lX",(ulong)keyid2[1]);
752             printf(" %s",datestr_from_pk(pk2));
753             if( pk2->expiredate )
754               printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) );
755             putchar('\n');
756             if( fpr > 1 )
757                 print_fingerprint( pk2, NULL, 0 );
758             if( opt.with_key_data )
759                 print_key_data( pk2, keyid2 );
760         }
761         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
762             u32 keyid2[2];
763             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
764
765             if( !any ) {
766                 putchar('\n');
767                 if( fpr )
768                     print_fingerprint( pk, sk, 0 ); /* of the main key */
769                 any = 1;
770             }
771
772             keyid_from_sk( sk2, keyid2 );
773             printf("ssb%c  %4u%c/",
774                    (sk->protect.s2k.mode==1001)?'#':
775                    (sk->protect.s2k.mode==1002)?'>':' ',
776                    nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ));
777             if(opt.list_options&LIST_SHOW_LONG_KEYIDS)
778               printf("%08lX%08lX",(ulong)keyid2[0],(ulong)keyid2[1]);
779             else
780               printf("%08lX",(ulong)keyid2[1]);
781             printf(" %s",datestr_from_sk( sk2 ) );
782             if( sk2->expiredate )
783               printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) );
784             putchar('\n');
785             if( fpr > 1 )
786                 print_fingerprint( NULL, sk2, 0 );
787         }
788         else if( opt.list_sigs
789                  && node->pkt->pkttype == PKT_SIGNATURE
790                  && !skip_sigs ) {
791             PKT_signature *sig = node->pkt->pkt.signature;
792             int sigrc;
793             char *sigstr;
794
795             if( stats ) {
796                 /*fflush(stdout);*/
797                 rc = check_key_signature( keyblock, node, NULL );
798                 switch( rc ) {
799                  case 0:                 sigrc = '!'; break;
800                  case G10ERR_BAD_SIGN:   stats->inv_sigs++; sigrc = '-'; break;
801                  case G10ERR_NO_PUBKEY: 
802                  case G10ERR_UNU_PUBKEY: stats->no_key++; continue;
803                  default:                stats->oth_err++; sigrc = '%'; break;
804                 }
805
806                 /* TODO: Make sure a cached sig record here still has
807                    the pk that issued it.  See also
808                    keyedit.c:print_and_check_one_sig */
809
810             }
811             else {
812                 rc = 0;
813                 sigrc = ' ';
814             }
815
816             if( !any ) { /* no user id, (maybe a revocation follows)*/
817               /* Check if the pk is really revoked - there could be a
818                  0x20 sig packet there even if we are not revoked
819                  (say, if a revocation key issued the packet, but the
820                  revocation key isn't present to verify it.) */
821                 if( sig->sig_class == 0x20 && pk->is_revoked )
822                     puts("[revoked]");
823                 else if( sig->sig_class == 0x18 )
824                     puts("[key binding]");
825                 else if( sig->sig_class == 0x28 )
826                     puts("[subkey revoked]");
827                 else
828                     putchar('\n');
829                 if( fpr )
830                     print_fingerprint( pk, sk, 0 );
831                 any=1;
832             }
833
834             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
835                                        || sig->sig_class == 0x30 )
836                sigstr = "rev";
837             else if( (sig->sig_class&~3) == 0x10 )
838                sigstr = "sig";
839             else if( sig->sig_class == 0x18 )
840                sigstr = "sig";
841             else if( sig->sig_class == 0x1F )
842                sigstr = "sig";
843             else {
844                 printf("sig                             "
845                        "[unexpected signature class 0x%02x]\n",sig->sig_class );
846                 continue;
847             }
848
849             fputs( sigstr, stdout );
850             printf("%c%c %c%c%c%c%c%c ",
851                    sigrc,(sig->sig_class-0x10>0 &&
852                           sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
853                    sig->flags.exportable?' ':'L',
854                    sig->flags.revocable?' ':'R',
855                    sig->flags.policy_url?'P':' ',
856                    sig->flags.notation?'N':' ',
857                    sig->flags.expired?'X':' ',
858                    (sig->trust_depth>9)?'T':
859                    (sig->trust_depth>0)?'0'+sig->trust_depth:' ');
860             if(opt.list_options&LIST_SHOW_LONG_KEYIDS)
861               printf("%08lX%08lX",(ulong)sig->keyid[0],(ulong)sig->keyid[1]);
862             else
863               printf("%08lX",(ulong)sig->keyid[1]);
864             printf(" %s", datestr_from_sig(sig));
865             if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
866               printf(" %s", expirestr_from_sig(sig));
867             printf("  ");
868             if( sigrc == '%' )
869                 printf("[%s] ", g10_errstr(rc) );
870             else if( sigrc == '?' )
871                 ;
872             else if ( !opt.fast_list_mode ) {
873                 size_t n;
874                 char *p = get_user_id( sig->keyid, &n );
875                 print_utf8_string( stdout, p, n );
876                 m_free(p);
877             }
878             putchar('\n');
879
880             if(sig->flags.policy_url
881                && (opt.list_options&LIST_SHOW_POLICY_URLS))
882               show_policy_url(sig,3,0);
883
884             if(sig->flags.notation
885                && (opt.list_options&LIST_SHOW_NOTATIONS))
886               show_notation(sig,3,0);
887
888             if(sig->flags.pref_ks
889                && (opt.list_options&LIST_SHOW_KEYSERVER_URLS))
890               show_keyserver_url(sig,3,0);
891
892             /* fixme: check or list other sigs here */
893         }
894     }
895     putchar('\n');
896 }
897
898
899 static void
900 list_keyblock_colon( KBNODE keyblock, int secret, int fpr )
901 {
902     int rc = 0;
903     KBNODE kbctx;
904     KBNODE node;
905     PKT_public_key *pk;
906     PKT_secret_key *sk;
907     u32 keyid[2];
908     int any=0;
909     int trustletter = 0;
910     int ulti_hack = 0;
911     int i;
912
913     /* get the keyid from the keyblock */
914     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
915     if( !node ) {
916         log_error("Oops; key lost!\n");
917         dump_kbnode( keyblock );
918         return;
919     }
920
921     if( secret ) {
922         pk = NULL;
923         sk = node->pkt->pkt.secret_key;
924         keyid_from_sk( sk, keyid );
925         printf("sec::%u:%d:%08lX%08lX:%s:%s:::",
926                     nbits_from_sk( sk ),
927                     sk->pubkey_algo,
928                     (ulong)keyid[0],(ulong)keyid[1],
929                     colon_datestr_from_sk( sk ),
930                     colon_strtime (sk->expiredate)
931                     /* fixme: add LID here */ );
932     }
933     else {
934         pk = node->pkt->pkt.public_key;
935         sk = NULL;
936         keyid_from_pk( pk, keyid );
937         fputs( "pub:", stdout );
938         if ( !pk->is_valid )
939             putchar ('i');
940         else if ( pk->is_revoked )
941             putchar ('r');
942         else if ( pk->has_expired )
943             putchar ('e');
944         else if ( opt.fast_list_mode || opt.no_expensive_trust_checks ) 
945             ;
946         else {
947             trustletter = get_validity_info ( pk, NULL );
948             if( trustletter == 'u' )
949                 ulti_hack = 1;
950             putchar(trustletter);
951         }
952         printf(":%u:%d:%08lX%08lX:%s:%s::",
953                     nbits_from_pk( pk ),
954                     pk->pubkey_algo,
955                     (ulong)keyid[0],(ulong)keyid[1],
956                     colon_datestr_from_pk( pk ),
957                     colon_strtime (pk->expiredate) );
958         if( !opt.fast_list_mode && !opt.no_expensive_trust_checks  )
959             putchar( get_ownertrust_info(pk) );
960             putchar(':');
961     }
962     
963     if (opt.fixed_list_mode) {
964         /* do not merge the first uid with the primary key */
965         putchar(':');
966         putchar(':');
967         print_capabilities (pk, sk, keyblock);
968         if (secret) {
969           putchar(':'); /* End of field 13. */
970           putchar(':'); /* End of field 14. */
971           if (sk->protect.s2k.mode == 1001)
972             putchar('#'); /* Key is just a stub. */
973           else if (sk->protect.s2k.mode == 1002) {
974             /* Key is stored on an external token (card) or handled by
975                the gpg-agent.  Print the serial number of that token
976                here. */
977             for (i=0; i < sk->protect.ivlen; i++)
978               printf ("%02X", sk->protect.iv[i]);
979           }
980           putchar(':'); /* End of field 15. */
981         }
982         putchar('\n');
983         if( fpr )
984             print_fingerprint( pk, sk, 0 );
985         if( opt.with_key_data )
986             print_key_data( pk, keyid );
987         any = 1;
988     }
989
990
991     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
992         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
993             PKT_user_id *uid=node->pkt->pkt.user_id;
994             if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL)
995               dump_attribs(node->pkt->pkt.user_id,pk,sk);
996             /*
997              * Fixme: We need a is_valid flag here too 
998              */
999             if( any ) {
1000                 char *str=uid->attrib_data?"uat":"uid";
1001                 /* If we're listing a secret key, leave out the
1002                    validity values for now.  This is handled better in
1003                    1.9. */
1004                 if ( sk )
1005                     printf("%s:::::",str);
1006                 else if ( uid->is_revoked )
1007                     printf("%s:r::::",str);
1008                 else if ( uid->is_expired )
1009                     printf("%s:e::::",str);
1010                 else if ( opt.no_expensive_trust_checks )
1011                     printf("%s:::::",str);
1012                 else {
1013                     int uid_validity;
1014
1015                     if( pk && !ulti_hack )
1016                       uid_validity=get_validity_info (pk, uid);
1017                     else
1018                         uid_validity = 'u';
1019                     printf("%s:%c::::",str,uid_validity);
1020                 }
1021
1022                 printf("%s:",colon_strtime(uid->created));
1023                 printf("%s:",colon_strtime(uid->expiredate));
1024
1025                 namehash_from_uid(uid);
1026
1027                 for(i=0; i < 20; i++ )
1028                   printf("%02X",uid->namehash[i]);
1029
1030                 printf("::");
1031             }
1032             if(uid->attrib_data)
1033               printf("%u %lu",uid->numattribs,uid->attrib_len);
1034             else
1035               print_string(stdout,uid->name,uid->len, ':' );
1036             putchar(':');
1037             if (any)
1038                 putchar('\n');
1039             else {
1040                 putchar(':');
1041                 print_capabilities (pk, sk, keyblock);
1042                 putchar('\n');
1043                 if( fpr )
1044                     print_fingerprint( pk, sk, 0 );
1045                 if( opt.with_key_data )
1046                     print_key_data( pk, keyid );
1047                 any = 1;
1048             }
1049         }
1050         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
1051             u32 keyid2[2];
1052             PKT_public_key *pk2 = node->pkt->pkt.public_key;
1053
1054             if( !any ) {
1055                 putchar(':');
1056                 putchar(':');
1057                 print_capabilities (pk, sk, keyblock);
1058                 putchar('\n');
1059                 if( fpr )
1060                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1061                 any = 1;
1062             }
1063
1064             keyid_from_pk( pk2, keyid2 );
1065             fputs ("sub:", stdout );
1066             if ( !pk2->is_valid )
1067                 putchar ('i');
1068             else if ( pk2->is_revoked )
1069                 putchar ('r');
1070             else if ( pk2->has_expired )
1071                 putchar ('e');
1072             else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
1073                 ;
1074             else {
1075                 /* trustletter should always be defined here */
1076                 if(trustletter)
1077                   printf("%c", trustletter );
1078             }
1079             printf(":%u:%d:%08lX%08lX:%s:%s:::::",
1080                         nbits_from_pk( pk2 ),
1081                         pk2->pubkey_algo,
1082                         (ulong)keyid2[0],(ulong)keyid2[1],
1083                         colon_datestr_from_pk( pk2 ),
1084                         colon_strtime (pk2->expiredate)
1085                         /* fixme: add LID and ownertrust here */
1086                                                 );
1087             print_capabilities (pk2, NULL, NULL);
1088             putchar('\n');
1089             if( fpr > 1 )
1090                 print_fingerprint( pk2, NULL, 0 );
1091             if( opt.with_key_data )
1092                 print_key_data( pk2, keyid2 );
1093         }
1094         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1095             u32 keyid2[2];
1096             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
1097
1098             if( !any ) {
1099                 putchar(':');
1100                 putchar(':');
1101                 print_capabilities (pk, sk, keyblock);
1102                 putchar('\n');
1103                 if( fpr )
1104                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1105                 any = 1;
1106             }
1107
1108             keyid_from_sk( sk2, keyid2 );
1109             printf("ssb::%u:%d:%08lX%08lX:%s:%s:::::",
1110                         nbits_from_sk( sk2 ),
1111                         sk2->pubkey_algo,
1112                         (ulong)keyid2[0],(ulong)keyid2[1],
1113                         colon_datestr_from_sk( sk2 ),
1114                         colon_strtime (sk2->expiredate)
1115                    /* fixme: add LID */ );
1116             print_capabilities (NULL, sk2, NULL);
1117             if (opt.fixed_list_mode) {
1118               /* We print the serial number only in fixed list mode
1119                  for the primary key so, so avoid questions we print
1120                  it for subkeys also only in this mode.  There is no
1121                  technical reason, though. */
1122               putchar(':'); /* End of field 13. */
1123               putchar(':'); /* End of field 14. */
1124               if (sk2->protect.s2k.mode == 1001)
1125                 putchar('#'); /* Key is just a stub. */
1126               else if (sk2->protect.s2k.mode == 1002) {
1127                 /* Key is stored on an external token (card) or handled by
1128                    the gpg-agent.  Print the serial number of that token
1129                    here. */
1130                 for (i=0; i < sk2->protect.ivlen; i++)
1131                   printf ("%02X", sk2->protect.iv[i]);
1132               }
1133               putchar(':'); /* End of field 15. */
1134             }
1135             putchar ('\n');
1136             if( fpr > 1 )
1137               print_fingerprint( NULL, sk2, 0 );
1138         }
1139         else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) {
1140             PKT_signature *sig = node->pkt->pkt.signature;
1141             int sigrc,fprokay=0;
1142             char *sigstr;
1143             size_t fplen;
1144             byte fparray[MAX_FINGERPRINT_LEN];
1145
1146             if( !any ) { /* no user id, (maybe a revocation follows)*/
1147                 if( sig->sig_class == 0x20 )
1148                     fputs("[revoked]:", stdout);
1149                 else if( sig->sig_class == 0x18 )
1150                     fputs("[key binding]:", stdout);
1151                 else if( sig->sig_class == 0x28 )
1152                     fputs("[subkey revoked]:", stdout);
1153                 else
1154                     putchar (':');
1155                 putchar(':');
1156                 print_capabilities (pk, sk, keyblock);
1157                 putchar('\n');
1158                 if( fpr )
1159                     print_fingerprint( pk, sk, 0 );
1160                 any=1;
1161             }
1162
1163             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
1164                                        || sig->sig_class == 0x30 )
1165                sigstr = "rev";
1166             else if( (sig->sig_class&~3) == 0x10 )
1167                sigstr = "sig";
1168             else if( sig->sig_class == 0x18 )
1169                sigstr = "sig";
1170             else if( sig->sig_class == 0x1F )
1171                sigstr = "sig";
1172             else {
1173                 printf ("sig::::::::::%02x%c:\n",
1174                         sig->sig_class, sig->flags.exportable?'x':'l');
1175                 continue;
1176             }
1177             if( opt.check_sigs ) {
1178                 PKT_public_key *signer_pk=NULL;
1179
1180                 fflush(stdout);
1181                 if(opt.no_sig_cache)
1182                   signer_pk=m_alloc_clear(sizeof(PKT_public_key));
1183
1184                 rc = check_key_signature2( keyblock, node, NULL, signer_pk,
1185                                            NULL, NULL, NULL );
1186                 switch( rc ) {
1187                   case 0:                  sigrc = '!'; break;
1188                   case G10ERR_BAD_SIGN:    sigrc = '-'; break;
1189                   case G10ERR_NO_PUBKEY: 
1190                   case G10ERR_UNU_PUBKEY:  sigrc = '?'; break;
1191                   default:                 sigrc = '%'; break;
1192                 }
1193
1194                 if(opt.no_sig_cache)
1195                   {
1196                     if(rc==0)
1197                       {
1198                         fingerprint_from_pk (signer_pk, fparray, &fplen);
1199                         fprokay=1;
1200                       }
1201                     free_public_key(signer_pk);
1202                   }
1203             }
1204             else {
1205                 rc = 0;
1206                 sigrc = ' ';
1207             }
1208             fputs( sigstr, stdout );
1209             putchar(':');
1210             if( sigrc != ' ' )
1211                 putchar(sigrc);
1212             printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
1213                    (ulong)sig->keyid[0], (ulong)sig->keyid[1],
1214                    colon_datestr_from_sig(sig),
1215                    colon_expirestr_from_sig(sig));
1216
1217             if(sig->trust_depth || sig->trust_value)
1218               printf("%d %d",sig->trust_depth,sig->trust_value);
1219             printf(":");
1220
1221             if(sig->trust_regexp)
1222               print_string(stdout,sig->trust_regexp,
1223                            strlen(sig->trust_regexp),':');
1224             printf(":");
1225
1226             if( sigrc == '%' )
1227                 printf("[%s] ", g10_errstr(rc) );
1228             else if( sigrc == '?' )
1229                 ;
1230             else if ( !opt.fast_list_mode ) {
1231                 size_t n;
1232                 char *p = get_user_id( sig->keyid, &n );
1233                 print_string( stdout, p, n, ':' );
1234                 m_free(p);
1235             }
1236             printf(":%02x%c:", sig->sig_class,sig->flags.exportable?'x':'l');
1237
1238             if(opt.no_sig_cache && opt.check_sigs && fprokay)
1239               {
1240                 printf(":");
1241
1242                 for (i=0; i < fplen ; i++ )
1243                   printf ("%02X", fparray[i] );
1244
1245                 printf(":");
1246               }
1247
1248             printf("\n");
1249
1250             /* fixme: check or list other sigs here */
1251         }
1252     }
1253     if( !any ) {/* oops, no user id */
1254         putchar(':');
1255         putchar(':');
1256         print_capabilities (pk, sk, keyblock);
1257         putchar('\n');
1258     }
1259 }
1260
1261 /*
1262  * Reorder the keyblock so that the primary user ID (and not attribute
1263  * packet) comes first.  Fixme: Replace this by a generic sort
1264  * function.  */
1265 void
1266 reorder_keyblock (KBNODE keyblock)
1267 {
1268     KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
1269     KBNODE last, node;
1270
1271     for (node=keyblock; node; primary0=node, node = node->next) {
1272         if( node->pkt->pkttype == PKT_USER_ID &&
1273             !node->pkt->pkt.user_id->attrib_data &&
1274             node->pkt->pkt.user_id->is_primary ) {
1275             primary = primary2 = node;
1276             for (node=node->next; node; primary2=node, node = node->next ) {
1277                 if( node->pkt->pkttype == PKT_USER_ID 
1278                     || node->pkt->pkttype == PKT_PUBLIC_SUBKEY 
1279                     || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1280                     break;
1281                 }
1282             }
1283             break;
1284         }
1285     }
1286     if ( !primary )
1287         return;  /* no primary key flag found (should not happen) */
1288
1289     for (last=NULL, node=keyblock; node; last = node, node = node->next) {
1290         if( node->pkt->pkttype == PKT_USER_ID )
1291             break;
1292     }
1293     assert (node);
1294     assert (last); /* the user ID is never the first packet */
1295     assert (primary0);  /* ditto (this is the node before primary) */
1296     if ( node == primary )
1297         return; /* already the first one */
1298
1299     last->next = primary;
1300     primary0->next = primary2->next;
1301     primary2->next = node;
1302 }
1303
1304 void
1305 list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque )
1306 {
1307     reorder_keyblock (keyblock);
1308     if (opt.with_colons)
1309         list_keyblock_colon (keyblock, secret, fpr );
1310     else
1311         list_keyblock_print (keyblock, secret, fpr, opaque );
1312 }
1313
1314 /*
1315  * standard function to print the finperprint.
1316  * mode 0: as used in key listings, opt.with_colons is honored
1317  *      1: print using log_info ()
1318  *      2: direct use of tty
1319  *      3: direct use of tty but only primary key.
1320  * modes 1 and 2 will try and print both subkey and primary key fingerprints
1321  */
1322 void
1323 print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode )
1324 {
1325     byte array[MAX_FINGERPRINT_LEN], *p;
1326     size_t i, n;
1327     FILE *fp;
1328     const char *text;
1329     int primary=0;
1330
1331     if(sk)
1332       {
1333         if(sk->main_keyid[0]==sk->keyid[0] && sk->main_keyid[1]==sk->keyid[1])
1334           primary=1;
1335       }
1336     else
1337       {
1338         if(pk->main_keyid[0]==pk->keyid[0] && pk->main_keyid[1]==pk->keyid[1])
1339           primary=1;
1340       }
1341
1342     /* Just to be safe */
1343     if(mode&0x80 && !primary)
1344       {
1345         log_error("primary key is not really primary!\n");
1346         return;
1347       }
1348
1349     mode&=~0x80;
1350
1351     if(!primary && (mode==1 || mode==2))
1352       {
1353         if(sk)
1354           {
1355             PKT_secret_key *primary_sk=m_alloc_clear(sizeof(*primary_sk));
1356             get_seckey(primary_sk,sk->main_keyid);
1357             print_fingerprint(NULL,primary_sk,mode|0x80);
1358             free_secret_key(primary_sk);
1359           }
1360         else
1361           {
1362             PKT_public_key *primary_pk=m_alloc_clear(sizeof(*primary_pk));
1363             get_pubkey(primary_pk,pk->main_keyid);
1364             print_fingerprint(primary_pk,NULL,mode|0x80);
1365             free_public_key(primary_pk);
1366           }
1367       }
1368
1369     if (mode == 1) {
1370         fp = log_stream ();
1371         if(primary)
1372           text = _("Primary key fingerprint:");
1373         else
1374           text = _("     Subkey fingerprint:");
1375     }
1376     else if (mode == 2) {
1377         fp = NULL; /* use tty */
1378         /* Translators: this should fit into 24 bytes to that the fingerprint
1379          * data is properly aligned with the user ID */
1380         if(primary)
1381           text = _(" Primary key fingerprint:");
1382         else
1383           text = _("      Subkey fingerprint:");
1384     }
1385     else if (mode == 3) {
1386         fp = NULL; /* use tty */
1387         text = _("      Key fingerprint =");
1388     }
1389     else {
1390         fp = stdout;
1391         text = _("      Key fingerprint =");
1392     }
1393   
1394     if (sk)
1395         fingerprint_from_sk (sk, array, &n);
1396     else
1397         fingerprint_from_pk (pk, array, &n);
1398     p = array;
1399     if (opt.with_colons && !mode) {
1400         fprintf (fp, "fpr:::::::::");
1401         for (i=0; i < n ; i++, p++ )
1402             fprintf (fp, "%02X", *p );
1403         putc(':', fp);
1404     }
1405     else {
1406         if (fp)
1407             fputs (text, fp);
1408         else
1409             tty_printf ("%s", text);
1410         if (n == 20) {
1411             for (i=0; i < n ; i++, i++, p += 2 ) {
1412                 if (fp) {
1413                     if (i == 10 )
1414                         putc(' ', fp);
1415                     fprintf (fp, " %02X%02X", *p, p[1] );
1416                 }
1417                 else {
1418                     if (i == 10 )
1419                         tty_printf (" ");
1420                     tty_printf (" %02X%02X", *p, p[1]);
1421                 }
1422             }
1423         }
1424         else {
1425             for (i=0; i < n ; i++, p++ ) {
1426                 if (fp) {
1427                     if (i && !(i%8) )
1428                         putc (' ', fp);
1429                     fprintf (fp, " %02X", *p );
1430                 }
1431                 else {
1432                     if (i && !(i%8) )
1433                         tty_printf (" ");
1434                     tty_printf (" %02X", *p );
1435                 }
1436             }
1437         }
1438     }
1439     if (fp)
1440         putc ('\n', fp);
1441     else
1442         tty_printf ("\n");
1443 }
1444
1445 void set_attrib_fd(int fd)
1446 {
1447   static int last_fd=-1;
1448
1449   if ( fd != -1 && last_fd == fd )
1450     return;
1451
1452   if ( attrib_fp && attrib_fp != stdout && attrib_fp != stderr )
1453     fclose (attrib_fp);
1454   attrib_fp = NULL;
1455   if ( fd == -1 ) 
1456     return;
1457
1458   if( fd == 1 )
1459     attrib_fp = stdout;
1460   else if( fd == 2 )
1461     attrib_fp = stderr;
1462   else
1463     attrib_fp = fdopen( fd, "wb" );
1464   if( !attrib_fp ) {
1465     log_fatal("can't open fd %d for attribute output: %s\n",
1466               fd, strerror(errno));
1467   }
1468
1469   last_fd = fd;
1470 }