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