1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2014 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
23 * Author: paisleyj@dcs.gla.ac.uk
32 #include "../pith/charconv/utf8.h"
33 #include "../pith/status.h"
34 #include "../pith/store.h"
35 #include "../pith/conf.h"
36 #include "../pith/list.h"
37 #include "../pith/mailcmd.h"
42 #include "confscroll.h"
46 /* internal prototypes */
47 void format_smime_info(int pass
, BODY
*body
, long msgno
, gf_io_t pc
);
48 void print_separator_line(int percent
, int ch
, gf_io_t pc
);
49 void output_cert_info(X509
*cert
, gf_io_t pc
);
50 void output_X509_NAME(X509_NAME
*name
, gf_io_t pc
);
51 void side_by_side(STORE_S
*left
, STORE_S
*right
, gf_io_t pc
);
52 STORE_S
*wrap_store(STORE_S
*in
, int width
);
53 void smime_config_init_display(struct pine
*, CONF_S
**, CONF_S
**);
54 void revert_to_saved_smime_config(struct pine
*ps
, SAVED_CONFIG_S
*vsave
);
55 SAVED_CONFIG_S
*save_smime_config_vars(struct pine
*ps
);
56 void free_saved_smime_config(struct pine
*ps
, SAVED_CONFIG_S
**vsavep
);
57 int smime_helper_tool(struct pine
*, int, CONF_S
**, unsigned);
58 int smime_public_certs_tool(struct pine
*, int, CONF_S
**, unsigned);
59 void manage_certificates(struct pine
*, WhichCerts
);
60 void smime_manage_certs_init (struct pine
*, CONF_S
**, CONF_S
**, WhichCerts
, int);
61 void display_certificate_information(struct pine
*, X509
*, char *, WhichCerts
);
62 int manage_certs_tool(struct pine
*ps
, int cmd
, CONF_S
**cl
, unsigned flags
);
63 int manage_certificate_info_tool(int, MSGNO_S
*, SCROLL_S
*);
67 * prompt the user for their passphrase
68 * (possibly prompting with the email address in s_passphrase_emailaddr)
71 smime_get_passphrase(void)
76 HelpType help
= NO_HELP
;
78 assert(ps_global
->smime
!= NULL
);
79 snprintf(prompt
, sizeof(prompt
),
80 _("Enter passphrase for <%s>: "), (ps_global
->smime
&& ps_global
->smime
->passphrase_emailaddr
) ? ps_global
->smime
->passphrase_emailaddr
[0] : "unknown");
83 flags
= OE_PASSWD
| OE_DISALLOW_HELP
;
84 ((char *) ps_global
->smime
->passphrase
)[0] = '\0';
85 rc
= optionally_enter((char *) ps_global
->smime
->passphrase
,
86 -FOOTER_ROWS(ps_global
), 0,
87 sizeof(ps_global
->smime
->passphrase
),
88 prompt
, NULL
, help
, &flags
);
89 } while (rc
!=0 && rc
!=1 && rc
>0);
93 ps_global
->smime
->entered_passphrase
= 1;
96 return rc
; /* better return rc and make the caller check its return value */
101 smime_info_screen(struct pine
*ps
)
108 HANDLE_S
*handles
= NULL
;
110 STORE_S
*store
= NULL
;
112 ps
->prev_screen
= smime_info_screen
;
113 ps
->next_screen
= SCREEN_FUN_NULL
;
115 if(mn_total_cur(ps
->msgmap
) > 1L){
116 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
117 _("Can only view one message's information at a time."));
120 /* else check for existence of smime bits */
122 msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
124 env
= mail_fetch_structure(ps
->mail_stream
, msgno
, &body
, 0);
126 q_status_message(SM_ORDER
, 0, 3,
127 _("Can't fetch body of message."));
133 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
135 while(ps
->next_screen
== SCREEN_FUN_NULL
){
139 so_truncate(store
, 0);
141 view_writec_init(store
, &handles
, HEADER_ROWS(ps
),
143 ps
->ttyo
->screen_rows
- (HEADER_ROWS(ps
)
146 gf_puts_uline("Overview", view_writec
);
147 gf_puts(NEWLINE
, view_writec
);
149 format_smime_info(1, body
, msgno
, view_writec
);
150 gf_puts(NEWLINE
, view_writec
);
151 format_smime_info(2, body
, msgno
, view_writec
);
153 view_writec_destroy();
155 ps
->next_screen
= SCREEN_FUN_NULL
;
157 memset(&scrollargs
, 0, sizeof(SCROLL_S
));
158 scrollargs
.text
.text
= so_text(store
);
159 scrollargs
.text
.src
= CharStar
;
160 scrollargs
.text
.desc
= "S/MIME information";
161 scrollargs
.body_valid
= 1;
163 if(offset
){ /* resize? preserve paging! */
164 scrollargs
.start
.on
= Offset
;
165 scrollargs
.start
.loc
.offset
= offset
;
169 scrollargs
.bar
.title
= "S/MIME INFORMATION";
170 /* scrollargs.end_scroll = view_end_scroll; */
171 scrollargs
.resize_exit
= 1;
172 scrollargs
.help
.text
= NULL
;
173 scrollargs
.help
.title
= "HELP FOR S/MIME INFORMATION VIEW";
174 scrollargs
.keys
.menu
= &smime_info_keymenu
;
175 scrollargs
.keys
.what
= what
;
176 setbitmap(scrollargs
.keys
.bitmap
);
178 if(scrolltool(&scrollargs
) == MC_RESIZE
)
179 offset
= scrollargs
.start
.loc
.offset
;
187 format_smime_info(int pass
, BODY
*body
, long msgno
, gf_io_t pc
)
192 if(body
->type
== TYPEMULTIPART
){
195 for(p
=body
->nested
.part
; p
; p
=p
->next
)
196 format_smime_info(pass
, &p
->body
, msgno
, pc
);
202 if(PKCS7_type_is_signed(p7
)){
203 STACK_OF(X509
) *signers
;
207 gf_puts(_("This message was cryptographically signed."), pc
);
208 gf_puts(NEWLINE
, pc
);
212 signers
= PKCS7_get0_signers(p7
, NULL
, 0);
216 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Certificate%s used for signing"),
217 plural(sk_X509_num(signers
)));
218 gf_puts_uline(tmp_20k_buf
, pc
);
219 gf_puts(NEWLINE
, pc
);
220 print_separator_line(100, '-', pc
);
222 for(i
=0; i
<sk_X509_num(signers
); i
++){
223 X509
*x
= sk_X509_value(signers
, i
);
226 output_cert_info(x
, pc
);
227 gf_puts(NEWLINE
, pc
);
232 sk_X509_free(signers
);
237 else if(PKCS7_type_is_enveloped(p7
)){
241 gf_puts(_("This message was encrypted."), pc
);
242 gf_puts(NEWLINE
, pc
);
246 if(p7
->d
.enveloped
&& p7
->d
.enveloped
->enc_data
){
247 X509_ALGOR
*alg
= p7
->d
.enveloped
->enc_data
->algorithm
;
248 STACK_OF(PKCS7_RECIP_INFO
) *ris
= p7
->d
.enveloped
->recipientinfo
;
251 gf_puts(_("The algorithm used to encrypt was "), pc
);
254 char *n
= (char *) OBJ_nid2sn( OBJ_obj2nid(alg
->algorithm
));
256 gf_puts(n
? n
: "<unknown>", pc
);
260 gf_puts("<unknown>", pc
);
262 gf_puts("." NEWLINE NEWLINE
, pc
);
264 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Certificate%s for decrypting"),
265 plural(sk_PKCS7_RECIP_INFO_num(ris
)));
266 gf_puts_uline(tmp_20k_buf
, pc
);
267 gf_puts(NEWLINE
, pc
);
268 print_separator_line(100, '-', pc
);
270 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(ris
); i
++){
271 PKCS7_RECIP_INFO
*ri
;
272 PERSONAL_CERT
*pcert
;
274 ri
= sk_PKCS7_RECIP_INFO_value(ris
, i
);
278 pcert
= find_certificate_matching_recip_info(ri
);
282 print_separator_line(25, '*', pc
);
283 gf_puts(NEWLINE
, pc
);
288 output_cert_info(pcert
->cert
, pc
);
289 gf_puts(NEWLINE
, pc
);
295 gf_puts(_("No certificate capable of decrypting could be found."), pc
);
296 gf_puts(NEWLINE
, pc
);
297 gf_puts(NEWLINE
, pc
);
309 print_separator_line(int percent
, int ch
, gf_io_t pc
)
313 len
= ps_global
->ttyo
->screen_cols
* percent
/ 100;
314 start
= (ps_global
->ttyo
->screen_cols
- len
)/2;
316 for(i
=0; i
<start
; i
++)
319 for(i
=start
; i
<start
+len
; i
++)
322 gf_puts(NEWLINE
, pc
);
327 output_cert_info(X509
*cert
, gf_io_t pc
)
330 STORE_S
*left
,*right
;
334 left
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
335 right
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
339 gf_set_so_writec(&spc
, left
);
341 if(!cert
->cert_info
){
342 gf_puts("Couldn't find certificate info.", spc
);
343 gf_puts(NEWLINE
, spc
);
346 gf_puts_uline("Subject (whose certificate it is)", spc
);
347 gf_puts(NEWLINE
, spc
);
349 output_X509_NAME(cert
->cert_info
->subject
, spc
);
350 gf_puts(NEWLINE
, spc
);
352 gf_puts_uline("Serial Number", spc
);
353 gf_puts(NEWLINE
, spc
);
361 bs
= X509_get_serialNumber(cert
);
362 if (bs
->length
<= (int)sizeof(long)){
363 l
= ASN1_INTEGER_get(bs
);
364 if (bs
->type
== V_ASN1_NEG_INTEGER
){
370 snprintf(buf
, sizeof(buf
), " %s%lu (%s0x%lx)", neg
, l
, neg
, l
);
372 snprintf(buf
, sizeof(buf
), "%s", bs
->type
== V_ASN1_NEG_INTEGER
? "(Negative)" : "");
373 for (i
= 0; i
< bs
->length
; i
++)
374 snprintf(buf
+ strlen(buf
), sizeof(buf
) - strlen(buf
), "%02x%s", bs
->data
[i
],
375 i
+1 == bs
->length
? "" : ":");
379 gf_puts(NEWLINE
, spc
);
380 gf_puts(NEWLINE
, spc
);
382 gf_puts_uline("Validity", spc
);
383 gf_puts(NEWLINE
, spc
);
385 BIO
*mb
= BIO_new(BIO_s_mem());
388 gf_puts("Not Before: ", spc
);
390 (void) BIO_reset(mb
);
391 ASN1_UTCTIME_print(mb
, cert
->cert_info
->validity
->notBefore
);
392 (void) BIO_flush(mb
);
393 while((len
= BIO_read(mb
, iobuf
, sizeof(iobuf
))) > 0)
394 gf_nputs(iobuf
, len
, spc
);
396 gf_puts(NEWLINE
, spc
);
398 gf_puts("Not After: ", spc
);
400 (void) BIO_reset(mb
);
401 ASN1_UTCTIME_print(mb
, cert
->cert_info
->validity
->notAfter
);
402 (void) BIO_flush(mb
);
403 while((len
= BIO_read(mb
, iobuf
, sizeof(iobuf
))) > 0)
404 gf_nputs(iobuf
, len
, spc
);
406 gf_puts(NEWLINE
, spc
);
407 gf_puts(NEWLINE
, spc
);
413 gf_clear_so_writec(left
);
415 gf_set_so_writec(&spc
, right
);
417 if(!cert
->cert_info
){
418 gf_puts(_("Couldn't find certificate info."), spc
);
419 gf_puts(NEWLINE
, spc
);
422 gf_puts_uline("Issuer", spc
);
423 gf_puts(NEWLINE
, spc
);
425 output_X509_NAME(cert
->cert_info
->issuer
, spc
);
426 gf_puts(NEWLINE
, spc
);
429 gf_clear_so_writec(right
);
431 side_by_side(left
, right
, pc
);
433 gf_puts_uline("SHA1 Fingerprint", pc
);
434 gf_puts(NEWLINE
, pc
);
435 get_fingerprint(cert
, EVP_sha1(), buf
, sizeof(buf
));
437 gf_puts(NEWLINE
, pc
);
439 gf_puts_uline("MD5 Fingerprint", pc
);
440 gf_puts(NEWLINE
, pc
);
441 get_fingerprint(cert
, EVP_md5(), buf
, sizeof(buf
));
443 gf_puts(NEWLINE
, pc
);
451 output_X509_NAME(X509_NAME
*name
, gf_io_t pc
)
456 c
= X509_NAME_entry_count(name
);
458 for(i
=c
-1; i
>=0; i
--){
461 e
= X509_NAME_get_entry(name
,i
);
465 X509_NAME_get_text_by_OBJ(name
, e
->object
, buf
, sizeof(buf
));
468 gf_puts(NEWLINE
, pc
);
474 * Output the contents of the given stores (left and right)
475 * to the given gf_io_t.
476 * The width of the terminal is inspected and two columns
477 * are created to fit the stores into. They are then wrapped
481 side_by_side(STORE_S
*left
, STORE_S
*right
, gf_io_t pc
)
483 STORE_S
*left_wrapped
;
484 STORE_S
*right_wrapped
;
490 int w
= ps_global
->ttyo
->screen_cols
/2 - 1;
493 so_seek(right
, 0, 0);
495 left_wrapped
= wrap_store(left
, w
);
496 right_wrapped
= wrap_store(right
, w
);
498 so_seek(left_wrapped
, 0, 0);
499 so_seek(right_wrapped
, 0, 0);
503 l
= so_fgets(left_wrapped
, buf_l
, sizeof(buf_l
));
504 r
= so_fgets(right_wrapped
, buf_r
, sizeof(buf_r
));
505 if(l
== NULL
&& r
== NULL
)
508 for(i
=0, b
=buf_l
; i
<w
&& *b
&& *b
!='\r' && *b
!='\n'; i
++,b
++){
510 /* reduce accumulated width if an embed tag is discovered */
522 for(i
=0, b
=buf_r
; i
<w
&& *b
&& *b
!='\r' && *b
!='\n'; i
++,b
++)
526 gf_puts(NEWLINE
, pc
);
529 so_give(&left_wrapped
);
530 so_give(&right_wrapped
);
534 * Wrap the text in the given store to the given width.
535 * A new store is created for the result.
538 wrap_store(STORE_S
*in
, int width
)
547 result
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
548 ws
= gf_wrap_filter_opt(width
, width
, NULL
, 0, 0);
551 gf_link_filter(gf_wrap
, ws
);
553 gf_set_so_writec(&opc
, result
);
554 gf_set_so_readc(&ipc
, in
);
558 gf_clear_so_readc(in
);
559 gf_clear_so_writec(result
);
566 smime_config_screen(struct pine
*ps
, int edit_exceptions
)
568 CONF_S
*ctmp
= NULL
, *first_line
= NULL
;
569 SAVED_CONFIG_S
*vsave
;
571 int ew
, readonly_warning
= 0;
573 dprint((9, "smime_config_screen()"));
574 ps
->next_screen
= SCREEN_FUN_NULL
;
577 * this is necessary because we need to know the correct paths
578 * to configure certificates and keys, and we could get here
579 * without having done that before we reach this place.
583 if(ps
->fix_fixed_warning
)
584 offer_to_fix_pinerc(ps
);
586 ew
= edit_exceptions
? ps_global
->ew_for_except_vars
: Main
;
589 readonly_warning
= 1;
591 PINERC_S
*prc
= NULL
;
604 readonly_warning
= prc
? prc
->readonly
: 1;
605 if(prc
&& prc
->quit_to_edit
){
606 quit_to_edit_msg(prc
);
611 smime_config_init_display(ps
, &ctmp
, &first_line
);
613 vsave
= save_smime_config_vars(ps
);
615 memset(&screen
, 0, sizeof(screen
));
616 screen
.deferred_ro_warning
= readonly_warning
;
617 switch(conf_scroll_screen(ps
, &screen
, first_line
,
618 edit_exceptions
? _("SETUP S/MIME EXCEPTIONS")
620 /* TRANSLATORS: Print something1 using something2.
621 configuration is something1 */
622 _("configuration"), 0)){
627 write_pinerc(ps
, ew
, WRP_NONE
);
631 revert_to_saved_smime_config(ps
, vsave
);
635 q_status_message(SM_ORDER
, 7, 10,
636 _("conf_scroll_screen bad ret in smime_config"));
640 free_saved_smime_config(ps
, &vsave
);
646 smime_related_var(struct pine
*ps
, struct variable
*var
)
648 return(var
== &ps
->vars
[V_PUBLICCERT_DIR
] ||
649 var
== &ps
->vars
[V_PUBLICCERT_CONTAINER
] ||
650 var
== &ps
->vars
[V_PRIVATEKEY_DIR
] ||
651 var
== &ps
->vars
[V_PRIVATEKEY_CONTAINER
] ||
652 var
== &ps
->vars
[V_CACERT_DIR
] ||
653 var
== &ps
->vars
[V_CACERT_CONTAINER
]);
657 smime_config_init_display(struct pine
*ps
, CONF_S
**ctmp
, CONF_S
**first_line
)
661 struct variable
*vtmp
;
665 /* find longest variable name */
666 for(vtmp
= ps
->vars
; vtmp
->name
; vtmp
++){
667 if(!(smime_related_var(ps
, vtmp
)))
670 if((i
= utf8_width(pretty_var_name(vtmp
->name
))) > ln
)
674 for(vtmp
= ps
->vars
; vtmp
->name
; vtmp
++){
675 if(!(smime_related_var(ps
, vtmp
)))
678 new_confline(ctmp
)->var
= vtmp
;
679 if(first_line
&& !*first_line
)
682 (*ctmp
)->valoffset
= ln
+3;
683 (*ctmp
)->keymenu
= &config_text_keymenu
;
684 (*ctmp
)->help
= config_help(vtmp
- ps
->vars
, 0);
685 (*ctmp
)->tool
= text_tool
;
687 utf8_snprintf(tmp
, sizeof(tmp
), "%-*.100w =", ln
, pretty_var_name(vtmp
->name
));
688 tmp
[sizeof(tmp
)-1] = '\0';
690 (*ctmp
)->varname
= cpystr(tmp
);
691 (*ctmp
)->varnamep
= (*ctmp
);
692 (*ctmp
)->flags
= CF_STARTITEM
;
693 (*ctmp
)->value
= pretty_value(ps
, *ctmp
);
697 vtmp
= &ps
->vars
[V_FEATURE_LIST
];
701 (*ctmp
)->flags
|= CF_NOSELECT
| CF_STARTITEM
;
702 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
703 (*ctmp
)->tool
= NULL
;
705 /* put a nice delimiter before list */
706 new_confline(ctmp
)->var
= NULL
;
707 (*ctmp
)->varnamep
= ctmpb
;
708 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
709 (*ctmp
)->help
= NO_HELP
;
710 (*ctmp
)->tool
= checkbox_tool
;
711 (*ctmp
)->valoffset
= feature_indent();
712 (*ctmp
)->flags
|= CF_NOSELECT
;
713 (*ctmp
)->value
= cpystr("Set Feature Name");
715 new_confline(ctmp
)->var
= NULL
;
716 (*ctmp
)->varnamep
= ctmpb
;
717 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
718 (*ctmp
)->help
= NO_HELP
;
719 (*ctmp
)->tool
= checkbox_tool
;
720 (*ctmp
)->valoffset
= feature_indent();
721 (*ctmp
)->flags
|= CF_NOSELECT
;
722 (*ctmp
)->value
= cpystr("--- ----------------------");
724 ind
= feature_list_index(F_DONT_DO_SMIME
);
725 feature
= feature_list(ind
);
726 new_confline(ctmp
)->var
= vtmp
;
727 (*ctmp
)->varnamep
= ctmpb
;
728 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
729 (*ctmp
)->help
= config_help(vtmp
-ps
->vars
, feature
->id
);
730 (*ctmp
)->tool
= checkbox_tool
;
731 (*ctmp
)->valoffset
= feature_indent();
732 (*ctmp
)->varmem
= ind
;
733 (*ctmp
)->value
= pretty_value(ps
, (*ctmp
));
735 ind
= feature_list_index(F_ENCRYPT_DEFAULT_ON
);
736 feature
= feature_list(ind
);
737 new_confline(ctmp
)->var
= vtmp
;
738 (*ctmp
)->varnamep
= ctmpb
;
739 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
740 (*ctmp
)->help
= config_help(vtmp
-ps
->vars
, feature
->id
);
741 (*ctmp
)->tool
= checkbox_tool
;
742 (*ctmp
)->valoffset
= feature_indent();
743 (*ctmp
)->varmem
= ind
;
744 (*ctmp
)->value
= pretty_value(ps
, (*ctmp
));
746 ind
= feature_list_index(F_REMEMBER_SMIME_PASSPHRASE
);
747 feature
= feature_list(ind
);
748 new_confline(ctmp
)->var
= vtmp
;
749 (*ctmp
)->varnamep
= ctmpb
;
750 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
751 (*ctmp
)->help
= config_help(vtmp
-ps
->vars
, feature
->id
);
752 (*ctmp
)->tool
= checkbox_tool
;
753 (*ctmp
)->valoffset
= feature_indent();
754 (*ctmp
)->varmem
= ind
;
755 (*ctmp
)->value
= pretty_value(ps
, (*ctmp
));
757 ind
= feature_list_index(F_SIGN_DEFAULT_ON
);
758 feature
= feature_list(ind
);
759 new_confline(ctmp
)->var
= vtmp
;
760 (*ctmp
)->varnamep
= ctmpb
;
761 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
762 (*ctmp
)->help
= config_help(vtmp
-ps
->vars
, feature
->id
);
763 (*ctmp
)->tool
= checkbox_tool
;
764 (*ctmp
)->valoffset
= feature_indent();
765 (*ctmp
)->varmem
= ind
;
766 (*ctmp
)->value
= pretty_value(ps
, (*ctmp
));
770 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
773 (*ctmp
)->flags
|= CF_NOSELECT
;
774 (*ctmp
)->value
= cpystr(_("Mac OS X specific features"));
776 ind
= feature_list_index(F_PUBLICCERTS_IN_KEYCHAIN
);
777 feature
= feature_list(ind
);
778 new_confline(ctmp
)->var
= vtmp
;
779 (*ctmp
)->varnamep
= ctmpb
;
780 (*ctmp
)->keymenu
= &config_checkbox_keymenu
;
781 (*ctmp
)->help
= config_help(vtmp
-ps
->vars
, feature
->id
);
782 (*ctmp
)->tool
= checkbox_tool
;
783 (*ctmp
)->valoffset
= feature_indent();
784 (*ctmp
)->varmem
= ind
;
785 (*ctmp
)->value
= pretty_value(ps
, (*ctmp
));
786 #endif /* APPLEKEYCHAIN */
789 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
791 for(i
= 0; i
< sizeof(tmp
) && i
< (ps
->ttyo
? ps
->ttyo
->screen_cols
: sizeof(tmp
)); i
++)
794 (*ctmp
)->flags
|= CF_NOSELECT
;
795 (*ctmp
)->value
= cpystr(tmp
);
798 (*ctmp
)->flags
|= CF_NOSELECT
;
799 (*ctmp
)->value
= cpystr(_("Be careful with the following commands, they REPLACE contents in the target"));
802 (*ctmp
)->flags
|= CF_NOSELECT
;
803 (*ctmp
)->value
= cpystr(tmp
);
806 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
808 /* copy public directory to container */
810 (*ctmp
)->tool
= smime_helper_tool
;
811 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
812 (*ctmp
)->help
= h_config_smime_transfer_pub_to_con
;
813 (*ctmp
)->value
= cpystr(_("Transfer public certs FROM directory TO container"));
816 /* copy private directory to container */
818 (*ctmp
)->tool
= smime_helper_tool
;
819 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
820 (*ctmp
)->help
= h_config_smime_transfer_priv_to_con
;
821 (*ctmp
)->value
= cpystr(_("Transfer private keys FROM directory TO container"));
824 /* copy cacert directory to container */
826 (*ctmp
)->tool
= smime_helper_tool
;
827 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
828 (*ctmp
)->help
= h_config_smime_transfer_cacert_to_con
;
829 (*ctmp
)->value
= cpystr(_("Transfer CA certs FROM directory TO container"));
832 new_confline(ctmp
)->var
= vtmp
;
833 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
835 /* copy public container to directory */
837 (*ctmp
)->tool
= smime_helper_tool
;
838 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
839 (*ctmp
)->help
= h_config_smime_transfer_pub_to_dir
;
840 (*ctmp
)->value
= cpystr(_("Transfer public certs FROM container TO directory"));
843 /* copy private container to directory */
845 (*ctmp
)->tool
= smime_helper_tool
;
846 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
847 (*ctmp
)->help
= h_config_smime_transfer_priv_to_dir
;
848 (*ctmp
)->value
= cpystr(_("Transfer private keys FROM container TO directory"));
851 /* copy cacert container to directory */
853 (*ctmp
)->tool
= smime_helper_tool
;
854 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
855 (*ctmp
)->help
= h_config_smime_transfer_cacert_to_dir
;
856 (*ctmp
)->value
= cpystr(_("Transfer CA certs FROM container TO directory"));
861 new_confline(ctmp
)->var
= vtmp
;
862 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
864 /* copy public container to keychain */
866 (*ctmp
)->tool
= smime_helper_tool
;
867 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
868 (*ctmp
)->help
= h_config_smime_transfer_pubcon_to_key
;
869 (*ctmp
)->value
= cpystr(_("Transfer public certs FROM container TO keychain"));
872 /* copy public keychain to container */
874 (*ctmp
)->tool
= smime_helper_tool
;
875 (*ctmp
)->keymenu
= &config_smime_helper_keymenu
;
876 (*ctmp
)->help
= h_config_smime_transfer_pubkey_to_con
;
877 (*ctmp
)->value
= cpystr(_("Transfer public certs FROM keychain TO container"));
880 #endif /* APPLEKEYCHAIN */
882 new_confline(ctmp
)->var
= vtmp
;
883 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
886 (*ctmp
)->flags
|= CF_NOSELECT
;
887 (*ctmp
)->value
= cpystr(tmp
);
890 (*ctmp
)->flags
|= CF_NOSELECT
;
891 (*ctmp
)->value
= cpystr(_("Manage your own certificates"));
894 (*ctmp
)->flags
|= CF_NOSELECT
;
895 (*ctmp
)->value
= cpystr(tmp
);
897 new_confline(ctmp
)->var
= vtmp
;
898 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
900 /* manage public certificates */
902 (*ctmp
)->tool
= smime_helper_tool
;
903 (*ctmp
)->keymenu
= &config_smime_manage_certs_menu_keymenu
;
904 (*ctmp
)->help
= h_config_smime_public_certificates
;
905 (*ctmp
)->value
= cpystr(_("Manage Public Certificates"));
908 /* manage private keys */
910 (*ctmp
)->tool
= smime_helper_tool
;
911 (*ctmp
)->keymenu
= &config_smime_manage_certs_menu_keymenu
;
912 (*ctmp
)->help
= h_config_smime_private_keys
;
913 (*ctmp
)->value
= cpystr(_("Manage Private Keys"));
914 (*ctmp
)->varmem
= 10;
916 /* manage Certificate Authorities */
918 (*ctmp
)->tool
= smime_helper_tool
;
919 (*ctmp
)->keymenu
= &config_smime_manage_certs_menu_keymenu
;
920 (*ctmp
)->help
= h_config_smime_certificate_authorities
;
921 (*ctmp
)->value
= cpystr(_("Manage Certificate Authorities"));
922 (*ctmp
)->varmem
= 11;
925 void display_certificate_information(struct pine
*ps
, X509
*cert
, char *email
, WhichCerts ctype
)
930 int pub_cert
, priv_cert
, new_store
;
934 cmd
= offset
= pub_cert
= priv_cert
= 0;
936 ps
->next_screen
= SCREEN_FUN_NULL
;
938 /* MC_PRIVATE and MC_PUBLIC cancel each other,
939 * they can not be active at the same time
944 priv_cert
= ++priv_cert
% 2;
945 smime_certificate_info_keymenu
.keys
[PUBLIC_KEY
].label
= N_("Public Key");
946 smime_certificate_info_keymenu
.keys
[PRIVATE_KEY
].label
= priv_cert
? N_("No Priv Key") : N_("Pivate Key");
951 pub_cert
= ++pub_cert
% 2;
952 smime_certificate_info_keymenu
.keys
[PRIVATE_KEY
].label
= priv_cert
? N_("No Priv Key") : N_("Pivate Key");
953 smime_certificate_info_keymenu
.keys
[PUBLIC_KEY
].label
= N_("Public Key");
957 save_cert_for(email
, cert
, CACert
);
963 if (get_cert_deleted(ctype
, email
) != 0)
964 q_status_message(SM_ORDER
, 1, 3, _("Certificate already deleted"));
966 mark_cert_deleted(ctype
, email
, 1);
967 q_status_message(SM_ORDER
, 1, 3, _("Certificate marked deleted"));
972 if (get_cert_deleted(ctype
, email
) != 0)
973 q_status_message(SM_ORDER
, 1, 3, _("Certificate not marked deleted"));
975 mark_cert_deleted(ctype
, email
, 0);
976 q_status_message(SM_ORDER
, 1, 3, _("Certificate will not be deleted"));
983 if((pub_cert
|| priv_cert
)
984 && (out
= print_private_key_information(email
, priv_cert
)) == NULL
)
985 q_status_message(SM_ORDER
, 1, 3, _("Problem Reading Private Certificate Information"));
988 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
989 view_writec_init(store
, NULL
, HEADER_ROWS(ps
),
990 HEADER_ROWS(ps
) + ps
->ttyo
->screen_rows
- (HEADER_ROWS(ps
)+ FOOTER_ROWS(ps
)));
992 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,"%s", _("Certificate Information"));
993 gf_puts_uline(tmp_20k_buf
, view_writec
);
994 gf_puts(NEWLINE
, view_writec
);
995 print_separator_line(100, '-', view_writec
);
997 output_cert_info(cert
, view_writec
);
998 gf_puts(NEWLINE
, view_writec
);
1000 if(smime_validate_cert(cert
, &error
) < 0){
1001 const char *errorp
= X509_verify_cert_error_string(error
);
1002 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,_("Error validating certificate: %s"), errorp
);
1004 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s", _("Certificate validated without errors"));
1006 gf_puts_uline(tmp_20k_buf
, view_writec
);
1007 gf_puts(NEWLINE
, view_writec
);
1009 if(out
!= NULL
){ /* print private key information */
1010 unsigned char ch
[2];
1012 gf_puts(NEWLINE
, view_writec
);
1014 while(BIO_read(out
, ch
, 1) >= 1)
1015 gf_puts(ch
, view_writec
);
1016 gf_puts(NEWLINE
, view_writec
);
1017 q_status_message1(SM_ORDER
, 1, 3, _("%s information shown at bottom of certificate information"), pub_cert
? _("Public") : _("Private"));
1021 view_writec_destroy();
1025 memset(&scrollargs
, 0, sizeof(SCROLL_S
));
1027 scrollargs
.text
.text
= so_text(store
);
1028 scrollargs
.text
.src
= CharStar
;
1029 scrollargs
.text
.desc
= "certificate information";
1030 scrollargs
.body_valid
= 1;
1032 if(offset
){ /* resize? preserve paging! */
1033 scrollargs
.start
.on
= Offset
;
1034 scrollargs
.start
.loc
.offset
= offset
;
1035 scrollargs
.body_valid
= 0;
1039 scrollargs
.use_indexline_color
= 1;
1041 scrollargs
.bar
.title
= _("CERTIFICATE INFORMATION");
1042 scrollargs
.proc
.tool
= manage_certificate_info_tool
;
1043 scrollargs
.resize_exit
= 1;
1044 scrollargs
.help
.text
= h_certificate_information
;
1045 scrollargs
.help
.title
= _("HELP FOR MESSAGE TEXT VIEW");
1046 scrollargs
.keys
.what
= FirstMenu
;
1047 scrollargs
.keys
.menu
= &smime_certificate_info_keymenu
;
1048 setbitmap(scrollargs
.keys
.bitmap
);
1049 if(ctype
!= Public
|| error
!= X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
)
1050 clrbitn(TRUST_KEY
, scrollargs
.keys
.bitmap
);
1051 if(ctype
!= Private
){
1052 clrbitn(PUBLIC_KEY
, scrollargs
.keys
.bitmap
);
1053 clrbitn(PRIVATE_KEY
, scrollargs
.keys
.bitmap
);
1056 cmd
= scrolltool(&scrollargs
);
1061 case MC_PUBLIC
: if(scrollargs
.start
.on
== Offset
)
1062 offset
= scrollargs
.start
.loc
.offset
;
1068 } while (cmd
!= MC_EXIT
);
1069 ps
->mangled_screen
= 1;
1073 * This is silly, we just need this function so that we can tell scrolltool
1074 * that some commands are recognized. We use scrolltool because we do not
1075 * want to rewrite output_cert_info.
1078 manage_certificate_info_tool(int cmd
, MSGNO_S
*msgmap
, SCROLL_S
*sparms
)
1086 case MC_TRUST
: rv
= 1; break;
1087 default: rv
= 0; break;
1094 manage_certs_tool(struct pine
*ps
, int cmd
, CONF_S
**cl
, unsigned flags
)
1098 WhichCerts ctype
= (*cl
)->d
.s
.ctype
;
1102 if(PATHCERTDIR(ctype
) == NULL
)
1105 if((cert
= get_cert_for((*cl
)->value
+3, ctype
)) == NULL
){
1106 q_status_message(SM_ORDER
, 1, 3, _("Problem Reading Certificate"));
1110 display_certificate_information(ps
, cert
, (*cl
)->value
+3, ctype
);
1111 rv
= 10 + (*cl
)->varmem
;
1116 if ((*cl
)->d
.s
.deleted
!= 0)
1117 q_status_message(SM_ORDER
, 1, 3, _("Certificate already deleted"));
1119 (*cl
)->d
.s
.deleted
= 1;
1120 rv
= 10 + (*cl
)->varmem
; /* forces redraw */
1121 mark_cert_deleted(ctype
, (*cl
)->value
+3, 1);
1122 q_status_message(SM_ORDER
, 1, 3, _("Certificate marked deleted"));
1127 if ((*cl
)->d
.s
.deleted
== 0)
1128 q_status_message(SM_ORDER
, 1, 3, _("Certificate not marked deleted"));
1130 (*cl
)->d
.s
.deleted
= 0;
1131 mark_cert_deleted(ctype
, (*cl
)->value
+3, 0);
1132 rv
= 10 + (*cl
)->varmem
; /* forces redraw */
1133 q_status_message(SM_ORDER
, 1, 3, _("Certificate will not be deleted"));
1140 for(cl
= DATACERT(ctype
); cl
!= NULL
&& DELETEDCERT(cl
) == 0; cl
= cl
->next
);
1141 if(cl
!= NULL
&& DELETEDCERT(cl
) != 0){
1142 smime_expunge_cert(ctype
);
1143 rv
= 10; /* forces redraw */
1146 q_status_message(SM_ORDER
, 3, 3, _("No certificates marked deleted"));
1152 rv
= import_certificate(ctype
);
1157 cmd_cancelled("Import certificate");
1161 q_status_message1(SM_ORDER
, 0, 2, _("Can't import certificate outside of %s"),
1162 ps_global
->VAR_OPER_DIR
);
1166 rv
= 10; /* forces redraw */
1170 rv
= config_exit_cmd(flags
);
1182 void smime_manage_certs_init(struct pine
*ps
, CONF_S
**ctmp
, CONF_S
**first_line
, WhichCerts ctype
, int fline
)
1191 data
= DATACERT(ctype
);
1192 ext
= EXTCERT(ctype
);
1194 if(data
== NULL
|| RENEWCERT(data
))
1195 renew_cert_data(&data
, ctype
);
1197 for(i
= 0; i
< sizeof(tmp
) && i
< (ps
->ttyo
? ps
->ttyo
->screen_cols
: sizeof(tmp
)); i
++)
1200 (*ctmp
)->flags
|= CF_NOSELECT
;
1201 (*ctmp
)->value
= cpystr(tmp
);
1203 (*ctmp
)->keymenu
= &config_text_keymenu
;
1206 (*ctmp
)->flags
|= CF_NOSELECT
;
1207 sprintf(tmp
, _("List of %scertificates"), ctype
== Public
? _("public ")
1208 : (ctype
== Private
? _("private ")
1209 : (ctype
== CACert
? _("certificate authority ") : "unknown (?) ")));
1210 (*ctmp
)->value
= cpystr(tmp
);
1212 for(i
= 0; i
< sizeof(tmp
) && i
< (ps
->ttyo
? ps
->ttyo
->screen_cols
: sizeof(tmp
)); i
++)
1215 (*ctmp
)->flags
|= CF_NOSELECT
;
1216 (*ctmp
)->value
= cpystr(tmp
);
1219 (*ctmp
)->flags
|= CF_NOSELECT
| CF_B_LINE
;
1222 CertList
*cl
; int i
;
1225 for(cl
= data
, i
= 0; cl
; cl
= cl
->next
)
1230 (*ctmp
)->d
.s
.ctype
= ctype
;
1231 (*ctmp
)->d
.s
.deleted
= get_cert_deleted(ctype
, cl
->name
);
1232 (*ctmp
)->tool
= manage_certs_tool
;
1233 (*ctmp
)->keymenu
= &config_smime_manage_certs_work_keymenu
;
1234 (*ctmp
)->varmem
= i
++;
1235 (*ctmp
)->help
= ctype
== Public
? h_config_smime_manage_public_menu
1236 : (ctype
== Private
? h_config_smime_manage_private_menu
1237 : h_config_smime_manage_cacerts_menu
);
1238 snprintf(tmp
, sizeof(tmp
), " %s\t%s", (*ctmp
)->d
.s
.deleted
? "D" : " ", cl
->name
);
1239 (*ctmp
)->value
= cpystr(tmp
);
1240 for(s
= (*ctmp
)->value
; s
&& (t
= strstr(s
, ext
)) != NULL
; s
= t
+1);
1241 if(s
) *(s
-1) = '\0';
1242 if(i
== fline
+1 && first_line
&& !*first_line
)
1243 *first_line
= *ctmp
;
1248 (*ctmp
)->d
.s
.ctype
= ctype
;
1249 (*ctmp
)->tool
= manage_certs_tool
;
1250 (*ctmp
)->keymenu
= &config_smime_add_certs_keymenu
;
1251 (*ctmp
)->value
= cpystr(_(" \tNo certificates found, press \"RETURN\" to add one."));
1252 if(first_line
&& !*first_line
)
1253 *first_line
= *ctmp
;
1257 void manage_certificates(struct pine
*ps
, WhichCerts ctype
)
1259 OPT_SCREEN_S screen
;
1260 int ew
, readonly_warning
= 0, rv
= 10, fline
;
1262 dprint((9, "manage_certificates(ps, %s)", ctype
== Public
? _("Public") : (ctype
== Private
? _("Private") : (ctype
== CACert
? _("certificate authority") : _("unknown")))));
1263 ps
->next_screen
= SCREEN_FUN_NULL
;
1266 CONF_S
*ctmp
= NULL
, *first_line
= NULL
;
1268 fline
= rv
>= 10 ? rv
- 10 : 0;
1272 smime_manage_certs_init(ps
, &ctmp
, &first_line
, ctype
, fline
);
1275 ps
->mangled_screen
= 1;
1280 memset(&screen
, 0, sizeof(screen
));
1281 screen
.deferred_ro_warning
= readonly_warning
;
1282 rv
= conf_scroll_screen(ps
, &screen
, first_line
,
1283 _("MANAGE CERTIFICATES"),
1284 /* TRANSLATORS: Print something1 using something2.
1285 configuration is something1 */
1286 _("configuration"), 0);
1289 ps
->mangled_screen
= 1;
1294 smime_helper_tool(struct pine
*ps
, int cmd
, CONF_S
**cl
, unsigned flags
)
1300 switch((*cl
)->varmem
){
1302 rv
= copy_publiccert_dir_to_container();
1304 q_status_message(SM_ORDER
, 1, 3, _("Public certs transferred to container"));
1306 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1313 rv
= copy_publiccert_container_to_dir();
1315 q_status_message(SM_ORDER
, 1, 3, _("Public certs transferred to directory, delete Container config to use"));
1317 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1324 rv
= copy_privatecert_dir_to_container();
1326 q_status_message(SM_ORDER
, 1, 3, _("Private keys transferred to container"));
1328 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1335 rv
= copy_privatecert_container_to_dir();
1337 q_status_message(SM_ORDER
, 1, 3, _("Private keys transferred to directory, delete Container config to use"));
1339 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1346 rv
= copy_cacert_dir_to_container();
1348 q_status_message(SM_ORDER
, 1, 3, _("CA certs transferred to container"));
1350 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1357 rv
= copy_cacert_container_to_dir();
1359 q_status_message(SM_ORDER
, 1, 3, _("CA certs transferred to directory, delete Container config to use"));
1361 q_status_message(SM_ORDER
, 3, 3, _("Problem transferring certs"));
1367 #ifdef APPLEKEYCHAIN
1369 rv
= copy_publiccert_container_to_keychain();
1371 q_status_message(SM_ORDER
, 1, 3, _("Public certs transferred to keychain"));
1373 q_status_message(SM_ORDER
, 3, 3, _("Command not implemented yet"));
1380 rv
= copy_publiccert_keychain_to_container();
1382 q_status_message(SM_ORDER
, 1, 3, _("Public certs transferred to container"));
1384 q_status_message(SM_ORDER
, 3, 3, _("Command not implemented yet"));
1389 #endif /* APPLEKEYCHAIN */
1391 case 9: manage_certificates(ps
, Public
) ; break;
1392 case 10: manage_certificates(ps
, Private
); break;
1393 case 11: manage_certificates(ps
, CACert
) ; break;
1403 rv
= config_exit_cmd(flags
);
1408 /* keep this selection consistent with the codes above */
1409 ctype
= (*cl
)->varmem
== 9 ? Public
1410 : ((*cl
)->varmem
== 10 ? Private
: CACert
);
1411 rv
= import_certificate(ctype
);
1425 * Compare saved user_val with current user_val to see if it changed.
1426 * If any have changed, change it back and take the appropriate action.
1429 revert_to_saved_smime_config(struct pine
*ps
, SAVED_CONFIG_S
*vsave
)
1431 struct variable
*vreal
;
1435 char *pval
, **apval
, **lval
, ***alval
;
1438 for(vreal
= ps
->vars
; vreal
->name
; vreal
++,v
++){
1439 if(!(smime_related_var(ps
, vreal
) || vreal
==&ps
->vars
[V_FEATURE_LIST
]))
1443 lval
= LVAL(vreal
, ew
);
1444 alval
= ALVAL(vreal
, ew
);
1446 if((v
->saved_user_val
.l
&& !lval
)
1447 || (!v
->saved_user_val
.l
&& lval
))
1449 else if(!v
->saved_user_val
.l
&& !lval
)
1450 ;/* no change, nothing to do */
1452 for(i
= 0; v
->saved_user_val
.l
[i
] || lval
[i
]; i
++)
1453 if((v
->saved_user_val
.l
[i
]
1455 || strcmp(v
->saved_user_val
.l
[i
], lval
[i
])))
1457 (!v
->saved_user_val
.l
[i
] && lval
[i
])){
1467 free_list_array(alval
);
1469 /* copy back the original one */
1470 if(v
->saved_user_val
.l
){
1471 list
= v
->saved_user_val
.l
;
1473 /* count how many */
1477 *alval
= (char **)fs_get((n
+1) * sizeof(char *));
1479 for(i
= 0; i
< n
; i
++)
1480 (*alval
)[i
] = cpystr(v
->saved_user_val
.l
[i
]);
1488 pval
= PVAL(vreal
, ew
);
1489 apval
= APVAL(vreal
, ew
);
1491 if((v
->saved_user_val
.p
&&
1492 (!pval
|| strcmp(v
->saved_user_val
.p
, pval
))) ||
1493 (!v
->saved_user_val
.p
&& pval
)){
1494 /* It changed, fix it */
1497 /* free the changed value */
1499 fs_give((void **)apval
);
1501 if(v
->saved_user_val
.p
)
1502 *apval
= cpystr(v
->saved_user_val
.p
);
1508 if(vreal
== &ps
->vars
[V_FEATURE_LIST
])
1509 set_feature_list_current_val(vreal
);
1511 set_current_val(vreal
, TRUE
, FALSE
);
1513 fix_side_effects(ps
, vreal
, 1);
1520 save_smime_config_vars(struct pine
*ps
)
1522 struct variable
*vreal
;
1523 SAVED_CONFIG_S
*vsave
, *v
;
1525 vsave
= (SAVED_CONFIG_S
*)fs_get((V_LAST_VAR
+1)*sizeof(SAVED_CONFIG_S
));
1526 memset((void *)vsave
, 0, (V_LAST_VAR
+1)*sizeof(SAVED_CONFIG_S
));
1528 for(vreal
= ps
->vars
; vreal
->name
; vreal
++,v
++){
1529 if(!(smime_related_var(ps
, vreal
) || vreal
==&ps
->vars
[V_FEATURE_LIST
]))
1536 if(LVAL(vreal
, ew
)){
1537 /* count how many */
1539 list
= LVAL(vreal
, ew
);
1543 v
->saved_user_val
.l
= (char **)fs_get((n
+1) * sizeof(char *));
1544 memset((void *)v
->saved_user_val
.l
, 0, (n
+1)*sizeof(char *));
1545 for(i
= 0; i
< n
; i
++)
1546 v
->saved_user_val
.l
[i
] = cpystr(list
[i
]);
1548 v
->saved_user_val
.l
[n
] = NULL
;
1553 v
->saved_user_val
.p
= cpystr(PVAL(vreal
, ew
));
1562 free_saved_smime_config(struct pine
*ps
, SAVED_CONFIG_S
**vsavep
)
1564 struct variable
*vreal
;
1567 if(vsavep
&& *vsavep
){
1568 for(v
= *vsavep
, vreal
= ps
->vars
; vreal
->name
; vreal
++,v
++){
1569 if(!(smime_related_var(ps
, vreal
)))
1572 if(vreal
->is_list
){ /* free saved_user_val.l */
1573 if(v
&& v
->saved_user_val
.l
)
1574 free_list_array(&v
->saved_user_val
.l
);
1576 else if(v
&& v
->saved_user_val
.p
)
1577 fs_give((void **)&v
->saved_user_val
.p
);
1580 fs_give((void **)vsavep
);