1 /* audit.c - GnuPG's audit subsystem
2 * Copyright (C) 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
29 #include "audit-events.h"
31 /* A list to maintain a list of helptags. */
34 struct helptag_s
*next
;
37 typedef struct helptag_s
*helptag_t
;
43 audit_event_t event
; /* The event. */
44 gpg_error_t err
; /* The logged error code. */
45 int intvalue
; /* A logged interger value. */
46 char *string
; /* A malloced string or NULL. */
47 ksba_cert_t cert
; /* A certifciate or NULL. */
51 typedef struct log_item_s
*log_item_t
;
55 /* The main audit object. */
58 const char *failure
; /* If set a description of the internal failure. */
61 log_item_t log
; /* The table with the log entries. */
62 size_t logsize
; /* The allocated size for LOG. */
63 size_t logused
; /* The used size of LOG. */
65 estream_t outstream
; /* The current output stream. */
66 int use_html
; /* The output shall be HTML formatted. */
67 int indentlevel
; /* Current level of indentation. */
68 helptag_t helptags
; /* List of help keys. */
74 static void writeout_para (audit_ctx_t ctx
,
75 const char *format
, ...) JNLIB_GCC_A_PRINTF(2,3);
76 static void writeout_li (audit_ctx_t ctx
, const char *oktext
,
77 const char *format
, ...) JNLIB_GCC_A_PRINTF(3,4);
78 static void writeout_rem (audit_ctx_t ctx
,
79 const char *format
, ...) JNLIB_GCC_A_PRINTF(2,3);
82 /* Add NAME to the list of help tags. NAME needs to be a const string
83 an this function merly stores this pointer. */
85 add_helptag (audit_ctx_t ctx
, const char *name
)
89 for (item
=ctx
->helptags
; item
; item
= item
->next
)
90 if (!strcmp (item
->name
, name
))
91 return; /* Already in the list. */
92 item
= xtrycalloc (1, sizeof *item
);
94 return; /* Don't care about memory problems. */
96 item
->next
= ctx
->helptags
;
101 /* Remove all help tags from the context. */
103 clear_helptags (audit_ctx_t ctx
)
105 while (ctx
->helptags
)
107 helptag_t tmp
= ctx
->helptags
->next
;
108 xfree (ctx
->helptags
);
116 event2str (audit_event_t event
)
118 /* We need the cast so that compiler does not complain about an
119 always true comparison (>= 0) for an unsigned value. */
120 int idx
= eventstr_msgidxof ((int)event
);
122 return "Unknown event";
124 return eventstr_msgstr
+ eventstr_msgidx
[idx
];
129 /* Create a new audit context. In case of an error NULL is returned
130 and errno set appropriately. */
136 ctx
= xtrycalloc (1, sizeof *ctx
);
142 /* Release an audit context. Passing NULL for CTX is allowed and does
145 audit_release (audit_ctx_t ctx
)
152 for (idx
=0; idx
< ctx
->logused
; idx
++)
154 if (ctx
->log
[idx
].string
)
155 xfree (ctx
->log
[idx
].string
);
156 if (ctx
->log
[idx
].cert
)
157 ksba_cert_release (ctx
->log
[idx
].cert
);
161 clear_helptags (ctx
);
166 /* Set the type for the audit operation. If CTX is NULL, this is a
169 audit_set_type (audit_ctx_t ctx
, audit_type_t type
)
171 if (!ctx
|| ctx
->failure
)
172 return; /* Audit not enabled or an internal error has occurred. */
174 if (ctx
->type
&& ctx
->type
!= type
)
176 ctx
->failure
= "conflict in type initialization";
183 /* Create a new log item and put it into the table. Return that log
184 item on success; return NULL on memory failure and mark that in
187 create_log_item (audit_ctx_t ctx
)
189 log_item_t item
, table
;
195 table
= xtrymalloc (size
* sizeof *table
);
198 ctx
->failure
= "Out of memory in create_log_item";
206 else if (ctx
->logused
>= ctx
->logsize
)
208 size
= ctx
->logsize
+ 10;
209 table
= xtryrealloc (ctx
->log
, size
* sizeof *table
);
212 ctx
->failure
= "Out of memory while reallocating in create_log_item";
217 item
= ctx
->log
+ ctx
->logused
++;
220 item
= ctx
->log
+ ctx
->logused
++;
222 item
->event
= AUDIT_NULL_EVENT
;
226 item
->have_intvalue
= 0;
234 /* Add a new event to the audit log. If CTX is NULL, this function
237 audit_log (audit_ctx_t ctx
, audit_event_t event
)
241 if (!ctx
|| ctx
->failure
)
242 return; /* Audit not enabled or an internal error has occurred. */
245 ctx
->failure
= "Invalid event passed to audit_log";
248 if (!(item
= create_log_item (ctx
)))
253 /* Add a new event to the audit log. If CTX is NULL, this function
254 does nothing. This version also adds the result of the oepration
257 audit_log_ok (audit_ctx_t ctx
, audit_event_t event
, gpg_error_t err
)
261 if (!ctx
|| ctx
->failure
)
262 return; /* Audit not enabled or an internal error has occurred. */
265 ctx
->failure
= "Invalid event passed to audit_log_ok";
268 if (!(item
= create_log_item (ctx
)))
276 /* Add a new event to the audit log. If CTX is NULL, this function
277 does nothing. This version also add the integer VALUE to the log. */
279 audit_log_i (audit_ctx_t ctx
, audit_event_t event
, int value
)
283 if (!ctx
|| ctx
->failure
)
284 return; /* Audit not enabled or an internal error has occurred. */
287 ctx
->failure
= "Invalid event passed to audit_log_i";
290 if (!(item
= create_log_item (ctx
)))
293 item
->intvalue
= value
;
294 item
->have_intvalue
= 1;
298 /* Add a new event to the audit log. If CTX is NULL, this function
299 does nothing. This version also add the integer VALUE to the log. */
301 audit_log_s (audit_ctx_t ctx
, audit_event_t event
, const char *value
)
306 if (!ctx
|| ctx
->failure
)
307 return; /* Audit not enabled or an internal error has occurred. */
310 ctx
->failure
= "Invalid event passed to audit_log_s";
313 tmp
= xtrystrdup (value
? value
: "");
316 ctx
->failure
= "Out of memory in audit_event";
319 if (!(item
= create_log_item (ctx
)))
328 /* Add a new event to the audit log. If CTX is NULL, this function
329 does nothing. This version also adds the certificate CERT and the
330 result of an operation to the log. */
332 audit_log_cert (audit_ctx_t ctx
, audit_event_t event
,
333 ksba_cert_t cert
, gpg_error_t err
)
337 if (!ctx
|| ctx
->failure
)
338 return; /* Audit not enabled or an internal error has occurred. */
341 ctx
->failure
= "Invalid event passed to audit_log_cert";
344 if (!(item
= create_log_item (ctx
)))
351 ksba_cert_ref (cert
);
357 /* Write TEXT to the outstream. */
359 writeout (audit_ctx_t ctx
, const char *text
)
363 for (; *text
; text
++)
366 es_fputs ("<", ctx
->outstream
);
367 else if (*text
== '&')
368 es_fputs ("&", ctx
->outstream
);
370 es_putc (*text
, ctx
->outstream
);
374 es_fputs (text
, ctx
->outstream
);
378 /* Write TEXT to the outstream using a variable argument list. */
380 writeout_v (audit_ctx_t ctx
, const char *format
, va_list arg_ptr
)
384 estream_vasprintf (&buf
, format
, arg_ptr
);
391 writeout (ctx
, "[!!Out of core!!]");
395 /* Write TEXT as a paragraph. */
397 writeout_para (audit_ctx_t ctx
, const char *format
, ...)
402 es_fputs ("<p>", ctx
->outstream
);
403 va_start (arg_ptr
, format
) ;
404 writeout_v (ctx
, format
, arg_ptr
);
407 es_fputs ("</p>\n", ctx
->outstream
);
409 es_fputc ('\n', ctx
->outstream
);
414 enter_li (audit_ctx_t ctx
)
418 if (!ctx
->indentlevel
)
420 es_fputs ("<table border=\"0\">\n"
422 " <col width=\"80%\" />\n"
423 " <col width=\"20%\" />\n"
433 leave_li (audit_ctx_t ctx
)
438 if (!ctx
->indentlevel
)
439 es_fputs ("</table>\n", ctx
->outstream
);
444 /* Write TEXT as a list element. If OKTEXT is not NULL, append it to
447 writeout_li (audit_ctx_t ctx
, const char *oktext
, const char *format
, ...)
450 const char *color
= NULL
;
452 if (ctx
->use_html
&& format
&& oktext
)
454 if (!strcmp (oktext
, "Yes"))
456 else if (!strcmp (oktext
, "No"))
460 if (format
&& oktext
)
462 if (!strcmp (oktext
, "Yes"))
464 else if (!strcmp (oktext
, "No"))
472 es_fputs (" <tr><td><table><tr><td>", ctx
->outstream
);
474 es_fprintf (ctx
->outstream
, "<font color=\"%s\">*</font>", color
);
476 es_fputs ("*", ctx
->outstream
);
477 for (i
=1; i
< ctx
->indentlevel
; i
++)
478 es_fputs (" ", ctx
->outstream
);
479 es_fputs ("</td><td>", ctx
->outstream
);
482 es_fprintf (ctx
->outstream
, "* %*s", (ctx
->indentlevel
-1)*2, "");
485 va_start (arg_ptr
, format
) ;
486 writeout_v (ctx
, format
, arg_ptr
);
490 es_fputs ("</td></tr></table>", ctx
->outstream
);
491 if (format
&& oktext
)
495 es_fputs ("</td><td>", ctx
->outstream
);
497 es_fprintf (ctx
->outstream
, "<font color=\"%s\">", color
);
500 writeout (ctx
, ": ");
501 writeout (ctx
, oktext
);
503 es_fputs ("</font>", ctx
->outstream
);
507 es_fputs ("</td></tr>\n", ctx
->outstream
);
509 es_fputc ('\n', ctx
->outstream
);
513 /* Write a remark line. */
515 writeout_rem (audit_ctx_t ctx
, const char *format
, ...)
523 es_fputs (" <tr><td><table><tr><td>*", ctx
->outstream
);
524 for (i
=1; i
< ctx
->indentlevel
; i
++)
525 es_fputs (" ", ctx
->outstream
);
526 es_fputs (" </td><td> (", ctx
->outstream
);
530 es_fprintf (ctx
->outstream
, "* %*s (", (ctx
->indentlevel
-1)*2, "");
533 va_start (arg_ptr
, format
) ;
534 writeout_v (ctx
, format
, arg_ptr
);
538 es_fputs (")</td></tr></table></td></tr>\n", ctx
->outstream
);
540 es_fputs (")\n", ctx
->outstream
);
544 /* Return the first log item for EVENT. If STOPEVENT is not 0 never
545 look behind that event in the log. If STARTITEM is not NULL start
546 search _after_that item. */
548 find_next_log_item (audit_ctx_t ctx
, log_item_t startitem
,
549 audit_event_t event
, audit_event_t stopevent
)
553 for (idx
=0; idx
< ctx
->logused
; idx
++)
557 if (ctx
->log
+ idx
== startitem
)
560 else if (stopevent
&& ctx
->log
[idx
].event
== stopevent
)
562 else if (ctx
->log
[idx
].event
== event
)
563 return ctx
->log
+ idx
;
570 find_log_item (audit_ctx_t ctx
, audit_event_t event
, audit_event_t stopevent
)
572 return find_next_log_item (ctx
, NULL
, event
, stopevent
);
576 /* Helper to a format a serial number. */
578 format_serial (ksba_const_sexp_t sn
)
580 const char *p
= (const char *)sn
;
587 BUG (); /* Not a valid S-expression. */
588 n
= strtoul (p
+1, &endp
, 10);
591 BUG (); /* Not a valid S-expression. */
592 return bin2hex (p
+1, n
, NULL
);
596 /* Return a malloced string with the serial number and the issuer DN
597 of the certificate. */
599 get_cert_name (ksba_cert_t cert
)
606 return xtrystrdup ("[no certificate]");
608 issuer
= ksba_cert_get_issuer (cert
, 0);
609 sn
= ksba_cert_get_serial (cert
);
612 p
= format_serial (sn
);
614 result
= xtrystrdup ("[invalid S/N]");
617 result
= xtrymalloc (strlen (p
) + strlen (issuer
) + 2 + 1);
621 strcpy (stpcpy (stpcpy (result
+1, p
),"/"), issuer
);
627 result
= xtrystrdup ("[missing S/N or issuer]");
633 /* Return a malloced string with the serial number and the issuer DN
634 of the certificate. */
636 get_cert_subject (ksba_cert_t cert
, int idx
)
642 return xtrystrdup ("[no certificate]");
644 subject
= ksba_cert_get_subject (cert
, idx
);
647 result
= xtrymalloc (strlen (subject
) + 1 + 1);
651 strcpy (result
+1, subject
);
661 /* List the given certificiate. If CERT is NULL, this is a NOP. */
663 list_cert (audit_ctx_t ctx
, ksba_cert_t cert
, int with_subj
)
668 name
= get_cert_name (cert
);
669 writeout_rem (ctx
, "%s", name
);
674 for (idx
=0; (name
= get_cert_subject (cert
, idx
)); idx
++)
676 writeout_rem (ctx
, "%s", name
);
684 /* List the chain of certificates from STARTITEM up to STOPEVENT. The
685 certifcates are written out as comments. */
687 list_certchain (audit_ctx_t ctx
, log_item_t startitem
, audit_event_t stopevent
)
691 startitem
= find_next_log_item (ctx
, startitem
, AUDIT_CHAIN_BEGIN
,stopevent
);
692 writeout_li (ctx
, startitem
? "Yes":"No", _("Certificate chain available"));
696 item
= find_next_log_item (ctx
, startitem
,
697 AUDIT_CHAIN_ROOTCERT
, AUDIT_CHAIN_END
);
699 writeout_rem (ctx
, "%s", _("root certificate missing"));
702 list_cert (ctx
, item
->cert
, 0);
705 while ( ((item
= find_next_log_item (ctx
, item
,
706 AUDIT_CHAIN_CERT
, AUDIT_CHAIN_END
))))
708 list_cert (ctx
, item
->cert
, 1);
714 /* Process an encrypt operation's log. */
716 proc_type_encrypt (audit_ctx_t ctx
)
718 log_item_t loopitem
, item
;
724 item
= find_log_item (ctx
, AUDIT_ENCRYPTION_DONE
, 0);
725 writeout_li (ctx
, item
?"Yes":"No", "%s", _("Data encryption succeeded"));
729 item
= find_log_item (ctx
, AUDIT_GOT_DATA
, 0);
730 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Data available"));
732 item
= find_log_item (ctx
, AUDIT_SESSION_KEY
, 0);
733 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Session key created"));
736 algo
= gcry_cipher_map_name (item
->string
);
738 writeout_rem (ctx
, _("algorithm: %s"), gcry_cipher_algo_name (algo
));
739 else if (item
->string
&& !strcmp (item
->string
, "1.2.840.113549.3.2"))
740 writeout_rem (ctx
, _("unsupported algorithm: %s"), "RC2");
741 else if (item
->string
)
742 writeout_rem (ctx
, _("unsupported algorithm: %s"), item
->string
);
744 writeout_rem (ctx
, _("seems to be not encrypted"));
747 item
= find_log_item (ctx
, AUDIT_GOT_RECIPIENTS
, 0);
748 snprintf (numbuf
, sizeof numbuf
, "%d",
749 item
&& item
->have_intvalue
? item
->intvalue
: 0);
750 writeout_li (ctx
, numbuf
, "%s", _("Number of recipients"));
752 /* Loop over all recipients. */
755 while ((loopitem
=find_next_log_item (ctx
, loopitem
, AUDIT_ENCRYPTED_TO
, 0)))
758 writeout_li (ctx
, NULL
, _("Recipient %d"), recp_no
);
761 name
= get_cert_name (loopitem
->cert
);
762 writeout_rem (ctx
, "%s", name
);
765 for (idx
=0; (name
= get_cert_subject (loopitem
->cert
, idx
)); idx
++)
767 writeout_rem (ctx
, "%s", name
);
779 /* Process a sign operation's log. */
781 proc_type_sign (audit_ctx_t ctx
)
786 writeout_li (ctx
, item
?"Yes":"No", "%s", _("Data signing succeeded"));
790 item
= find_log_item (ctx
, AUDIT_GOT_DATA
, 0);
791 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Data available"));
799 /* Process a decrypt operation's log. */
801 proc_type_decrypt (audit_ctx_t ctx
)
806 writeout_li (ctx
, item
?"Yes":"No", "%s", _("Data decryption succeeded"));
810 item
= find_log_item (ctx
, AUDIT_GOT_DATA
, 0);
811 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Data available"));
819 /* Process a verification operation's log. */
821 proc_type_verify (audit_ctx_t ctx
)
823 log_item_t loopitem
, item
;
824 int signo
, count
, idx
;
827 /* If there is at least one signature status we claim that the
828 verifciation succeeded. This does not mean that the data has
830 item
= find_log_item (ctx
, AUDIT_SIG_STATUS
, 0);
831 writeout_li (ctx
, item
?"Yes":"No", "%s", _("Data verification succeeded"));
834 item
= find_log_item (ctx
, AUDIT_GOT_DATA
, AUDIT_NEW_SIG
);
835 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Data available"));
839 item
= find_log_item (ctx
, AUDIT_NEW_SIG
, 0);
840 writeout_li (ctx
, item
? "Yes":"No", "%s", _("Signature available"));
844 item
= find_log_item (ctx
, AUDIT_DATA_HASH_ALGO
, AUDIT_NEW_SIG
);
845 writeout_li (ctx
, item
?"Yes":"No", "%s", _("Parsing signature succeeded"));
848 item
= find_log_item (ctx
, AUDIT_BAD_DATA_HASH_ALGO
, AUDIT_NEW_SIG
);
850 writeout_rem (ctx
, _("Bad hash algorithm: %s"),
851 item
->string
? item
->string
:"?");
856 /* Loop over all signatures. */
857 loopitem
= find_log_item (ctx
, AUDIT_NEW_SIG
, 0);
861 signo
= loopitem
->have_intvalue
? loopitem
->intvalue
: -1;
863 item
= find_next_log_item (ctx
, loopitem
,
864 AUDIT_SIG_STATUS
, AUDIT_NEW_SIG
);
865 writeout_li (ctx
, item
? item
->string
:"?", _("Signature %d"), signo
);
866 item
= find_next_log_item (ctx
, loopitem
,
867 AUDIT_SIG_NAME
, AUDIT_NEW_SIG
);
869 writeout_rem (ctx
, "%s", item
->string
);
872 /* List the certificate chain. */
873 list_certchain (ctx
, loopitem
, AUDIT_NEW_SIG
);
875 /* Show the result of the chain validation. */
876 item
= find_next_log_item (ctx
, loopitem
,
877 AUDIT_CHAIN_STATUS
, AUDIT_NEW_SIG
);
878 if (item
&& item
->have_err
)
880 writeout_li (ctx
, item
->err
? "No":"Yes",
881 _("Certificate chain valid"));
883 writeout_rem (ctx
, "%s", gpg_strerror (item
->err
));
886 /* Show whether the root certificate is fine. */
887 item
= find_next_log_item (ctx
, loopitem
,
888 AUDIT_ROOT_TRUSTED
, AUDIT_CHAIN_STATUS
);
891 writeout_li (ctx
, item
->err
?"No":"Yes", "%s",
892 _("Root certificate trustworthy"));
895 add_helptag (ctx
, "gpgsm.root-cert-not-trusted");
896 writeout_rem (ctx
, "%s", gpg_strerror (item
->err
));
897 list_cert (ctx
, item
->cert
, 0);
901 /* Show result of the CRL/OCSP check. */
902 writeout_li (ctx
, "-", "%s", _("CRL/OCSP check of certificates"));
903 /* add_helptag (ctx, "gpgsm.ocsp-problem"); */
908 while ((loopitem
= find_next_log_item (ctx
, loopitem
, AUDIT_NEW_SIG
, 0)));
912 /* Always list the certificates stored in the signature. */
915 while ( ((item
= find_next_log_item (ctx
, item
,
916 AUDIT_SAVE_CERT
, AUDIT_NEW_SIG
))))
918 snprintf (numbuf
, sizeof numbuf
, "%d", count
);
919 writeout_li (ctx
, numbuf
, _("Included certificates"));
921 while ( ((item
= find_next_log_item (ctx
, item
,
922 AUDIT_SAVE_CERT
, AUDIT_NEW_SIG
))))
924 char *name
= get_cert_name (item
->cert
);
925 writeout_rem (ctx
, "%s", name
);
928 for (idx
=0; (name
= get_cert_subject (item
->cert
, idx
)); idx
++)
930 writeout_rem (ctx
, "%s", name
);
941 /* Print the formatted audit result. THIS IS WORK IN PROGRESS. */
943 audit_print_result (audit_ctx_t ctx
, estream_t out
, int use_html
)
956 orig_codeset
= i18n_switchto_utf8 ();
958 /* We use an environment variable to include some debug info in the
960 if ((s
= getenv ("gnupg_debug_audit")))
963 if (!strcmp (s
, "html"))
967 assert (!ctx
->outstream
);
968 ctx
->outstream
= out
;
969 ctx
->use_html
= use_html
;
970 ctx
->indentlevel
= 0;
971 clear_helptags (ctx
);
974 es_fputs ("<div class=\"GnuPGAuditLog\">\n", ctx
->outstream
);
976 if (!ctx
->log
|| !ctx
->logused
)
978 writeout_para (ctx
, _("No audit log entries."));
986 for (idx
=0,maxlen
=0; idx
< DIM (eventstr_msgidx
); idx
++)
988 n
= strlen (eventstr_msgstr
+ eventstr_msgidx
[idx
]);
994 es_fputs ("<pre>\n", out
);
995 for (idx
=0; idx
< ctx
->logused
; idx
++)
997 es_fprintf (out
, "log: %-*s",
998 maxlen
, event2str (ctx
->log
[idx
].event
));
999 if (ctx
->log
[idx
].have_intvalue
)
1000 es_fprintf (out
, " i=%d", ctx
->log
[idx
].intvalue
);
1001 if (ctx
->log
[idx
].string
)
1003 es_fputs (" s=`", out
);
1004 writeout (ctx
, ctx
->log
[idx
].string
);
1005 es_fputs ("'", out
);
1007 if (ctx
->log
[idx
].cert
)
1008 es_fprintf (out
, " has_cert");
1009 if (ctx
->log
[idx
].have_err
)
1011 es_fputs (" err=`", out
);
1012 writeout (ctx
, gpg_strerror (ctx
->log
[idx
].err
));
1013 es_fputs ("'", out
);
1015 es_fputs ("\n", out
);
1018 es_fputs ("</pre>\n", out
);
1020 es_fputs ("\n", out
);
1026 case AUDIT_TYPE_NONE
:
1027 writeout_li (ctx
, NULL
, _("Unknown operation"));
1029 case AUDIT_TYPE_ENCRYPT
:
1030 proc_type_encrypt (ctx
);
1032 case AUDIT_TYPE_SIGN
:
1033 proc_type_sign (ctx
);
1035 case AUDIT_TYPE_DECRYPT
:
1036 proc_type_decrypt (ctx
);
1038 case AUDIT_TYPE_VERIFY
:
1039 proc_type_verify (ctx
);
1042 item
= find_log_item (ctx
, AUDIT_AGENT_READY
, 0);
1043 if (item
&& item
->have_err
)
1045 writeout_li (ctx
, item
->err
? "No":"Yes", "%s", _("Gpg-Agent usable"));
1048 writeout_rem (ctx
, "%s", gpg_strerror (item
->err
));
1049 add_helptag (ctx
, "gnupg.agent-problem");
1052 item
= find_log_item (ctx
, AUDIT_DIRMNGR_READY
, 0);
1053 if (item
&& item
->have_err
)
1055 writeout_li (ctx
, item
->err
? "No":"Yes", "%s", _("Dirmngr usable"));
1058 writeout_rem (ctx
, "%s", gpg_strerror (item
->err
));
1059 add_helptag (ctx
, "gnupg.dirmngr-problem");
1065 /* Show the help from the collected help tags. */
1070 es_fputs ("<hr/>\n", ctx
->outstream
);
1071 if (ctx
->helptags
->next
)
1072 es_fputs ("<ul>\n", ctx
->outstream
);
1075 es_fputs ("\n\n", ctx
->outstream
);
1077 for (helptag
= ctx
->helptags
; helptag
; helptag
= helptag
->next
)
1081 if (use_html
&& ctx
->helptags
->next
)
1082 es_fputs ("<li>\n", ctx
->outstream
);
1084 text
= gnupg_get_help_string (helptag
->name
, 0);
1087 writeout_para (ctx
, "%s", text
);
1091 writeout_para (ctx
, _("No help available for `%s'."), helptag
->name
);
1092 if (use_html
&& ctx
->helptags
->next
)
1093 es_fputs ("</li>\n", ctx
->outstream
);
1095 es_fputs ("\n", ctx
->outstream
);
1097 if (use_html
&& ctx
->helptags
&& ctx
->helptags
->next
)
1098 es_fputs ("</ul>\n", ctx
->outstream
);
1102 es_fputs ("</div>\n", ctx
->outstream
);
1103 ctx
->outstream
= NULL
;
1105 clear_helptags (ctx
);
1106 i18n_switchback (orig_codeset
);