* Add "remove password" command to the management screen for the
[alpine.git] / alpine / smime.c
blob2fe22a367ddea9b2a55374ba9e38b923e3504b2c
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 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
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "headers.h"
30 #ifdef SMIME
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"
38 #include "../pith/tempfile.h"
39 #include "radio.h"
40 #include "keymenu.h"
41 #include "mailcmd.h"
42 #include "mailview.h"
43 #include "conftype.h"
44 #include "confscroll.h"
45 #include "setup.h"
46 #include "smime.h"
48 /* internal prototypes */
49 void format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc);
50 void print_separator_line(int percent, int ch, gf_io_t pc);
51 void output_cert_info(X509 *cert, gf_io_t pc);
52 void output_X509_NAME(X509_NAME *name, gf_io_t pc);
53 void side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc);
54 STORE_S *wrap_store(STORE_S *in, int width);
55 void smime_config_init_display(struct pine *, CONF_S **, CONF_S **);
56 void revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave);
57 SAVED_CONFIG_S *save_smime_config_vars(struct pine *ps);
58 void free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep);
59 int smime_helper_tool(struct pine *, int, CONF_S **, unsigned);
60 void manage_certificates(struct pine *, WhichCerts);
61 #ifdef PASSFILE
62 void manage_password_file_certificates(struct pine *);
63 #endif /* PASSFILE */
64 void smime_manage_certs_init (struct pine *, CONF_S **, CONF_S **, WhichCerts, int);
65 void smime_manage_password_file_certs_init(struct pine *, CONF_S **, CONF_S **, int, int *);
66 void display_certificate_information(struct pine *, X509 *, char *, WhichCerts, int num);
67 int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags);
68 int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *);
69 void smime_setup_size(char **, size_t, size_t);
73 * prompt the user for their passphrase
74 * (possibly prompting with the email address in s_passphrase_emailaddr)
76 int
77 smime_get_passphrase(void)
79 int rc;
80 int flags;
81 char prompt[500];
82 HelpType help = NO_HELP;
84 assert(ps_global->smime != NULL);
85 snprintf(prompt, sizeof(prompt),
86 _("Enter passphrase for <%s>: "), (ps_global->smime && ps_global->smime->passphrase_emailaddr) ? ps_global->smime->passphrase_emailaddr[0] : "unknown");
88 do {
89 flags = OE_PASSWD | OE_DISALLOW_HELP;
90 ((char *) ps_global->smime->passphrase)[0] = '\0';
91 rc = optionally_enter((char *) ps_global->smime->passphrase,
92 -FOOTER_ROWS(ps_global), 0,
93 sizeof(ps_global->smime->passphrase),
94 prompt, NULL, help, &flags);
95 } while (rc!=0 && rc!=1 && rc>0);
97 if(rc==0){
98 if(ps_global->smime)
99 ps_global->smime->entered_passphrase = 1;
102 return rc; /* better return rc and make the caller check its return value */
106 smime_check(BODY *body)
108 int rv = 0;
109 PKCS7 *p7 = NULL;
111 if(body->type == TYPEMULTIPART){
112 PART *p;
114 for(p=body->nested.part; p && rv == 0; p=p->next)
115 rv += smime_check(&p->body);
117 if(rv > 0) return rv;
118 if(body->sparep)
119 p7 = get_smime_sparep_type(body->sparep) == P7Type
120 ? (PKCS7 *)get_smime_sparep_data(body->sparep)
121 : NULL;
122 if(p7 && (PKCS7_type_is_signed(p7) || PKCS7_type_is_enveloped(p7)))
123 rv += 1;
124 return rv;
128 void
129 display_smime_info(struct pine *ps, ENVELOPE *env, BODY *body)
131 OtherMenu what = FirstMenu;
132 HANDLE_S *handles = NULL;
133 SCROLL_S scrollargs;
134 STORE_S *store = NULL;
135 long msgno;
136 int offset = 0;
138 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
139 store = so_get(CharStar, NULL, EDIT_ACCESS);
141 while(ps->next_screen == SCREEN_FUN_NULL){
143 ClearLine(1);
145 so_truncate(store, 0);
147 view_writec_init(store, &handles, HEADER_ROWS(ps),
148 HEADER_ROWS(ps) +
149 ps->ttyo->screen_rows - (HEADER_ROWS(ps)
150 + HEADER_ROWS(ps)));
152 gf_puts_uline("Overview", view_writec);
153 gf_puts(NEWLINE, view_writec);
155 format_smime_info(1, body, msgno, view_writec);
156 gf_puts(NEWLINE, view_writec);
157 format_smime_info(2, body, msgno, view_writec);
159 view_writec_destroy();
161 ps->next_screen = SCREEN_FUN_NULL;
163 memset(&scrollargs, 0, sizeof(SCROLL_S));
164 scrollargs.text.text = so_text(store);
165 scrollargs.text.src = CharStar;
166 scrollargs.text.desc = "S/MIME information";
167 scrollargs.body_valid = 1;
169 if(offset){ /* resize? preserve paging! */
170 scrollargs.start.on = Offset;
171 scrollargs.start.loc.offset = offset;
172 offset = 0L;
175 scrollargs.bar.title = "S/MIME INFORMATION";
176 /* scrollargs.end_scroll = view_end_scroll; */
177 scrollargs.resize_exit = 1;
178 scrollargs.help.text = NULL;
179 scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
180 scrollargs.keys.menu = &smime_info_keymenu;
181 scrollargs.keys.what = what;
182 setbitmap(scrollargs.keys.bitmap);
184 if(scrolltool(&scrollargs) == MC_RESIZE)
185 offset = scrollargs.start.loc.offset;
188 so_give(&store);
191 void
192 smime_info_screen(struct pine *ps)
194 long msgno;
195 BODY *body;
196 ENVELOPE *env;
198 /* ps->prev_screen = smime_info_screen;
199 ps->next_screen = SCREEN_FUN_NULL; */
201 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
203 env = mail_fetch_structure(ps->mail_stream, msgno, &body, 0);
205 if(!env || !body){
206 q_status_message(SM_ORDER, 0, 3,
207 _("Can't fetch body of message."));
208 return;
211 if(smime_check(body) == 0){
212 q_status_message(SM_ORDER | SM_DING, 0, 3,
213 _("Not a signed or encrypted message"));
214 return;
217 if(mn_total_cur(ps->msgmap) > 1L){
218 q_status_message(SM_ORDER | SM_DING, 0, 3,
219 _("Can only view one message's information at a time."));
220 return;
223 display_smime_info(ps, env, body);
227 void
228 format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc)
230 PKCS7 *p7 = NULL;
231 int i;
233 if(body->type == TYPEMULTIPART){
234 PART *p;
236 for(p=body->nested.part; p; p=p->next)
237 format_smime_info(pass, &p->body, msgno, pc);
239 if(body->sparep)
240 p7 = get_smime_sparep_type(body->sparep) == P7Type
241 ? (PKCS7 *)get_smime_sparep_data(body->sparep)
242 : NULL;
243 if(p7){
245 if(PKCS7_type_is_signed(p7)){
246 STACK_OF(X509) *signers;
248 switch(pass){
249 case 1:
250 gf_puts(_("This message was cryptographically signed."), pc);
251 gf_puts(NEWLINE, pc);
252 break;
254 case 2:
255 signers = PKCS7_get0_signers(p7, NULL, 0);
257 if(signers){
259 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s used for signing"),
260 plural(sk_X509_num(signers)));
261 gf_puts_uline(tmp_20k_buf, pc);
262 gf_puts(NEWLINE, pc);
263 print_separator_line(100, '-', pc);
265 for(i=0; i<sk_X509_num(signers); i++){
266 X509 *x = sk_X509_value(signers, i);
268 if(x){
269 output_cert_info(x, pc);
270 gf_puts(NEWLINE, pc);
275 sk_X509_free(signers);
276 break;
280 else if(PKCS7_type_is_enveloped(p7)){
282 switch(pass){
283 case 1:
284 gf_puts(_("This message was encrypted."), pc);
285 gf_puts(NEWLINE, pc);
286 break;
288 case 2:
289 if(p7->d.enveloped && p7->d.enveloped->enc_data){
290 X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
291 STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
292 int found = 0;
294 gf_puts(_("The algorithm used to encrypt was "), pc);
296 if(alg){
297 char *n = (char *) OBJ_nid2sn( OBJ_obj2nid(alg->algorithm));
299 gf_puts(n ? n : "<unknown>", pc);
302 else
303 gf_puts("<unknown>", pc);
305 gf_puts("." NEWLINE NEWLINE, pc);
307 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s for decrypting"),
308 plural(sk_PKCS7_RECIP_INFO_num(ris)));
309 gf_puts_uline(tmp_20k_buf, pc);
310 gf_puts(NEWLINE, pc);
311 print_separator_line(100, '-', pc);
313 for(i=0; i<sk_PKCS7_RECIP_INFO_num(ris); i++){
314 PKCS7_RECIP_INFO *ri;
315 PERSONAL_CERT *pcert;
317 ri = sk_PKCS7_RECIP_INFO_value(ris, i);
318 if(!ri)
319 continue;
321 pcert = find_certificate_matching_recip_info(ri);
323 if(pcert){
324 if(found){
325 print_separator_line(25, '*', pc);
326 gf_puts(NEWLINE, pc);
329 found = 1;
331 output_cert_info(pcert->cert, pc);
332 gf_puts(NEWLINE, pc);
337 if(!found){
338 gf_puts(_("No certificate capable of decrypting could be found."), pc);
339 gf_puts(NEWLINE, pc);
340 gf_puts(NEWLINE, pc);
344 break;
351 void
352 print_separator_line(int percent, int ch, gf_io_t pc)
354 int i, start, len;
356 len = ps_global->ttyo->screen_cols * percent / 100;
357 start = (ps_global->ttyo->screen_cols - len)/2;
359 for(i=0; i<start; i++)
360 pc(' ');
362 for(i=start; i<start+len; i++)
363 pc(ch);
365 gf_puts(NEWLINE, pc);
369 void
370 output_cert_info(X509 *cert, gf_io_t pc)
372 char buf[256];
373 STORE_S *left,*right;
374 gf_io_t spc;
375 int len, error;
376 STACK_OF(X509) *chain;
378 left = so_get(CharStar, NULL, EDIT_ACCESS);
379 right = so_get(CharStar, NULL, EDIT_ACCESS);
380 if(!(left && right))
381 return;
383 gf_set_so_writec(&spc, left);
385 gf_puts_uline("Certificate Owner", spc);
386 gf_puts(NEWLINE, spc);
388 output_X509_NAME(X509_get_subject_name(cert), spc);
389 gf_puts(NEWLINE, spc);
391 gf_puts_uline("Serial Number", spc);
392 gf_puts(NEWLINE, spc);
394 { ASN1_INTEGER *bs;
395 long l;
396 const char *neg;
397 int i;
399 bs = X509_get_serialNumber(cert);
400 if (bs->length <= (int)sizeof(long)){
401 l = ASN1_INTEGER_get(bs);
402 if (bs->type == V_ASN1_NEG_INTEGER){
403 l = -l;
404 neg="-";
406 else
407 neg="";
408 snprintf(buf, sizeof(buf), " %s%lu (%s0x%lx)", neg, l, neg, l);
409 } else {
410 snprintf(buf, sizeof(buf), "%s", bs->type == V_ASN1_NEG_INTEGER ? "(Negative)" : "");
411 for (i = 0; i < bs->length; i++)
412 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%02x%s", bs->data[i],
413 i+1 == bs->length ? "" : ":");
416 gf_puts(buf, spc);
417 gf_puts(NEWLINE, spc);
418 gf_puts(NEWLINE, spc);
420 gf_puts_uline("Validity", spc);
421 gf_puts(NEWLINE, spc);
422 { BIO *mb = BIO_new(BIO_s_mem());
423 char iobuf[4096];
425 gf_puts("Not Before: ", spc);
427 (void) BIO_reset(mb);
428 ASN1_UTCTIME_print(mb, X509_get0_notBefore(cert));
429 (void) BIO_flush(mb);
430 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
431 gf_nputs(iobuf, len, spc);
433 gf_puts(NEWLINE, spc);
435 gf_puts("Not After: ", spc);
437 (void) BIO_reset(mb);
438 ASN1_UTCTIME_print(mb, X509_get0_notAfter(cert));
439 (void) BIO_flush(mb);
440 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
441 gf_nputs(iobuf, len, spc);
443 gf_puts(NEWLINE, spc);
444 gf_puts(NEWLINE, spc);
446 BIO_free(mb);
449 gf_clear_so_writec(left);
451 gf_set_so_writec(&spc, right);
453 gf_puts_uline("Issuer", spc);
454 gf_puts(NEWLINE, spc);
456 output_X509_NAME(X509_get_issuer_name(cert), spc);
457 gf_puts(NEWLINE, spc);
459 gf_clear_so_writec(right);
461 side_by_side(left, right, pc);
463 gf_puts_uline("SHA1 Fingerprint", pc);
464 gf_puts(NEWLINE, pc);
465 get_fingerprint(cert, EVP_sha1(), buf, sizeof(buf), ":");
466 gf_puts(buf, pc);
467 gf_puts(NEWLINE, pc);
469 gf_puts_uline("MD5 Fingerprint", pc);
470 gf_puts(NEWLINE, pc);
471 get_fingerprint(cert, EVP_md5(), buf, sizeof(buf), ":");
472 gf_puts(buf, pc);
473 gf_puts(NEWLINE, pc);
474 gf_puts(NEWLINE, pc);
476 gf_puts_uline("Certificate Chain Information", pc);
477 gf_puts(NEWLINE, pc);
479 if((chain = get_chain_for_cert(cert, &error, &len)) != NULL){
480 X509 *x;
481 X509_NAME_ENTRY *e;
482 int i, offset = 2;
483 char space[256];
484 X509_NAME *subject;
486 for(i = 0; i < offset; i++) space[i] = ' ';
488 for(i = -1; i < sk_X509_num(chain); i++){
489 char buf[256];
491 x = i == -1 ? cert : sk_X509_value(chain, i);
493 if(x){
494 if(i>=0){
495 space[offset + i + 0] = ' ';
496 space[offset + i + 1] = '\\';
497 space[offset + i + 2] = '-';
498 space[offset + i + 3] = ' ';
499 space[offset + i + 4] = '\0';
500 gf_puts(space, pc);
502 else{
503 space[offset] = '\0';
504 gf_puts(space, pc);
506 if(i >= 0)
507 gf_puts_uline("Signed by: ", pc);
508 else
509 gf_puts_uline("Issued to: ", pc);
511 subject = X509_get_subject_name(x);
513 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
514 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
515 gf_puts(buf, pc);
516 gf_puts(NEWLINE, pc);
519 else{
520 gf_puts("No certificate info found", pc);
521 gf_puts(NEWLINE, pc);
522 break;
525 e = X509_NAME_get_entry(X509_get_issuer_name(x),
526 X509_NAME_entry_count(X509_get_issuer_name(x))-1);
527 if(e){
528 X509_NAME_get_text_by_OBJ(X509_get_issuer_name(x), X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
529 space[offset + i + 0] = ' ';
530 space[offset + i + 1] = '\\';
531 space[offset + i + 2] = '-';
532 space[offset + i + 3] = ' ';
533 space[offset + i + 4] = '\0';
534 gf_puts(space, pc);
535 gf_puts_uline("Signed by: ", pc);
536 gf_puts(buf, pc);
537 gf_puts(NEWLINE, pc);
539 sk_X509_pop_free(chain, X509_free);
541 gf_puts(NEWLINE, pc);
543 so_give(&left);
544 so_give(&right);
548 void
549 output_X509_NAME(X509_NAME *name, gf_io_t pc)
551 int i, c;
552 char buf[256];
554 c = X509_NAME_entry_count(name);
556 for(i=c-1; i>=0; i--){
557 X509_NAME_ENTRY *e;
559 e = X509_NAME_get_entry(name,i);
560 if(!e)
561 continue;
563 X509_NAME_get_text_by_OBJ(name, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
565 gf_puts(buf, pc);
566 gf_puts(NEWLINE, pc);
572 * Output the contents of the given stores (left and right)
573 * to the given gf_io_t.
574 * The width of the terminal is inspected and two columns
575 * are created to fit the stores into. They are then wrapped
576 * and merged.
578 void
579 side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
581 STORE_S *left_wrapped;
582 STORE_S *right_wrapped;
583 char buf_l[256];
584 char buf_r[256];
585 char *l, *r;
586 char *b;
587 int i;
588 int w = ps_global->ttyo->screen_cols/2 - 1;
590 so_seek(left, 0, 0);
591 so_seek(right, 0, 0);
593 left_wrapped = wrap_store(left, w);
594 right_wrapped = wrap_store(right, w);
596 so_seek(left_wrapped, 0, 0);
597 so_seek(right_wrapped, 0, 0);
599 for(;;){
601 l = so_fgets(left_wrapped, buf_l, sizeof(buf_l));
602 r = so_fgets(right_wrapped, buf_r, sizeof(buf_r));
603 if(l == NULL && r == NULL)
604 break;
606 for(i=0, b=buf_l; i<w && *b && *b!='\r' && *b!='\n'; i++,b++){
607 pc(*b);
608 /* reduce accumulated width if an embed tag is discovered */
609 if(*b==TAG_EMBED)
610 i-=2;
613 if(buf_r[0]){
614 while(i<w){
615 pc(' ');
616 i++;
618 pc(' ');
620 for(i=0, b=buf_r; i<w && *b && *b!='\r' && *b!='\n'; i++,b++)
621 pc(*b);
624 gf_puts(NEWLINE, pc);
627 so_give(&left_wrapped);
628 so_give(&right_wrapped);
632 * Wrap the text in the given store to the given width.
633 * A new store is created for the result.
635 STORE_S *
636 wrap_store(STORE_S *in, int width)
638 STORE_S *result;
639 void *ws;
640 gf_io_t ipc,opc;
642 if(width<10)
643 width = 10;
645 result = so_get(CharStar, NULL, EDIT_ACCESS);
646 ws = gf_wrap_filter_opt(width, width, NULL, 0, 0);
648 gf_filter_init();
649 gf_link_filter(gf_wrap, ws);
651 gf_set_so_writec(&opc, result);
652 gf_set_so_readc(&ipc, in);
654 gf_pipe(ipc, opc);
656 gf_clear_so_readc(in);
657 gf_clear_so_writec(result);
659 return result;
663 void
664 smime_config_screen(struct pine *ps, int edit_exceptions)
666 CONF_S *ctmp = NULL, *first_line = NULL;
667 SAVED_CONFIG_S *vsave;
668 OPT_SCREEN_S screen;
669 int ew, readonly_warning = 0;
671 dprint((9, "smime_config_screen()"));
672 ps->next_screen = SCREEN_FUN_NULL;
675 * this is necessary because we need to know the correct paths
676 * to configure certificates and keys, and we could get here
677 * without having done that before we reach this place.
679 smime_reinit();
681 if(ps->fix_fixed_warning)
682 offer_to_fix_pinerc(ps);
684 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
686 if(ps->restricted)
687 readonly_warning = 1;
688 else{
689 PINERC_S *prc = NULL;
691 switch(ew){
692 case Main:
693 prc = ps->prc;
694 break;
695 case Post:
696 prc = ps->post_prc;
697 break;
698 default:
699 break;
702 readonly_warning = prc ? prc->readonly : 1;
703 if(prc && prc->quit_to_edit){
704 quit_to_edit_msg(prc);
705 return;
709 smime_config_init_display(ps, &ctmp, &first_line);
711 vsave = save_smime_config_vars(ps);
713 memset(&screen, 0, sizeof(screen));
714 screen.deferred_ro_warning = readonly_warning;
715 switch(conf_scroll_screen(ps, &screen, first_line,
716 edit_exceptions ? _("SETUP S/MIME EXCEPTIONS")
717 : _("SETUP S/MIME"),
718 /* TRANSLATORS: Print something1 using something2.
719 configuration is something1 */
720 _("configuration"), 0, NULL)){
721 case 0:
722 break;
724 case 1:
725 write_pinerc(ps, ew, WRP_NONE);
726 break;
728 case 10:
729 revert_to_saved_smime_config(ps, vsave);
730 break;
732 default:
733 q_status_message(SM_ORDER, 7, 10,
734 _("conf_scroll_screen bad ret in smime_config"));
735 break;
738 free_saved_smime_config(ps, &vsave);
739 smime_reinit();
744 smime_related_var(struct pine *ps, struct variable *var)
746 return(var == &ps->vars[V_PUBLICCERT_DIR] ||
747 var == &ps->vars[V_PUBLICCERT_CONTAINER] ||
748 var == &ps->vars[V_PRIVATEKEY_DIR] ||
749 var == &ps->vars[V_PRIVATEKEY_CONTAINER] ||
750 var == &ps->vars[V_CACERT_DIR] ||
751 var == &ps->vars[V_CACERT_CONTAINER]);
754 void
755 smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
757 char tmp[200];
758 int i, ind, ln = 0;
759 struct variable *vtmp;
760 CONF_S *ctmpb;
761 FEATURE_S *feature;
763 /* find longest variable name */
764 for(vtmp = ps->vars; vtmp->name; vtmp++){
765 if(!(smime_related_var(ps, vtmp)))
766 continue;
768 if((i = utf8_width(pretty_var_name(vtmp->name))) > ln)
769 ln = i;
772 for(vtmp = ps->vars; vtmp->name; vtmp++){
773 if(!(smime_related_var(ps, vtmp)))
774 continue;
776 new_confline(ctmp)->var = vtmp;
777 if(first_line && !*first_line)
778 *first_line = *ctmp;
780 (*ctmp)->valoffset = ln+3;
781 (*ctmp)->keymenu = &config_text_keymenu;
782 (*ctmp)->help = config_help(vtmp - ps->vars, 0);
783 (*ctmp)->tool = text_tool;
785 utf8_snprintf(tmp, sizeof(tmp), "%-*.100w =", ln, pretty_var_name(vtmp->name));
786 tmp[sizeof(tmp)-1] = '\0';
788 (*ctmp)->varname = cpystr(tmp);
789 (*ctmp)->varnamep = (*ctmp);
790 (*ctmp)->flags = CF_STARTITEM;
791 (*ctmp)->value = pretty_value(ps, *ctmp);
795 vtmp = &ps->vars[V_FEATURE_LIST];
797 new_confline(ctmp);
798 ctmpb = (*ctmp);
799 (*ctmp)->flags |= CF_NOSELECT | CF_STARTITEM;
800 (*ctmp)->keymenu = &config_checkbox_keymenu;
801 (*ctmp)->tool = NULL;
803 /* put a nice delimiter before list */
804 new_confline(ctmp)->var = NULL;
805 (*ctmp)->varnamep = ctmpb;
806 (*ctmp)->keymenu = &config_checkbox_keymenu;
807 (*ctmp)->help = NO_HELP;
808 (*ctmp)->tool = checkbox_tool;
809 (*ctmp)->valoffset = feature_indent();
810 (*ctmp)->flags |= CF_NOSELECT;
811 (*ctmp)->value = cpystr("Set Feature Name");
813 new_confline(ctmp)->var = NULL;
814 (*ctmp)->varnamep = ctmpb;
815 (*ctmp)->keymenu = &config_checkbox_keymenu;
816 (*ctmp)->help = NO_HELP;
817 (*ctmp)->tool = checkbox_tool;
818 (*ctmp)->valoffset = feature_indent();
819 (*ctmp)->flags |= CF_NOSELECT;
820 (*ctmp)->value = cpystr("--- ----------------------");
822 ind = feature_list_index(F_DONT_DO_SMIME);
823 feature = feature_list(ind);
824 new_confline(ctmp)->var = vtmp;
825 (*ctmp)->varnamep = ctmpb;
826 (*ctmp)->keymenu = &config_checkbox_keymenu;
827 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
828 (*ctmp)->tool = checkbox_tool;
829 (*ctmp)->valoffset = feature_indent();
830 (*ctmp)->varmem = ind;
831 (*ctmp)->value = pretty_value(ps, (*ctmp));
833 ind = feature_list_index(F_ENCRYPT_DEFAULT_ON);
834 feature = feature_list(ind);
835 new_confline(ctmp)->var = vtmp;
836 (*ctmp)->varnamep = ctmpb;
837 (*ctmp)->keymenu = &config_checkbox_keymenu;
838 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
839 (*ctmp)->tool = checkbox_tool;
840 (*ctmp)->valoffset = feature_indent();
841 (*ctmp)->varmem = ind;
842 (*ctmp)->value = pretty_value(ps, (*ctmp));
844 ind = feature_list_index(F_REMEMBER_SMIME_PASSPHRASE);
845 feature = feature_list(ind);
846 new_confline(ctmp)->var = vtmp;
847 (*ctmp)->varnamep = ctmpb;
848 (*ctmp)->keymenu = &config_checkbox_keymenu;
849 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
850 (*ctmp)->tool = checkbox_tool;
851 (*ctmp)->valoffset = feature_indent();
852 (*ctmp)->varmem = ind;
853 (*ctmp)->value = pretty_value(ps, (*ctmp));
855 ind = feature_list_index(F_SIGN_DEFAULT_ON);
856 feature = feature_list(ind);
857 new_confline(ctmp)->var = vtmp;
858 (*ctmp)->varnamep = ctmpb;
859 (*ctmp)->keymenu = &config_checkbox_keymenu;
860 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
861 (*ctmp)->tool = checkbox_tool;
862 (*ctmp)->valoffset = feature_indent();
863 (*ctmp)->varmem = ind;
864 (*ctmp)->value = pretty_value(ps, (*ctmp));
866 ind = feature_list_index(F_USE_CERT_STORE_ONLY);
867 feature = feature_list(ind);
868 new_confline(ctmp)->var = vtmp;
869 (*ctmp)->varnamep = ctmpb;
870 (*ctmp)->keymenu = &config_checkbox_keymenu;
871 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
872 (*ctmp)->tool = checkbox_tool;
873 (*ctmp)->valoffset = feature_indent();
874 (*ctmp)->varmem = ind;
875 (*ctmp)->value = pretty_value(ps, (*ctmp));
877 #ifdef APPLEKEYCHAIN
878 new_confline(ctmp);
879 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
881 new_confline(ctmp);
882 (*ctmp)->flags |= CF_NOSELECT;
883 (*ctmp)->value = cpystr(_("Mac OS X specific features"));
885 ind = feature_list_index(F_PUBLICCERTS_IN_KEYCHAIN);
886 feature = feature_list(ind);
887 new_confline(ctmp)->var = vtmp;
888 (*ctmp)->varnamep = ctmpb;
889 (*ctmp)->keymenu = &config_checkbox_keymenu;
890 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
891 (*ctmp)->tool = checkbox_tool;
892 (*ctmp)->valoffset = feature_indent();
893 (*ctmp)->varmem = ind;
894 (*ctmp)->value = pretty_value(ps, (*ctmp));
895 #endif /* APPLEKEYCHAIN */
897 new_confline(ctmp);
898 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
900 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
901 tmp[i] = '-';
902 new_confline(ctmp);
903 (*ctmp)->flags |= CF_NOSELECT;
904 (*ctmp)->value = cpystr(tmp);
906 new_confline(ctmp);
907 (*ctmp)->flags |= CF_NOSELECT;
908 (*ctmp)->value = cpystr(_("Be careful with the following commands, they REPLACE contents in the target"));
910 new_confline(ctmp);
911 (*ctmp)->flags |= CF_NOSELECT;
912 (*ctmp)->value = cpystr(tmp);
914 new_confline(ctmp);
915 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
917 /* copy public directory to container */
918 new_confline(ctmp);
919 (*ctmp)->tool = smime_helper_tool;
920 (*ctmp)->keymenu = &config_smime_helper_keymenu;
921 (*ctmp)->help = h_config_smime_transfer_pub_to_con;
922 (*ctmp)->value = cpystr(_("Transfer public certs FROM directory TO container"));
923 (*ctmp)->varmem = 1;
925 /* copy private directory to container */
926 new_confline(ctmp);
927 (*ctmp)->tool = smime_helper_tool;
928 (*ctmp)->keymenu = &config_smime_helper_keymenu;
929 (*ctmp)->help = h_config_smime_transfer_priv_to_con;
930 (*ctmp)->value = cpystr(_("Transfer private keys FROM directory TO container"));
931 (*ctmp)->varmem = 3;
933 /* copy cacert directory to container */
934 new_confline(ctmp);
935 (*ctmp)->tool = smime_helper_tool;
936 (*ctmp)->keymenu = &config_smime_helper_keymenu;
937 (*ctmp)->help = h_config_smime_transfer_cacert_to_con;
938 (*ctmp)->value = cpystr(_("Transfer CA certs FROM directory TO container"));
939 (*ctmp)->varmem = 5;
941 new_confline(ctmp)->var = vtmp;
942 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
944 /* copy public container to directory */
945 new_confline(ctmp);
946 (*ctmp)->tool = smime_helper_tool;
947 (*ctmp)->keymenu = &config_smime_helper_keymenu;
948 (*ctmp)->help = h_config_smime_transfer_pub_to_dir;
949 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO directory"));
950 (*ctmp)->varmem = 2;
952 /* copy private container to directory */
953 new_confline(ctmp);
954 (*ctmp)->tool = smime_helper_tool;
955 (*ctmp)->keymenu = &config_smime_helper_keymenu;
956 (*ctmp)->help = h_config_smime_transfer_priv_to_dir;
957 (*ctmp)->value = cpystr(_("Transfer private keys FROM container TO directory"));
958 (*ctmp)->varmem = 4;
960 /* copy cacert container to directory */
961 new_confline(ctmp);
962 (*ctmp)->tool = smime_helper_tool;
963 (*ctmp)->keymenu = &config_smime_helper_keymenu;
964 (*ctmp)->help = h_config_smime_transfer_cacert_to_dir;
965 (*ctmp)->value = cpystr(_("Transfer CA certs FROM container TO directory"));
966 (*ctmp)->varmem = 6;
968 #ifdef APPLEKEYCHAIN
970 new_confline(ctmp)->var = vtmp;
971 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
973 /* copy public container to keychain */
974 new_confline(ctmp);
975 (*ctmp)->tool = smime_helper_tool;
976 (*ctmp)->keymenu = &config_smime_helper_keymenu;
977 (*ctmp)->help = h_config_smime_transfer_pubcon_to_key;
978 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO keychain"));
979 (*ctmp)->varmem = 7;
981 /* copy public keychain to container */
982 new_confline(ctmp);
983 (*ctmp)->tool = smime_helper_tool;
984 (*ctmp)->keymenu = &config_smime_helper_keymenu;
985 (*ctmp)->help = h_config_smime_transfer_pubkey_to_con;
986 (*ctmp)->value = cpystr(_("Transfer public certs FROM keychain TO container"));
987 (*ctmp)->varmem = 8;
989 #endif /* APPLEKEYCHAIN */
991 if(SMHOLDERTYPE(Private) == Keychain
992 && SMHOLDERTYPE(Public) == Keychain
993 && SMHOLDERTYPE(CACert) == Keychain)
994 return;
996 new_confline(ctmp)->var = vtmp;
997 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
999 new_confline(ctmp);
1000 (*ctmp)->flags |= CF_NOSELECT;
1001 (*ctmp)->value = cpystr(tmp);
1003 new_confline(ctmp);
1004 (*ctmp)->flags |= CF_NOSELECT;
1005 (*ctmp)->value = cpystr(_("Manage your own certificates"));
1007 new_confline(ctmp);
1008 (*ctmp)->flags |= CF_NOSELECT;
1009 (*ctmp)->value = cpystr(tmp);
1011 new_confline(ctmp)->var = vtmp;
1012 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1014 /* manage public certificates */
1015 new_confline(ctmp);
1016 (*ctmp)->tool = smime_helper_tool;
1017 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1018 (*ctmp)->help = h_config_smime_public_certificates;
1019 (*ctmp)->value = cpystr(_("Manage Public Certificates"));
1020 (*ctmp)->varmem = 9;
1021 (*ctmp)->d.s.ctype = Public;
1023 /* manage private keys */
1024 new_confline(ctmp);
1025 (*ctmp)->tool = smime_helper_tool;
1026 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1027 (*ctmp)->help = h_config_smime_private_keys;
1028 (*ctmp)->value = cpystr(_("Manage Private Keys"));
1029 (*ctmp)->varmem = 10;
1030 (*ctmp)->d.s.ctype = Private;
1032 /* manage Certificate Authorities */
1033 new_confline(ctmp);
1034 (*ctmp)->tool = smime_helper_tool;
1035 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1036 (*ctmp)->help = h_config_smime_certificate_authorities;
1037 (*ctmp)->value = cpystr(_("Manage Certificate Authorities"));
1038 (*ctmp)->varmem = 11;
1039 (*ctmp)->d.s.ctype = CACert;
1041 #ifdef PASSFILE
1042 new_confline(ctmp)->var = vtmp;
1043 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1045 new_confline(ctmp);
1046 (*ctmp)->flags |= CF_NOSELECT;
1047 (*ctmp)->value = cpystr(tmp);
1049 new_confline(ctmp);
1050 (*ctmp)->flags |= CF_NOSELECT;
1051 (*ctmp)->value = cpystr(_("Manage Key and Certificate for Password File"));
1053 new_confline(ctmp);
1054 (*ctmp)->flags |= CF_NOSELECT;
1055 (*ctmp)->value = cpystr(tmp);
1057 new_confline(ctmp)->var = vtmp;
1058 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1060 /* manage password file certificates */
1061 new_confline(ctmp);
1062 (*ctmp)->tool = smime_helper_tool;
1063 (*ctmp)->keymenu = &config_smime_manage_password_file_menu_keymenu;
1064 (*ctmp)->help = h_config_smime_password_file_certificates;
1065 (*ctmp)->value = cpystr(_("Manage Password File Key and Certificate"));
1066 (*ctmp)->varmem = 12;
1067 (*ctmp)->d.s.ctype = Password;
1068 #endif /* PASSFILE */
1070 (*ctmp)->next = NULL;
1073 void display_certificate_information(struct pine *ps, X509 *cert, char *email, WhichCerts ctype, int num)
1075 STORE_S *store;
1076 SCROLL_S scrollargs;
1077 int cmd, offset;
1078 int pub_cert, priv_cert, new_store;
1079 long error;
1080 BIO *out = NULL;
1082 cmd = offset = pub_cert = priv_cert = 0;
1083 new_store = 1;
1084 ps->next_screen = SCREEN_FUN_NULL;
1085 do {
1086 /* MC_PRIVATE and MC_PUBLIC cancel each other,
1087 * they can not be active at the same time
1089 switch(cmd){
1090 case MC_PRIVATE:
1091 pub_cert = 0;
1092 priv_cert = 1 - priv_cert;
1093 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1094 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1095 break;
1097 case MC_PUBLIC:
1098 priv_cert = 0;
1099 pub_cert = 1 - pub_cert;
1100 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1101 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1102 break;
1104 case MC_TRUST:
1105 if(SMHOLDERTYPE(CACert) == Directory)
1106 save_cert_for(email, cert, CACert);
1107 else{ /* if(SMHOLDERTYPE(CACert) == Container) */
1108 char path[MAXPATH];
1109 char *upath = PATHCERTDIR(ctype);
1110 char *tempfile = tempfile_in_same_dir(path, "az", NULL);
1111 CertList *clist;
1113 if(IS_REMOTE(upath))
1114 strncpy(path, temp_nam(NULL, "a6"), sizeof(path)-1);
1115 else
1116 strncpy(path, upath, sizeof(path)-1);
1117 path[sizeof(path)-1] = '\0';
1119 add_to_end_of_certlist(&ps_global->smime->cacertlist, email, X509_dup(cert));
1120 for(clist=ps_global->smime->cacertlist; clist && clist->next; clist = clist->next);
1121 certlist_to_file(tempfile, clist);
1122 add_file_to_container(CACert, tempfile, email);
1123 unlink(tempfile);
1125 renew_store();
1126 new_store = 1;
1127 break;
1129 case MC_DELETE:
1130 if (get_cert_deleted(ctype, num) != 0)
1131 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1132 else{
1133 mark_cert_deleted(ctype, num, 1);
1134 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1136 break;
1138 case MC_UNDELETE:
1139 if (get_cert_deleted(ctype, num) != 0){
1140 mark_cert_deleted(ctype, num, 0);
1141 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1143 else
1144 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1145 break;
1147 default: break;
1150 if((pub_cert || priv_cert)
1151 && (out = print_private_key_information(email, priv_cert)) == NULL)
1152 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Private Certificate Information"));
1154 if(new_store){
1155 store = so_get(CharStar, NULL, EDIT_ACCESS);
1156 view_writec_init(store, NULL, HEADER_ROWS(ps),
1157 HEADER_ROWS(ps) + ps->ttyo->screen_rows - (HEADER_ROWS(ps)+ FOOTER_ROWS(ps)));
1159 snprintf(tmp_20k_buf, SIZEOF_20KBUF,"%s", _("Certificate Information"));
1160 gf_puts_uline(tmp_20k_buf, view_writec);
1161 gf_puts(NEWLINE, view_writec);
1162 print_separator_line(100, '-', view_writec);
1164 output_cert_info(cert, view_writec);
1165 gf_puts(NEWLINE, view_writec);
1167 if(smime_validate_cert(cert, &error) < 0){
1168 const char *errorp = X509_verify_cert_error_string(error);
1169 snprintf(tmp_20k_buf, SIZEOF_20KBUF,_("Error validating certificate: %s"), errorp);
1170 } else
1171 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", _("Certificate validated without errors"));
1173 gf_puts_uline(tmp_20k_buf, view_writec);
1174 gf_puts(NEWLINE, view_writec);
1176 if(out != NULL){ /* print private key information */
1177 unsigned char ch[2];
1179 gf_puts(NEWLINE, view_writec);
1180 ch[1] = '\0';
1181 while(BIO_read(out, ch, 1) >= 1)
1182 gf_puts((char *)ch, view_writec);
1183 gf_puts(NEWLINE, view_writec);
1184 q_status_message1(SM_ORDER, 1, 3, _("%s information shown at bottom of certificate information"), pub_cert ? _("Public") : _("Private"));
1185 BIO_free_all(out);
1186 out = NULL;
1188 view_writec_destroy();
1189 new_store = 0;
1192 memset(&scrollargs, 0, sizeof(SCROLL_S));
1194 scrollargs.text.text = so_text(store);
1195 scrollargs.text.src = CharStar;
1196 scrollargs.text.desc = "certificate information";
1197 scrollargs.body_valid = 1;
1199 if(offset){ /* resize? preserve paging! */
1200 scrollargs.start.on = Offset;
1201 scrollargs.start.loc.offset = offset;
1202 scrollargs.body_valid = 0;
1203 offset = 0L;
1206 scrollargs.use_indexline_color = 1;
1208 scrollargs.bar.title = _("CERTIFICATE INFORMATION");
1209 scrollargs.proc.tool = manage_certificate_info_tool;
1210 scrollargs.resize_exit = 1;
1211 scrollargs.help.text = h_certificate_information;
1212 scrollargs.help.title = _("HELP FOR MESSAGE TEXT VIEW");
1213 scrollargs.keys.what = FirstMenu;
1214 scrollargs.keys.menu = &smime_certificate_info_keymenu;
1215 setbitmap(scrollargs.keys.bitmap);
1216 if(ctype != Public || error != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
1217 /*error != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)*/
1218 clrbitn(TRUST_KEY, scrollargs.keys.bitmap);
1219 if(ctype != Private){
1220 clrbitn(PUBLIC_KEY, scrollargs.keys.bitmap);
1221 clrbitn(PRIVATE_KEY, scrollargs.keys.bitmap);
1223 if(ctype == Password){
1224 clrbitn(DELETE_CERT_KEY, scrollargs.keys.bitmap);
1225 clrbitn(UNDELETE_CERT_KEY, scrollargs.keys.bitmap);
1228 cmd = scrolltool(&scrollargs);
1230 switch(cmd){
1231 case MC_RESIZE :
1232 case MC_PRIVATE:
1233 case MC_PUBLIC : if(scrollargs.start.on == Offset)
1234 offset = scrollargs.start.loc.offset;
1235 new_store = 1;
1236 default: break;
1238 if(new_store)
1239 so_give(&store);
1240 } while (cmd != MC_EXIT);
1241 ps->mangled_screen = 1;
1245 * This is silly, we just need this function so that we can tell scrolltool
1246 * that some commands are recognized. We use scrolltool because we do not
1247 * want to rewrite output_cert_info.
1250 manage_certificate_info_tool(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1252 int rv;
1253 switch(cmd){
1254 case MC_DELETE:
1255 case MC_UNDELETE:
1256 case MC_PRIVATE:
1257 case MC_PUBLIC:
1258 case MC_TRUST: rv = 1; break;
1259 default: rv = 0; break;
1261 return rv;
1266 manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1268 int rv = 0;
1269 X509 *cert = NULL;
1270 WhichCerts ctype = (*cl)->d.s.ctype;
1272 switch(cmd){
1273 case MC_ADD: /* create a self signed certificate and import it */
1274 if(ctype == Password){
1275 PERSONAL_CERT *pc;
1276 char pathdir[MAXPATH+1], filename[MAXPATH+1];
1277 struct stat sbuf;
1278 int st;
1279 smime_path(DF_SMIMETMPDIR, pathdir, sizeof(pathdir));
1280 if(((st = our_stat(pathdir, &sbuf)) == 0
1281 && (sbuf.st_mode & S_IFMT) == S_IFDIR)
1282 || (st != 0
1283 && can_access(pathdir, ACCESS_EXISTS) != 0
1284 && our_mkpath(pathdir, 0700) == 0)){
1285 pc = ALPINE_self_signed_certificate(NULL, 0, pathdir, MASTERNAME);
1286 snprintf(filename, sizeof(filename), "%s/%s.key",
1287 pathdir, MASTERNAME);
1288 filename[sizeof(filename)-1] = '\0';
1289 rv = import_certificate(ctype, pc, filename);
1290 if(rv == 1){
1291 ps->keyemptypwd = 0;
1292 if(our_stat(pathdir, &sbuf) == 0){
1293 if(unlink(filename) < 0)
1294 q_status_message1(SM_ORDER, 0, 2,
1295 _("Could not remove private key %s.key"), MASTERNAME);
1296 filename[strlen(filename)-4] = '\0';
1297 strcat(filename, ".crt");
1298 if(unlink(filename) < 0)
1299 q_status_message1(SM_ORDER, 0, 2,
1300 _("Could not remove public certificate %s.crt"), MASTERNAME);
1301 if(rmdir(pathdir) < 0)
1302 q_status_message1(SM_ORDER, 0, 2,
1303 _("Could not remove temporary directory %s"), pathdir);
1307 rv = 10; /* forces redraw */
1309 break;
1311 case MC_CHOICE:
1312 if(PATHCERTDIR(ctype) == NULL)
1313 return 0;
1315 if((cert = get_cert_for((*cl)->d.s.address, ctype, 0)) == NULL){
1316 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Certificate"));
1317 rv = 0;
1319 else{
1320 display_certificate_information(ps, cert, (*cl)->d.s.address, ctype, (*cl)->varmem);
1321 rv = 10 + (*cl)->varmem;
1323 break;
1325 case MC_DELETE:
1326 if(ctype == Password){
1327 EVP_PKEY *key = NULL;
1328 PERSONAL_CERT *pc = (PERSONAL_CERT *) ps->pwdcert;
1329 RSA *rsa = NULL;
1330 const EVP_CIPHER *enc = NULL;
1331 BIO *out = NULL;
1332 BIO *in = NULL;
1333 char filename[MAXPATH+1];
1334 char passwd[MAILTMPLEN];
1335 char prompt[MAILTMPLEN];
1337 if (pc != NULL && pc->key != NULL){
1338 strncpy(prompt, _("Enter password to unlock key: "), sizeof(prompt));
1339 prompt[sizeof(prompt)-1] = '\0';
1340 passwd[0] = '\0';
1342 rv = alpine_get_password(prompt, passwd, sizeof(passwd));
1344 if(rv == 1)
1345 q_status_message(SM_ORDER, 1, 3, _("Password deletion cancelled"));
1346 else if(rv == 0){
1347 snprintf(filename, sizeof(filename), "%s/%s.key", ps->pwdcertdir, pc->name);
1348 filename[sizeof(filename)-1] = '\0';
1349 if((in = BIO_new_file(filename, "r")) != NULL){
1350 key = PEM_read_bio_PrivateKey(in, NULL, NULL, passwd);
1351 if(key != NULL){
1352 if((rsa = EVP_PKEY_get1_RSA(key)) != NULL
1353 && (out = BIO_new(BIO_s_file())) != NULL
1354 && BIO_write_filename(out, filename) > 0
1355 && PEM_write_bio_RSAPrivateKey(out, rsa, enc, NULL, 0, NULL, passwd) > 0){
1356 q_status_message(SM_ORDER, 1, 3, _("Password Removed from private key"));
1357 ps->keyemptypwd = 1;
1359 else
1360 rv = 1;
1362 else{
1363 rv = 1;
1364 q_status_message(SM_ORDER, 1, 3, _("Failed to unlock private key"));
1366 BIO_free(in);
1368 else
1369 rv = 1;
1371 if(rv == 1)
1372 q_status_message(SM_ORDER, 1, 3, _("Failed to remove password from private key"));
1373 rv += 10; /* forces redraw */
1374 if(out != NULL)
1375 BIO_free_all(out);
1376 if(rsa != NULL)
1377 RSA_free(rsa);
1378 if(key != NULL)
1379 EVP_PKEY_free(key);
1382 else {
1383 if ((*cl)->d.s.deleted != 0)
1384 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1385 else{
1386 (*cl)->d.s.deleted = 1;
1387 rv = 10 + (*cl)->varmem; /* forces redraw */
1388 mark_cert_deleted(ctype, (*cl)->varmem, 1);
1389 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1392 break;
1394 case MC_UNDELETE:
1395 if ((*cl)->d.s.deleted == 0)
1396 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1397 else{
1398 (*cl)->d.s.deleted = 0;
1399 mark_cert_deleted(ctype, (*cl)->varmem, 0);
1400 rv = 10 + (*cl)->varmem; /* forces redraw */
1401 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1403 break;
1405 case MC_EXPUNGE:
1406 { CertList *cl;
1408 for(cl = DATACERT(ctype); cl != NULL && DELETEDCERT(cl) == 0; cl = cl->next);
1409 if(cl != NULL && DELETEDCERT(cl) != 0){
1410 smime_expunge_cert(ctype);
1411 rv = 10; /* forces redraw */
1413 else{
1414 q_status_message(SM_ORDER, 3, 3, _("No certificates marked deleted"));
1415 rv = 0;
1417 break;
1419 case MC_IMPORT:
1420 rv = import_certificate(ctype, NULL, NULL);
1421 if(rv < 0){
1422 switch(rv){
1423 default:
1424 case -1:
1425 cmd_cancelled("Import certificate");
1426 break;
1428 case -2:
1429 q_status_message1(SM_ORDER, 0, 2, _("Can't import certificate outside of %s"),
1430 ps_global->VAR_OPER_DIR);
1431 break;
1434 rv = 10; /* forces redraw */
1435 break;
1437 case MC_EXIT:
1438 rv = config_exit_cmd(flags);
1439 break;
1441 default:
1442 rv = -1;
1443 break;
1446 X509_free(cert);
1447 return rv;
1450 void
1451 smime_setup_size(char **s, size_t buflen, size_t n)
1453 char *t = *s;
1454 *t++ = ' ';
1455 *t++ = '%';
1456 *t++ = '-';
1457 snprintf(t, buflen-3, "%zu.%zu", n, n);
1458 t += strlen(t);
1459 *t++ = 's';
1460 *s = t;
1463 #ifdef PASSFILE
1464 void manage_password_file_certificates(struct pine *ps)
1466 OPT_SCREEN_S screen;
1467 int readonly_warning = 0, rv = 10, fline, state = 0;
1469 dprint((9, "manage_password_file_certificates"));
1470 ps->next_screen = SCREEN_FUN_NULL;
1471 ps->keyemptypwd = 0; /* just in case */
1473 do {
1474 CONF_S *ctmp = NULL, *first_line = NULL;
1476 fline = rv >= 10 ? rv - 10 : 0;
1478 smime_manage_password_file_certs_init(ps, &ctmp, &first_line, fline, &state);
1480 if(ctmp == NULL){
1481 ps->mangled_screen = 1;
1482 q_status_message(SM_ORDER, 1, 3, _("Failed to initialize password management screen (no key)"));
1483 return;
1486 memset(&screen, 0, sizeof(screen));
1487 screen.deferred_ro_warning = readonly_warning;
1489 rv = conf_scroll_screen(ps, &screen, first_line,
1490 _("MANAGE PASSWORD FILE CERTS"),
1491 /* TRANSLATORS: Print something1 using something2.
1492 configuration is something1 */
1493 _("configuration"), 0, NULL);
1494 } while (rv != 0);
1496 ps->mangled_screen = 1;
1497 ps->keyemptypwd = 0; /* reset this so it will not confuse other routines */
1498 smime_reinit();
1501 /* state: 0 = first time,
1502 * 1 = second or another time
1504 void
1505 smime_manage_password_file_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, int fline, int *state)
1507 char tmp[200];
1508 char *ext;
1509 CertList *cl;
1510 int i;
1511 void *pwdcert = NULL; /* this is our current password file */
1512 X509_LOOKUP *lookup = NULL;
1513 X509_STORE *store = NULL;
1514 char filename[MAXPATH+1];
1515 BIO *in = NULL;
1516 EVP_PKEY *key = NULL;
1517 PERSONAL_CERT *pc;
1519 if(*state == 0){ /* first time around? */
1520 setup_pwdcert(&pwdcert);
1521 if(pwdcert == NULL) return;
1522 if(ps->pwdcert == NULL)
1523 ps->pwdcert = pwdcert;
1524 else
1525 free_personal_certs((PERSONAL_CERT **) &pwdcert);
1526 (*state)++;
1529 pc = (PERSONAL_CERT *) ps_global->pwdcert;
1530 snprintf(filename, sizeof(filename), "%s/%s.key", ps->pwdcertdir, pc->name);
1531 filename[sizeof(filename)-1] = '\0';
1532 if((in = BIO_new_file(filename, "r")) != NULL
1533 && (key = PEM_read_bio_PrivateKey(in, NULL, NULL, "")) != NULL)
1534 ps->keyemptypwd = 1;
1535 if(in != NULL)
1536 BIO_free(in);
1537 if(key != NULL)
1538 EVP_PKEY_free(key);
1540 ps->pwdcertlist = cl = smime_X509_to_cert_info(X509_dup(pc->cert), pc->name);
1542 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1543 tmp[i] = '-';
1545 new_confline(ctmp);
1546 (*ctmp)->flags |= CF_NOSELECT;
1547 (*ctmp)->value = cpystr(tmp);
1549 new_confline(ctmp);
1550 (*ctmp)->flags |= CF_NOSELECT;
1551 (*ctmp)->value = cpystr(_("Manage Certificates and Keys Used to Encrypt your Password File"));
1553 new_confline(ctmp);
1554 (*ctmp)->flags |= CF_NOSELECT;
1555 (*ctmp)->value = cpystr(tmp);
1557 new_confline(ctmp);
1558 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1560 if(cl){
1561 int s, e, df, dt, md5; /* sizes of certain fields */
1562 int nf; /* number of fields */
1563 char u[MAILTMPLEN], *t;
1565 e = MIN(strlen(cl->name), ps->ttyo->screen_cols/3); /* do not use too much screen */
1566 nf = 5; /* there are 5 fields */
1567 s = 3; /* status has fixed size */
1568 df = dt = 10; /* date from and date to have fixed size */
1569 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1571 t = u;
1572 smime_setup_size(&t, sizeof(u), s);
1573 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1574 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1575 *t++ = ' '; /* leave an extra space between dates */
1576 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1577 *t++ = ' '; /* and another space between date and md5 sum */
1578 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1579 *t = '\0'; /* tie off */
1581 new_confline(ctmp);
1582 (*ctmp)->flags |= CF_NOSELECT;
1583 (*ctmp)->value = cpystr(_("New Public Certificate and Key:"));
1585 new_confline(ctmp);
1586 (*ctmp)->d.s.ctype = Password;
1587 (*ctmp)->help = h_config_smime_password_file_certificates;
1588 (*ctmp)->tool = manage_certs_tool;
1589 (*ctmp)->keymenu = &config_smime_add_new_key_keymenu;
1590 s += 2;
1591 for(i = 0; i < s; i++) tmp[i] = ' ';
1592 tmp[i] = '\0';
1593 strncpy(tmp+s, _("Press \"RETURN\" to add new personal key"), sizeof(tmp)-s-1);
1594 for(i = strlen(tmp); i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp) - 1); i++)
1595 tmp[i] = ' ';
1596 tmp[i] = '\0';
1597 (*ctmp)->value = cpystr(tmp);
1598 *first_line = *ctmp;
1600 new_confline(ctmp);
1601 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1603 new_confline(ctmp);
1604 (*ctmp)->flags |= CF_NOSELECT;
1605 (*ctmp)->value = cpystr(_("Current Public Certificate and Key:"));
1607 new_confline(ctmp);
1608 (*ctmp)->d.s.ctype = Password;
1609 (*ctmp)->d.s.deleted = 0;
1610 (*ctmp)->help = h_config_smime_password_file_certificates;
1611 (*ctmp)->tool = manage_certs_tool;
1612 (*ctmp)->keymenu = ps->keyemptypwd == 0
1613 ? &config_smime_manage_view_cert_keymenu
1614 : &config_smime_manage_view_cert_keymenu_no_delete;
1615 (*ctmp)->varmem = 0;
1616 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1617 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1618 snprintf(tmp, sizeof(tmp), u,
1619 (*ctmp)->d.s.deleted ? "D" : " ",
1620 cl->name,
1621 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1622 (*ctmp)->value = cpystr(tmp);
1625 #endif /* PASSFILE */
1628 void smime_manage_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, WhichCerts ctype, int fline)
1630 char tmp[200];
1631 char *ext;
1632 CertList *data;
1633 int i;
1635 smime_init();
1637 data = DATACERT(ctype);
1638 ext = EXTCERT(ctype);
1640 if(data == NULL || RENEWCERT(data))
1641 renew_cert_data(&data, ctype);
1643 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1644 tmp[i] = '-';
1645 new_confline(ctmp);
1646 (*ctmp)->flags |= CF_NOSELECT;
1647 (*ctmp)->value = cpystr(tmp);
1649 (*ctmp)->keymenu = &config_text_keymenu;
1651 new_confline(ctmp);
1652 (*ctmp)->flags |= CF_NOSELECT;
1653 sprintf(tmp, _("List of %s certificates"), ctype == Public ? _("public")
1654 : (ctype == Private ? _("private")
1655 : (ctype == CACert ? _("certificate authority") : "unknown (?)")));
1656 (*ctmp)->value = cpystr(tmp);
1658 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1659 tmp[i] = '-';
1660 new_confline(ctmp);
1661 (*ctmp)->flags |= CF_NOSELECT;
1662 (*ctmp)->value = cpystr(tmp);
1664 new_confline(ctmp);
1665 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1667 if(data){
1668 CertList *cl; int i;
1669 int s, e, df, dt, md5; /* sizes of certain fields */
1670 int nf; /* number of fields */
1671 char u[MAILTMPLEN], *t;
1673 for(cl = data, e = 0; cl; cl = cl->next)
1674 if(cl->name && strlen(cl->name) > e)
1675 e = strlen(cl->name);
1677 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1678 e -= 4; /* remove extension length */
1679 e = MIN(e, ps->ttyo->screen_cols/3); /* do not use too much screen */
1680 nf = 5; /* there are 5 fields */
1681 s = 3; /* status has fixed size */
1682 df = dt = 10; /* date from and date to have fixed size */
1683 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1685 t = u;
1686 smime_setup_size(&t, sizeof(u), s);
1687 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1688 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1689 *t++ = ' '; /* leave an extra space between dates */
1690 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1691 *t++ = ' '; /* and another space between date and md5 sum */
1692 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1693 *t = '\0'; /* tie off */
1695 for(cl = data, i = 0; cl; cl = cl->next)
1696 if(cl->name){
1697 char *s, *t;
1699 new_confline(ctmp);
1700 (*ctmp)->d.s.ctype = ctype;
1701 (*ctmp)->d.s.deleted = get_cert_deleted(ctype, i);
1702 (*ctmp)->tool = manage_certs_tool;
1703 (*ctmp)->keymenu = &config_smime_manage_certs_work_keymenu;
1704 (*ctmp)->varmem = i++;
1705 (*ctmp)->help = ctype == Public ? h_config_smime_manage_public_menu
1706 : (ctype == Private ? h_config_smime_manage_private_menu
1707 : h_config_smime_manage_cacerts_menu);
1708 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1709 cl->name[strlen(cl->name) - 4] = '\0'; /* FIX FIX FIX */
1710 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1711 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1712 snprintf(tmp, sizeof(tmp), u,
1713 (*ctmp)->d.s.deleted ? "D" : " ",
1714 ctype == CACert ? cl->cn : cl->name,
1715 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1716 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1717 cl->name[strlen(cl->name)] = '.';
1718 (*ctmp)->value = cpystr(tmp);
1719 if(i == fline+1 && first_line && !*first_line)
1720 *first_line = *ctmp;
1723 else {
1724 new_confline(ctmp);
1725 (*ctmp)->d.s.ctype = ctype;
1726 (*ctmp)->tool = manage_certs_tool;
1727 (*ctmp)->keymenu = &config_smime_add_certs_keymenu;
1728 (*ctmp)->value = cpystr(_(" \tNo certificates found, press \"RETURN\" to add one."));
1729 if(first_line && !*first_line)
1730 *first_line = *ctmp;
1734 void manage_certificates(struct pine *ps, WhichCerts ctype)
1736 OPT_SCREEN_S screen;
1737 int readonly_warning = 0, rv = 10, fline;
1739 dprint((9, "manage_certificates(ps, %s)", ctype == Public ? _("Public") : (ctype == Private ? _("Private") : (ctype == CACert ? _("certificate authority") : _("unknown")))));
1740 ps->next_screen = SCREEN_FUN_NULL;
1742 do {
1743 CONF_S *ctmp = NULL, *first_line = NULL;
1745 fline = rv >= 10 ? rv - 10 : 0;
1747 smime_init();
1749 smime_manage_certs_init(ps, &ctmp, &first_line, ctype, fline);
1751 if(ctmp == NULL){
1752 ps->mangled_screen = 1;
1753 smime_reinit();
1754 return;
1757 memset(&screen, 0, sizeof(screen));
1758 screen.deferred_ro_warning = readonly_warning;
1759 rv = conf_scroll_screen(ps, &screen, first_line,
1760 _("MANAGE CERTIFICATES"),
1761 /* TRANSLATORS: Print something1 using something2.
1762 configuration is something1 */
1763 _("configuration"), 0, NULL);
1764 } while (rv != 0);
1766 ps->mangled_screen = 1;
1767 smime_reinit();
1771 smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1773 int rv = 0;
1775 switch(cmd){
1776 case MC_CHOICE:
1777 switch((*cl)->varmem){
1778 case 1:
1779 rv = copy_publiccert_dir_to_container();
1780 if(rv == 0)
1781 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1782 else{
1783 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1784 rv = 0;
1787 break;
1789 case 2:
1790 rv = copy_publiccert_container_to_dir();
1791 if(rv == 0)
1792 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to directory, delete Container config to use"));
1793 else{
1794 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1795 rv = 0;
1798 break;
1800 case 3:
1801 rv = copy_privatecert_dir_to_container();
1802 if(rv == 0)
1803 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to container"));
1804 else{
1805 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1806 rv = 0;
1809 break;
1811 case 4:
1812 rv = copy_privatecert_container_to_dir();
1813 if(rv == 0)
1814 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to directory, delete Container config to use"));
1815 else{
1816 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1817 rv = 0;
1820 break;
1822 case 5:
1823 rv = copy_cacert_dir_to_container();
1824 if(rv == 0)
1825 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to container"));
1826 else{
1827 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1828 rv = 0;
1831 break;
1833 case 6:
1834 rv = copy_cacert_container_to_dir();
1835 if(rv == 0)
1836 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to directory, delete Container config to use"));
1837 else{
1838 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1839 rv = 0;
1842 break;
1844 #ifdef APPLEKEYCHAIN
1845 case 7:
1846 rv = copy_publiccert_container_to_keychain();
1847 if(rv == 0)
1848 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to keychain"));
1849 else{
1850 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1851 rv = 0;
1854 break;
1856 case 8:
1857 rv = copy_publiccert_keychain_to_container();
1858 if(rv == 0)
1859 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1860 else{
1861 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1862 rv = 0;
1865 break;
1866 #endif /* APPLEKEYCHAIN */
1868 case 9: manage_certificates(ps, Public) ; break;
1869 case 10: manage_certificates(ps, Private); break;
1870 case 11: manage_certificates(ps, CACert) ; break;
1872 #ifdef PASSFILE
1873 case 12: manage_password_file_certificates(ps); break;
1874 #endif /* PASSFILE */
1876 default:
1877 rv = -1;
1878 break;
1881 break;
1883 case MC_EXIT:
1884 rv = config_exit_cmd(flags);
1885 break;
1887 case MC_IMPORT:
1888 rv = import_certificate((*cl)->d.s.ctype, NULL, NULL);
1889 break;
1891 default:
1892 rv = -1;
1893 break;
1896 return rv;
1901 * Compare saved user_val with current user_val to see if it changed.
1902 * If any have changed, change it back and take the appropriate action.
1904 void
1905 revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave)
1907 struct variable *vreal;
1908 SAVED_CONFIG_S *v;
1909 int i, n;
1910 int changed = 0;
1911 char *pval, **apval, **lval, ***alval;
1913 v = vsave;
1914 for(vreal = ps->vars; vreal->name; vreal++,v++){
1915 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
1916 continue;
1918 if(vreal->is_list){
1919 lval = LVAL(vreal, ew);
1920 alval = ALVAL(vreal, ew);
1922 if((v->saved_user_val.l && !lval)
1923 || (!v->saved_user_val.l && lval))
1924 changed++;
1925 else if(!v->saved_user_val.l && !lval)
1926 ;/* no change, nothing to do */
1927 else
1928 for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
1929 if((v->saved_user_val.l[i]
1930 && (!lval[i]
1931 || strcmp(v->saved_user_val.l[i], lval[i])))
1933 (!v->saved_user_val.l[i] && lval[i])){
1934 changed++;
1935 break;
1938 if(changed){
1939 char **list;
1941 if(alval){
1942 if(*alval)
1943 free_list_array(alval);
1945 /* copy back the original one */
1946 if(v->saved_user_val.l){
1947 list = v->saved_user_val.l;
1948 n = 0;
1949 /* count how many */
1950 while(list[n])
1951 n++;
1953 *alval = (char **)fs_get((n+1) * sizeof(char *));
1955 for(i = 0; i < n; i++)
1956 (*alval)[i] = cpystr(v->saved_user_val.l[i]);
1958 (*alval)[n] = NULL;
1963 else{
1964 pval = PVAL(vreal, ew);
1965 apval = APVAL(vreal, ew);
1967 if((v->saved_user_val.p &&
1968 (!pval || strcmp(v->saved_user_val.p, pval))) ||
1969 (!v->saved_user_val.p && pval)){
1970 /* It changed, fix it */
1971 changed++;
1972 if(apval){
1973 /* free the changed value */
1974 if(*apval)
1975 fs_give((void **)apval);
1977 if(v->saved_user_val.p)
1978 *apval = cpystr(v->saved_user_val.p);
1983 if(changed){
1984 if(vreal == &ps->vars[V_FEATURE_LIST])
1985 set_feature_list_current_val(vreal);
1986 else
1987 set_current_val(vreal, TRUE, FALSE);
1989 fix_side_effects(ps, vreal, 1);
1995 SAVED_CONFIG_S *
1996 save_smime_config_vars(struct pine *ps)
1998 struct variable *vreal;
1999 SAVED_CONFIG_S *vsave, *v;
2001 vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
2002 memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
2003 v = vsave;
2004 for(vreal = ps->vars; vreal->name; vreal++,v++){
2005 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
2006 continue;
2008 if(vreal->is_list){
2009 int n, i;
2010 char **list;
2012 if(LVAL(vreal, ew)){
2013 /* count how many */
2014 n = 0;
2015 list = LVAL(vreal, ew);
2016 while(list[n])
2017 n++;
2019 v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *));
2020 memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
2021 for(i = 0; i < n; i++)
2022 v->saved_user_val.l[i] = cpystr(list[i]);
2024 v->saved_user_val.l[n] = NULL;
2027 else{
2028 if(PVAL(vreal, ew))
2029 v->saved_user_val.p = cpystr(PVAL(vreal, ew));
2033 return(vsave);
2037 void
2038 free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep)
2040 struct variable *vreal;
2041 SAVED_CONFIG_S *v;
2043 if(vsavep && *vsavep){
2044 for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
2045 if(!(smime_related_var(ps, vreal)))
2046 continue;
2048 if(vreal->is_list){ /* free saved_user_val.l */
2049 if(v && v->saved_user_val.l)
2050 free_list_array(&v->saved_user_val.l);
2052 else if(v && v->saved_user_val.p)
2053 fs_give((void **)&v->saved_user_val.p);
2056 fs_give((void **)vsavep);
2060 #endif /* SMIME */