* Rewrite support for specific SSL encryption protocols, including
[alpine.git] / alpine / smime.c
blobdd73ba03a598dae892062ffd69b6b6156e917bd8
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-2018 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 "../pith/body.h"
40 #include "radio.h"
41 #include "keymenu.h"
42 #include "mailcmd.h"
43 #include "mailview.h"
44 #include "conftype.h"
45 #include "confscroll.h"
46 #include "setup.h"
47 #include "smime.h"
49 /* internal prototypes */
50 void format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc);
51 void print_separator_line(int percent, int ch, gf_io_t pc);
52 void output_cert_info(X509 *cert, gf_io_t pc);
53 void output_X509_NAME(X509_NAME *name, gf_io_t pc);
54 void side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc);
55 STORE_S *wrap_store(STORE_S *in, int width);
56 void smime_config_init_display(struct pine *, CONF_S **, CONF_S **);
57 void revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave);
58 SAVED_CONFIG_S *save_smime_config_vars(struct pine *ps);
59 void free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep);
60 int smime_helper_tool(struct pine *, int, CONF_S **, unsigned);
61 void manage_certificates(struct pine *, WhichCerts);
62 #ifdef PASSFILE
63 void manage_password_file_certificates(struct pine *);
64 #endif /* PASSFILE */
65 void smime_manage_certs_init (struct pine *, CONF_S **, CONF_S **, WhichCerts, int);
66 void smime_manage_password_file_certs_init(struct pine *, CONF_S **, CONF_S **, int, int *);
67 void display_certificate_information(struct pine *, X509 *, char *, WhichCerts, int num);
68 int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags);
69 int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *);
70 void smime_setup_size(char **, size_t, size_t);
74 * prompt the user for their passphrase
75 * (possibly prompting with the email address in s_passphrase_emailaddr)
77 int
78 smime_get_passphrase(void)
80 int rc;
81 int flags;
82 char prompt[500];
83 HelpType help = NO_HELP;
85 assert(ps_global->smime != NULL);
86 snprintf(prompt, sizeof(prompt),
87 _("Enter passphrase for <%s>: "), (ps_global->smime && ps_global->smime->passphrase_emailaddr) ? ps_global->smime->passphrase_emailaddr[0] : "unknown");
89 do {
90 flags = OE_PASSWD | OE_DISALLOW_HELP;
91 ((char *) ps_global->smime->passphrase)[0] = '\0';
92 rc = optionally_enter((char *) ps_global->smime->passphrase,
93 -FOOTER_ROWS(ps_global), 0,
94 sizeof(ps_global->smime->passphrase),
95 prompt, NULL, help, &flags);
96 } while (rc!=0 && rc!=1 && rc>0);
98 if(rc==0){
99 if(ps_global->smime)
100 ps_global->smime->entered_passphrase = 1;
103 return rc; /* better return rc and make the caller check its return value */
107 smime_check(BODY *body)
109 int rv = 0;
110 PKCS7 *p7 = NULL;
112 if(body->type == TYPEMULTIPART){
113 PART *p;
115 for(p=body->nested.part; p && rv == 0; p=p->next)
116 rv += smime_check(&p->body);
118 if(rv > 0) return rv;
119 if(body->sparep)
120 p7 = get_body_sparep_type(body->sparep) == P7Type
121 ? (PKCS7 *)get_body_sparep_data(body->sparep)
122 : NULL;
123 if(p7 && (PKCS7_type_is_signed(p7) || PKCS7_type_is_enveloped(p7)))
124 rv += 1;
125 return rv;
129 void
130 display_smime_info(struct pine *ps, ENVELOPE *env, BODY *body)
132 OtherMenu what = FirstMenu;
133 HANDLE_S *handles = NULL;
134 SCROLL_S scrollargs;
135 STORE_S *store = NULL;
136 long msgno;
137 int offset = 0;
139 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
140 store = so_get(CharStar, NULL, EDIT_ACCESS);
142 while(ps->next_screen == SCREEN_FUN_NULL){
144 ClearLine(1);
146 so_truncate(store, 0);
148 view_writec_init(store, &handles, HEADER_ROWS(ps),
149 HEADER_ROWS(ps) +
150 ps->ttyo->screen_rows - (HEADER_ROWS(ps)
151 + HEADER_ROWS(ps)));
153 gf_puts_uline("Overview", view_writec);
154 gf_puts(NEWLINE, view_writec);
156 format_smime_info(1, body, msgno, view_writec);
157 gf_puts(NEWLINE, view_writec);
158 format_smime_info(2, body, msgno, view_writec);
160 view_writec_destroy();
162 ps->next_screen = SCREEN_FUN_NULL;
164 memset(&scrollargs, 0, sizeof(SCROLL_S));
165 scrollargs.text.text = so_text(store);
166 scrollargs.text.src = CharStar;
167 scrollargs.text.desc = "S/MIME information";
168 scrollargs.body_valid = 1;
170 if(offset){ /* resize? preserve paging! */
171 scrollargs.start.on = Offset;
172 scrollargs.start.loc.offset = offset;
173 offset = 0L;
176 scrollargs.bar.title = "S/MIME INFORMATION";
177 /* scrollargs.end_scroll = view_end_scroll; */
178 scrollargs.resize_exit = 1;
179 scrollargs.help.text = NULL;
180 scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
181 scrollargs.keys.menu = &smime_info_keymenu;
182 scrollargs.keys.what = what;
183 setbitmap(scrollargs.keys.bitmap);
185 if(scrolltool(&scrollargs) == MC_RESIZE)
186 offset = scrollargs.start.loc.offset;
189 so_give(&store);
192 void
193 smime_info_screen(struct pine *ps)
195 long msgno;
196 BODY *body;
197 ENVELOPE *env;
199 /* ps->prev_screen = smime_info_screen;
200 ps->next_screen = SCREEN_FUN_NULL; */
202 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
204 env = mail_fetch_structure(ps->mail_stream, msgno, &body, 0);
206 if(!env || !body){
207 q_status_message(SM_ORDER, 0, 3,
208 _("Can't fetch body of message."));
209 return;
212 if(smime_check(body) == 0){
213 q_status_message(SM_ORDER | SM_DING, 0, 3,
214 _("Not a signed or encrypted message"));
215 return;
218 if(mn_total_cur(ps->msgmap) > 1L){
219 q_status_message(SM_ORDER | SM_DING, 0, 3,
220 _("Can only view one message's information at a time."));
221 return;
224 display_smime_info(ps, env, body);
228 void
229 format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc)
231 PKCS7 *p7 = NULL;
232 int i;
234 if(body->type == TYPEMULTIPART){
235 PART *p;
237 for(p=body->nested.part; p; p=p->next)
238 format_smime_info(pass, &p->body, msgno, pc);
240 if(body->sparep)
241 p7 = get_body_sparep_type(body->sparep) == P7Type
242 ? (PKCS7 *)get_body_sparep_data(body->sparep)
243 : NULL;
244 if(p7){
246 if(PKCS7_type_is_signed(p7)){
247 STACK_OF(X509) *signers;
249 switch(pass){
250 case 1:
251 gf_puts(_("This message was cryptographically signed."), pc);
252 gf_puts(NEWLINE, pc);
253 break;
255 case 2:
256 signers = PKCS7_get0_signers(p7, NULL, 0);
258 if(signers){
260 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s used for signing"),
261 plural(sk_X509_num(signers)));
262 gf_puts_uline(tmp_20k_buf, pc);
263 gf_puts(NEWLINE, pc);
264 print_separator_line(100, '-', pc);
266 for(i=0; i<sk_X509_num(signers); i++){
267 X509 *x = sk_X509_value(signers, i);
269 if(x){
270 output_cert_info(x, pc);
271 gf_puts(NEWLINE, pc);
276 sk_X509_free(signers);
277 break;
281 else if(PKCS7_type_is_enveloped(p7)){
283 switch(pass){
284 case 1:
285 gf_puts(_("This message was encrypted."), pc);
286 gf_puts(NEWLINE, pc);
287 break;
289 case 2:
290 if(p7->d.enveloped && p7->d.enveloped->enc_data){
291 X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
292 STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
293 int found = 0;
295 gf_puts(_("The algorithm used to encrypt was "), pc);
297 if(alg){
298 char *n = (char *) OBJ_nid2sn( OBJ_obj2nid(alg->algorithm));
300 gf_puts(n ? n : "<unknown>", pc);
303 else
304 gf_puts("<unknown>", pc);
306 gf_puts("." NEWLINE NEWLINE, pc);
308 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s for decrypting"),
309 plural(sk_PKCS7_RECIP_INFO_num(ris)));
310 gf_puts_uline(tmp_20k_buf, pc);
311 gf_puts(NEWLINE, pc);
312 print_separator_line(100, '-', pc);
314 for(i=0; i<sk_PKCS7_RECIP_INFO_num(ris); i++){
315 PKCS7_RECIP_INFO *ri;
316 PERSONAL_CERT *pcert;
318 ri = sk_PKCS7_RECIP_INFO_value(ris, i);
319 if(!ri)
320 continue;
322 pcert = find_certificate_matching_recip_info(ri);
324 if(pcert){
325 if(found){
326 print_separator_line(25, '*', pc);
327 gf_puts(NEWLINE, pc);
330 found = 1;
332 output_cert_info(pcert->cert, pc);
333 gf_puts(NEWLINE, pc);
338 if(!found){
339 gf_puts(_("No certificate capable of decrypting could be found."), pc);
340 gf_puts(NEWLINE, pc);
341 gf_puts(NEWLINE, pc);
345 break;
352 void
353 print_separator_line(int percent, int ch, gf_io_t pc)
355 int i, start, len;
357 len = ps_global->ttyo->screen_cols * percent / 100;
358 start = (ps_global->ttyo->screen_cols - len)/2;
360 for(i=0; i<start; i++)
361 pc(' ');
363 for(i=start; i<start+len; i++)
364 pc(ch);
366 gf_puts(NEWLINE, pc);
370 void
371 output_cert_info(X509 *cert, gf_io_t pc)
373 char buf[256];
374 STORE_S *left,*right;
375 gf_io_t spc;
376 int len, error;
377 STACK_OF(X509) *chain;
379 left = so_get(CharStar, NULL, EDIT_ACCESS);
380 right = so_get(CharStar, NULL, EDIT_ACCESS);
381 if(!(left && right))
382 return;
384 gf_set_so_writec(&spc, left);
386 gf_puts_uline("Certificate Owner", spc);
387 gf_puts(NEWLINE, spc);
389 output_X509_NAME(X509_get_subject_name(cert), spc);
390 gf_puts(NEWLINE, spc);
392 gf_puts_uline("Serial Number", spc);
393 gf_puts(NEWLINE, spc);
395 { ASN1_INTEGER *bs;
396 long l;
397 const char *neg;
398 int i;
400 bs = X509_get_serialNumber(cert);
401 if (bs->length <= (int)sizeof(long)){
402 l = ASN1_INTEGER_get(bs);
403 if (bs->type == V_ASN1_NEG_INTEGER){
404 l = -l;
405 neg="-";
407 else
408 neg="";
409 snprintf(buf, sizeof(buf), " %s%lu (%s0x%lx)", neg, l, neg, l);
410 } else {
411 snprintf(buf, sizeof(buf), "%s", bs->type == V_ASN1_NEG_INTEGER ? "(Negative)" : "");
412 for (i = 0; i < bs->length; i++)
413 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%02x%s", bs->data[i],
414 i+1 == bs->length ? "" : ":");
417 gf_puts(buf, spc);
418 gf_puts(NEWLINE, spc);
419 gf_puts(NEWLINE, spc);
421 gf_puts_uline("Validity", spc);
422 gf_puts(NEWLINE, spc);
423 { BIO *mb = BIO_new(BIO_s_mem());
424 char iobuf[4096];
426 gf_puts("Not Before: ", spc);
428 (void) BIO_reset(mb);
429 ASN1_UTCTIME_print(mb, X509_get0_notBefore(cert));
430 (void) BIO_flush(mb);
431 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
432 gf_nputs(iobuf, len, spc);
434 gf_puts(NEWLINE, spc);
436 gf_puts("Not After: ", spc);
438 (void) BIO_reset(mb);
439 ASN1_UTCTIME_print(mb, X509_get0_notAfter(cert));
440 (void) BIO_flush(mb);
441 while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
442 gf_nputs(iobuf, len, spc);
444 gf_puts(NEWLINE, spc);
445 gf_puts(NEWLINE, spc);
447 BIO_free(mb);
450 gf_clear_so_writec(left);
452 gf_set_so_writec(&spc, right);
454 gf_puts_uline("Issuer", spc);
455 gf_puts(NEWLINE, spc);
457 output_X509_NAME(X509_get_issuer_name(cert), spc);
458 gf_puts(NEWLINE, spc);
460 gf_clear_so_writec(right);
462 side_by_side(left, right, pc);
464 gf_puts_uline("SHA1 Fingerprint", pc);
465 gf_puts(NEWLINE, pc);
466 get_fingerprint(cert, EVP_sha1(), buf, sizeof(buf), ":");
467 gf_puts(buf, pc);
468 gf_puts(NEWLINE, pc);
470 gf_puts_uline("MD5 Fingerprint", pc);
471 gf_puts(NEWLINE, pc);
472 get_fingerprint(cert, EVP_md5(), buf, sizeof(buf), ":");
473 gf_puts(buf, pc);
474 gf_puts(NEWLINE, pc);
475 gf_puts(NEWLINE, pc);
477 gf_puts_uline("Certificate Chain Information", pc);
478 gf_puts(NEWLINE, pc);
480 if((chain = get_chain_for_cert(cert, &error, &len)) != NULL){
481 X509 *x;
482 X509_NAME_ENTRY *e;
483 int i, offset = 2;
484 char space[256];
485 X509_NAME *subject;
487 for(i = 0; i < offset; i++) space[i] = ' ';
489 for(i = -1; i < sk_X509_num(chain); i++){
490 char buf[256];
492 x = i == -1 ? cert : sk_X509_value(chain, i);
494 if(x){
495 if(i>=0){
496 space[offset + i + 0] = ' ';
497 space[offset + i + 1] = '\\';
498 space[offset + i + 2] = '-';
499 space[offset + i + 3] = ' ';
500 space[offset + i + 4] = '\0';
501 gf_puts(space, pc);
503 else{
504 space[offset] = '\0';
505 gf_puts(space, pc);
507 if(i >= 0)
508 gf_puts_uline("Signed by: ", pc);
509 else
510 gf_puts_uline("Issued to: ", pc);
512 subject = X509_get_subject_name(x);
514 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
515 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
516 gf_puts(buf, pc);
517 gf_puts(NEWLINE, pc);
520 else{
521 gf_puts("No certificate info found", pc);
522 gf_puts(NEWLINE, pc);
523 break;
526 e = X509_NAME_get_entry(X509_get_issuer_name(x),
527 X509_NAME_entry_count(X509_get_issuer_name(x))-1);
528 if(e){
529 X509_NAME_get_text_by_OBJ(X509_get_issuer_name(x), X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
530 space[offset + i + 0] = ' ';
531 space[offset + i + 1] = '\\';
532 space[offset + i + 2] = '-';
533 space[offset + i + 3] = ' ';
534 space[offset + i + 4] = '\0';
535 gf_puts(space, pc);
536 gf_puts_uline("Signed by: ", pc);
537 gf_puts(buf, pc);
538 gf_puts(NEWLINE, pc);
540 sk_X509_pop_free(chain, X509_free);
542 gf_puts(NEWLINE, pc);
544 so_give(&left);
545 so_give(&right);
549 void
550 output_X509_NAME(X509_NAME *name, gf_io_t pc)
552 int i, c;
553 char buf[256];
555 c = X509_NAME_entry_count(name);
557 for(i=c-1; i>=0; i--){
558 X509_NAME_ENTRY *e;
560 e = X509_NAME_get_entry(name,i);
561 if(!e)
562 continue;
564 X509_NAME_get_text_by_OBJ(name, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
566 gf_puts(buf, pc);
567 gf_puts(NEWLINE, pc);
573 * Output the contents of the given stores (left and right)
574 * to the given gf_io_t.
575 * The width of the terminal is inspected and two columns
576 * are created to fit the stores into. They are then wrapped
577 * and merged.
579 void
580 side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
582 STORE_S *left_wrapped;
583 STORE_S *right_wrapped;
584 char buf_l[256];
585 char buf_r[256];
586 char *l, *r;
587 char *b;
588 int i;
589 int w = ps_global->ttyo->screen_cols/2 - 1;
591 so_seek(left, 0, 0);
592 so_seek(right, 0, 0);
594 left_wrapped = wrap_store(left, w);
595 right_wrapped = wrap_store(right, w);
597 so_seek(left_wrapped, 0, 0);
598 so_seek(right_wrapped, 0, 0);
600 for(;;){
602 l = so_fgets(left_wrapped, buf_l, sizeof(buf_l));
603 r = so_fgets(right_wrapped, buf_r, sizeof(buf_r));
604 if(l == NULL && r == NULL)
605 break;
607 for(i=0, b=buf_l; i<w && *b && *b!='\r' && *b!='\n'; i++,b++){
608 pc(*b);
609 /* reduce accumulated width if an embed tag is discovered */
610 if(*b==TAG_EMBED)
611 i-=2;
614 if(buf_r[0]){
615 while(i<w){
616 pc(' ');
617 i++;
619 pc(' ');
621 for(i=0, b=buf_r; i<w && *b && *b!='\r' && *b!='\n'; i++,b++)
622 pc(*b);
625 gf_puts(NEWLINE, pc);
628 so_give(&left_wrapped);
629 so_give(&right_wrapped);
633 * Wrap the text in the given store to the given width.
634 * A new store is created for the result.
636 STORE_S *
637 wrap_store(STORE_S *in, int width)
639 STORE_S *result;
640 void *ws;
641 gf_io_t ipc,opc;
643 if(width<10)
644 width = 10;
646 result = so_get(CharStar, NULL, EDIT_ACCESS);
647 ws = gf_wrap_filter_opt(width, width, NULL, 0, 0);
649 gf_filter_init();
650 gf_link_filter(gf_wrap, ws);
652 gf_set_so_writec(&opc, result);
653 gf_set_so_readc(&ipc, in);
655 gf_pipe(ipc, opc);
657 gf_clear_so_readc(in);
658 gf_clear_so_writec(result);
660 return result;
664 void
665 smime_config_screen(struct pine *ps, int edit_exceptions)
667 CONF_S *ctmp = NULL, *first_line = NULL;
668 SAVED_CONFIG_S *vsave;
669 OPT_SCREEN_S screen;
670 int ew, readonly_warning = 0;
672 dprint((9, "smime_config_screen()"));
673 ps->next_screen = SCREEN_FUN_NULL;
676 * this is necessary because we need to know the correct paths
677 * to configure certificates and keys, and we could get here
678 * without having done that before we reach this place.
680 smime_reinit();
682 if(ps->fix_fixed_warning)
683 offer_to_fix_pinerc(ps);
685 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
687 if(ps->restricted)
688 readonly_warning = 1;
689 else{
690 PINERC_S *prc = NULL;
692 switch(ew){
693 case Main:
694 prc = ps->prc;
695 break;
696 case Post:
697 prc = ps->post_prc;
698 break;
699 default:
700 break;
703 readonly_warning = prc ? prc->readonly : 1;
704 if(prc && prc->quit_to_edit){
705 quit_to_edit_msg(prc);
706 return;
710 smime_config_init_display(ps, &ctmp, &first_line);
712 vsave = save_smime_config_vars(ps);
714 memset(&screen, 0, sizeof(screen));
715 screen.deferred_ro_warning = readonly_warning;
716 switch(conf_scroll_screen(ps, &screen, first_line,
717 edit_exceptions ? _("SETUP S/MIME EXCEPTIONS")
718 : _("SETUP S/MIME"),
719 /* TRANSLATORS: Print something1 using something2.
720 configuration is something1 */
721 _("configuration"), 0, NULL)){
722 case 0:
723 break;
725 case 1:
726 write_pinerc(ps, ew, WRP_NONE);
727 break;
729 case 10:
730 revert_to_saved_smime_config(ps, vsave);
731 break;
733 default:
734 q_status_message(SM_ORDER, 7, 10,
735 _("conf_scroll_screen bad ret in smime_config"));
736 break;
739 free_saved_smime_config(ps, &vsave);
740 smime_reinit();
745 smime_related_var(struct pine *ps, struct variable *var)
747 return(var == &ps->vars[V_PUBLICCERT_DIR] ||
748 var == &ps->vars[V_PUBLICCERT_CONTAINER] ||
749 var == &ps->vars[V_PRIVATEKEY_DIR] ||
750 var == &ps->vars[V_PRIVATEKEY_CONTAINER] ||
751 var == &ps->vars[V_CACERT_DIR] ||
752 var == &ps->vars[V_CACERT_CONTAINER]);
755 void
756 smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
758 char tmp[200];
759 int i, ind, ln = 0;
760 struct variable *vtmp;
761 CONF_S *ctmpb;
762 FEATURE_S *feature;
764 /* find longest variable name */
765 for(vtmp = ps->vars; vtmp->name; vtmp++){
766 if(!(smime_related_var(ps, vtmp)))
767 continue;
769 if((i = utf8_width(pretty_var_name(vtmp->name))) > ln)
770 ln = i;
773 for(vtmp = ps->vars; vtmp->name; vtmp++){
774 if(!(smime_related_var(ps, vtmp)))
775 continue;
777 new_confline(ctmp)->var = vtmp;
778 if(first_line && !*first_line)
779 *first_line = *ctmp;
781 (*ctmp)->valoffset = ln+3;
782 (*ctmp)->keymenu = &config_text_keymenu;
783 (*ctmp)->help = config_help(vtmp - ps->vars, 0);
784 (*ctmp)->tool = text_tool;
786 utf8_snprintf(tmp, sizeof(tmp), "%-*.100w =", ln, pretty_var_name(vtmp->name));
787 tmp[sizeof(tmp)-1] = '\0';
789 (*ctmp)->varname = cpystr(tmp);
790 (*ctmp)->varnamep = (*ctmp);
791 (*ctmp)->flags = CF_STARTITEM;
792 (*ctmp)->value = pretty_value(ps, *ctmp);
796 vtmp = &ps->vars[V_FEATURE_LIST];
798 new_confline(ctmp);
799 ctmpb = (*ctmp);
800 (*ctmp)->flags |= CF_NOSELECT | CF_STARTITEM;
801 (*ctmp)->keymenu = &config_checkbox_keymenu;
802 (*ctmp)->tool = NULL;
804 /* put a nice delimiter before list */
805 new_confline(ctmp)->var = NULL;
806 (*ctmp)->varnamep = ctmpb;
807 (*ctmp)->keymenu = &config_checkbox_keymenu;
808 (*ctmp)->help = NO_HELP;
809 (*ctmp)->tool = checkbox_tool;
810 (*ctmp)->valoffset = feature_indent();
811 (*ctmp)->flags |= CF_NOSELECT;
812 (*ctmp)->value = cpystr("Set Feature Name");
814 new_confline(ctmp)->var = NULL;
815 (*ctmp)->varnamep = ctmpb;
816 (*ctmp)->keymenu = &config_checkbox_keymenu;
817 (*ctmp)->help = NO_HELP;
818 (*ctmp)->tool = checkbox_tool;
819 (*ctmp)->valoffset = feature_indent();
820 (*ctmp)->flags |= CF_NOSELECT;
821 (*ctmp)->value = cpystr("--- ----------------------");
823 ind = feature_list_index(F_DONT_DO_SMIME);
824 feature = feature_list(ind);
825 new_confline(ctmp)->var = vtmp;
826 (*ctmp)->varnamep = ctmpb;
827 (*ctmp)->keymenu = &config_checkbox_keymenu;
828 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
829 (*ctmp)->tool = checkbox_tool;
830 (*ctmp)->valoffset = feature_indent();
831 (*ctmp)->varmem = ind;
832 (*ctmp)->value = pretty_value(ps, (*ctmp));
834 ind = feature_list_index(F_ENCRYPT_DEFAULT_ON);
835 feature = feature_list(ind);
836 new_confline(ctmp)->var = vtmp;
837 (*ctmp)->varnamep = ctmpb;
838 (*ctmp)->keymenu = &config_checkbox_keymenu;
839 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
840 (*ctmp)->tool = checkbox_tool;
841 (*ctmp)->valoffset = feature_indent();
842 (*ctmp)->varmem = ind;
843 (*ctmp)->value = pretty_value(ps, (*ctmp));
845 ind = feature_list_index(F_REMEMBER_SMIME_PASSPHRASE);
846 feature = feature_list(ind);
847 new_confline(ctmp)->var = vtmp;
848 (*ctmp)->varnamep = ctmpb;
849 (*ctmp)->keymenu = &config_checkbox_keymenu;
850 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
851 (*ctmp)->tool = checkbox_tool;
852 (*ctmp)->valoffset = feature_indent();
853 (*ctmp)->varmem = ind;
854 (*ctmp)->value = pretty_value(ps, (*ctmp));
856 ind = feature_list_index(F_SIGN_DEFAULT_ON);
857 feature = feature_list(ind);
858 new_confline(ctmp)->var = vtmp;
859 (*ctmp)->varnamep = ctmpb;
860 (*ctmp)->keymenu = &config_checkbox_keymenu;
861 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
862 (*ctmp)->tool = checkbox_tool;
863 (*ctmp)->valoffset = feature_indent();
864 (*ctmp)->varmem = ind;
865 (*ctmp)->value = pretty_value(ps, (*ctmp));
867 ind = feature_list_index(F_USE_CERT_STORE_ONLY);
868 feature = feature_list(ind);
869 new_confline(ctmp)->var = vtmp;
870 (*ctmp)->varnamep = ctmpb;
871 (*ctmp)->keymenu = &config_checkbox_keymenu;
872 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
873 (*ctmp)->tool = checkbox_tool;
874 (*ctmp)->valoffset = feature_indent();
875 (*ctmp)->varmem = ind;
876 (*ctmp)->value = pretty_value(ps, (*ctmp));
878 #ifdef APPLEKEYCHAIN
879 new_confline(ctmp);
880 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
882 new_confline(ctmp);
883 (*ctmp)->flags |= CF_NOSELECT;
884 (*ctmp)->value = cpystr(_("Mac OS X specific features"));
886 ind = feature_list_index(F_PUBLICCERTS_IN_KEYCHAIN);
887 feature = feature_list(ind);
888 new_confline(ctmp)->var = vtmp;
889 (*ctmp)->varnamep = ctmpb;
890 (*ctmp)->keymenu = &config_checkbox_keymenu;
891 (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
892 (*ctmp)->tool = checkbox_tool;
893 (*ctmp)->valoffset = feature_indent();
894 (*ctmp)->varmem = ind;
895 (*ctmp)->value = pretty_value(ps, (*ctmp));
896 #endif /* APPLEKEYCHAIN */
898 new_confline(ctmp);
899 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
901 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
902 tmp[i] = '-';
903 tmp[i] = '\0';
904 new_confline(ctmp);
905 (*ctmp)->flags |= CF_NOSELECT;
906 (*ctmp)->value = cpystr(tmp);
908 new_confline(ctmp);
909 (*ctmp)->flags |= CF_NOSELECT;
910 (*ctmp)->value = cpystr(_("Be careful with the following commands, they REPLACE contents in the target"));
912 new_confline(ctmp);
913 (*ctmp)->flags |= CF_NOSELECT;
914 (*ctmp)->value = cpystr(tmp);
916 new_confline(ctmp);
917 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
919 /* copy public directory to container */
920 new_confline(ctmp);
921 (*ctmp)->tool = smime_helper_tool;
922 (*ctmp)->keymenu = &config_smime_helper_keymenu;
923 (*ctmp)->help = h_config_smime_transfer_pub_to_con;
924 (*ctmp)->value = cpystr(_("Transfer public certs FROM directory TO container"));
925 (*ctmp)->varmem = 1;
927 /* copy private directory to container */
928 new_confline(ctmp);
929 (*ctmp)->tool = smime_helper_tool;
930 (*ctmp)->keymenu = &config_smime_helper_keymenu;
931 (*ctmp)->help = h_config_smime_transfer_priv_to_con;
932 (*ctmp)->value = cpystr(_("Transfer private keys FROM directory TO container"));
933 (*ctmp)->varmem = 3;
935 /* copy cacert directory to container */
936 new_confline(ctmp);
937 (*ctmp)->tool = smime_helper_tool;
938 (*ctmp)->keymenu = &config_smime_helper_keymenu;
939 (*ctmp)->help = h_config_smime_transfer_cacert_to_con;
940 (*ctmp)->value = cpystr(_("Transfer CA certs FROM directory TO container"));
941 (*ctmp)->varmem = 5;
943 new_confline(ctmp)->var = vtmp;
944 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
946 /* copy public container to directory */
947 new_confline(ctmp);
948 (*ctmp)->tool = smime_helper_tool;
949 (*ctmp)->keymenu = &config_smime_helper_keymenu;
950 (*ctmp)->help = h_config_smime_transfer_pub_to_dir;
951 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO directory"));
952 (*ctmp)->varmem = 2;
954 /* copy private container to directory */
955 new_confline(ctmp);
956 (*ctmp)->tool = smime_helper_tool;
957 (*ctmp)->keymenu = &config_smime_helper_keymenu;
958 (*ctmp)->help = h_config_smime_transfer_priv_to_dir;
959 (*ctmp)->value = cpystr(_("Transfer private keys FROM container TO directory"));
960 (*ctmp)->varmem = 4;
962 /* copy cacert container to directory */
963 new_confline(ctmp);
964 (*ctmp)->tool = smime_helper_tool;
965 (*ctmp)->keymenu = &config_smime_helper_keymenu;
966 (*ctmp)->help = h_config_smime_transfer_cacert_to_dir;
967 (*ctmp)->value = cpystr(_("Transfer CA certs FROM container TO directory"));
968 (*ctmp)->varmem = 6;
970 #ifdef APPLEKEYCHAIN
972 new_confline(ctmp)->var = vtmp;
973 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
975 /* copy public container to keychain */
976 new_confline(ctmp);
977 (*ctmp)->tool = smime_helper_tool;
978 (*ctmp)->keymenu = &config_smime_helper_keymenu;
979 (*ctmp)->help = h_config_smime_transfer_pubcon_to_key;
980 (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO keychain"));
981 (*ctmp)->varmem = 7;
983 /* copy public keychain to container */
984 new_confline(ctmp);
985 (*ctmp)->tool = smime_helper_tool;
986 (*ctmp)->keymenu = &config_smime_helper_keymenu;
987 (*ctmp)->help = h_config_smime_transfer_pubkey_to_con;
988 (*ctmp)->value = cpystr(_("Transfer public certs FROM keychain TO container"));
989 (*ctmp)->varmem = 8;
991 #endif /* APPLEKEYCHAIN */
993 if(ps_global->smime
994 && SMHOLDERTYPE(Private) == Keychain
995 && SMHOLDERTYPE(Public) == Keychain
996 && SMHOLDERTYPE(CACert) == Keychain)
997 return;
999 new_confline(ctmp)->var = vtmp;
1000 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1002 new_confline(ctmp);
1003 (*ctmp)->flags |= CF_NOSELECT;
1004 (*ctmp)->value = cpystr(tmp);
1006 new_confline(ctmp);
1007 (*ctmp)->flags |= CF_NOSELECT;
1008 (*ctmp)->value = cpystr(_("Manage your own certificates"));
1010 new_confline(ctmp);
1011 (*ctmp)->flags |= CF_NOSELECT;
1012 (*ctmp)->value = cpystr(tmp);
1014 new_confline(ctmp)->var = vtmp;
1015 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1017 /* manage public certificates */
1018 new_confline(ctmp);
1019 (*ctmp)->tool = smime_helper_tool;
1020 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1021 (*ctmp)->help = h_config_smime_public_certificates;
1022 (*ctmp)->value = cpystr(_("Manage Public Certificates"));
1023 (*ctmp)->varmem = 9;
1024 (*ctmp)->d.s.ctype = Public;
1026 /* manage private keys */
1027 new_confline(ctmp);
1028 (*ctmp)->tool = smime_helper_tool;
1029 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1030 (*ctmp)->help = h_config_smime_private_keys;
1031 (*ctmp)->value = cpystr(_("Manage Private Keys"));
1032 (*ctmp)->varmem = 10;
1033 (*ctmp)->d.s.ctype = Private;
1035 /* manage Certificate Authorities */
1036 new_confline(ctmp);
1037 (*ctmp)->tool = smime_helper_tool;
1038 (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
1039 (*ctmp)->help = h_config_smime_certificate_authorities;
1040 (*ctmp)->value = cpystr(_("Manage Certificate Authorities"));
1041 (*ctmp)->varmem = 11;
1042 (*ctmp)->d.s.ctype = CACert;
1044 #ifdef PASSFILE
1045 new_confline(ctmp)->var = vtmp;
1046 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1048 new_confline(ctmp);
1049 (*ctmp)->flags |= CF_NOSELECT;
1050 (*ctmp)->value = cpystr(tmp);
1052 new_confline(ctmp);
1053 (*ctmp)->flags |= CF_NOSELECT;
1054 (*ctmp)->value = cpystr(_("Manage Key and Certificate for Password File"));
1056 new_confline(ctmp);
1057 (*ctmp)->flags |= CF_NOSELECT;
1058 (*ctmp)->value = cpystr(tmp);
1060 new_confline(ctmp)->var = vtmp;
1061 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1063 /* manage password file certificates */
1064 new_confline(ctmp);
1065 (*ctmp)->tool = smime_helper_tool;
1066 (*ctmp)->keymenu = &config_smime_manage_password_file_menu_keymenu;
1067 (*ctmp)->help = h_config_smime_password_file_certificates;
1068 (*ctmp)->value = cpystr(_("Manage Password File Key and Certificate"));
1069 (*ctmp)->varmem = 12;
1070 (*ctmp)->d.s.ctype = Password;
1071 #endif /* PASSFILE */
1073 (*ctmp)->next = NULL;
1076 void
1077 display_certificate_information(struct pine *ps, X509 *cert, char *email, WhichCerts ctype, int num)
1079 STORE_S *store;
1080 SCROLL_S scrollargs;
1081 int cmd, offset;
1082 int pub_cert, priv_cert, new_store;
1083 long error;
1084 BIO *out = NULL;
1086 cmd = offset = pub_cert = priv_cert = 0;
1087 new_store = 1;
1088 ps->next_screen = SCREEN_FUN_NULL;
1089 do {
1090 /* MC_PRIVATE and MC_PUBLIC cancel each other,
1091 * they can not be active at the same time
1093 switch(cmd){
1094 case MC_PRIVATE:
1095 pub_cert = 0;
1096 priv_cert = 1 - priv_cert;
1097 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1098 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1099 break;
1101 case MC_PUBLIC:
1102 priv_cert = 0;
1103 pub_cert = 1 - pub_cert;
1104 smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
1105 smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
1106 break;
1108 case MC_TRUST:
1109 if(SMHOLDERTYPE(CACert) == Directory)
1110 save_cert_for(email, cert, CACert);
1111 else{ /* if(SMHOLDERTYPE(CACert) == Container) */
1112 char path[MAXPATH];
1113 char *upath = PATHCERTDIR(ctype);
1114 char *tempfile = tempfile_in_same_dir(path, "az", NULL);
1115 CertList *clist;
1117 if(IS_REMOTE(upath))
1118 strncpy(path, temp_nam(NULL, "a6"), sizeof(path)-1);
1119 else
1120 strncpy(path, upath, sizeof(path)-1);
1121 path[sizeof(path)-1] = '\0';
1123 add_to_end_of_certlist(&ps_global->smime->cacertlist, email, X509_dup(cert));
1124 for(clist=ps_global->smime->cacertlist; clist && clist->next; clist = clist->next);
1125 certlist_to_file(tempfile, clist);
1126 add_file_to_container(CACert, tempfile, email);
1127 unlink(tempfile);
1129 renew_store();
1130 new_store = 1;
1131 break;
1133 case MC_DELETE:
1134 if (get_cert_deleted(ctype, num) != 0)
1135 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1136 else{
1137 mark_cert_deleted(ctype, num, 1);
1138 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1140 break;
1142 case MC_UNDELETE:
1143 if (get_cert_deleted(ctype, num) != 0){
1144 mark_cert_deleted(ctype, num, 0);
1145 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1147 else
1148 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1149 break;
1151 default: break;
1154 if((pub_cert || priv_cert)
1155 && (out = print_private_key_information(email, priv_cert)) == NULL)
1156 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Private Certificate Information"));
1158 if(new_store){
1159 store = so_get(CharStar, NULL, EDIT_ACCESS);
1160 view_writec_init(store, NULL, HEADER_ROWS(ps),
1161 HEADER_ROWS(ps) + ps->ttyo->screen_rows - (HEADER_ROWS(ps)+ FOOTER_ROWS(ps)));
1163 snprintf(tmp_20k_buf, SIZEOF_20KBUF,"%s", _("Certificate Information"));
1164 gf_puts_uline(tmp_20k_buf, view_writec);
1165 gf_puts(NEWLINE, view_writec);
1166 print_separator_line(100, '-', view_writec);
1168 output_cert_info(cert, view_writec);
1169 gf_puts(NEWLINE, view_writec);
1171 if(smime_validate_cert(cert, &error) < 0){
1172 const char *errorp = X509_verify_cert_error_string(error);
1173 snprintf(tmp_20k_buf, SIZEOF_20KBUF,_("Error validating certificate: %s"), errorp);
1174 } else
1175 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", _("Certificate validated without errors"));
1177 gf_puts_uline(tmp_20k_buf, view_writec);
1178 gf_puts(NEWLINE, view_writec);
1180 if(out != NULL){ /* print private key information */
1181 unsigned char ch[2];
1183 gf_puts(NEWLINE, view_writec);
1184 ch[1] = '\0';
1185 while(BIO_read(out, ch, 1) >= 1)
1186 gf_puts((char *)ch, view_writec);
1187 gf_puts(NEWLINE, view_writec);
1188 q_status_message1(SM_ORDER, 1, 3, _("%s information shown at bottom of certificate information"), pub_cert ? _("Public") : _("Private"));
1189 BIO_free_all(out);
1190 out = NULL;
1192 view_writec_destroy();
1193 new_store = 0;
1196 memset(&scrollargs, 0, sizeof(SCROLL_S));
1198 scrollargs.text.text = so_text(store);
1199 scrollargs.text.src = CharStar;
1200 scrollargs.text.desc = "certificate information";
1201 scrollargs.body_valid = 1;
1203 if(offset){ /* resize? preserve paging! */
1204 scrollargs.start.on = Offset;
1205 scrollargs.start.loc.offset = offset;
1206 scrollargs.body_valid = 0;
1207 offset = 0L;
1210 scrollargs.use_indexline_color = 1;
1212 scrollargs.bar.title = _("CERTIFICATE INFORMATION");
1213 scrollargs.proc.tool = manage_certificate_info_tool;
1214 scrollargs.resize_exit = 1;
1215 scrollargs.help.text = h_certificate_information;
1216 scrollargs.help.title = _("HELP FOR MESSAGE TEXT VIEW");
1217 scrollargs.keys.what = FirstMenu;
1218 scrollargs.keys.menu = &smime_certificate_info_keymenu;
1219 setbitmap(scrollargs.keys.bitmap);
1220 if(ctype != Public || error != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
1221 /*error != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)*/
1222 clrbitn(TRUST_KEY, scrollargs.keys.bitmap);
1223 if(ctype != Private){
1224 clrbitn(PUBLIC_KEY, scrollargs.keys.bitmap);
1225 clrbitn(PRIVATE_KEY, scrollargs.keys.bitmap);
1227 if(ctype == Password){
1228 clrbitn(DELETE_CERT_KEY, scrollargs.keys.bitmap);
1229 clrbitn(UNDELETE_CERT_KEY, scrollargs.keys.bitmap);
1232 cmd = scrolltool(&scrollargs);
1234 switch(cmd){
1235 case MC_RESIZE :
1236 case MC_PRIVATE:
1237 case MC_PUBLIC : if(scrollargs.start.on == Offset)
1238 offset = scrollargs.start.loc.offset;
1239 new_store = 1;
1240 default: break;
1242 if(new_store)
1243 so_give(&store);
1244 } while (cmd != MC_EXIT);
1245 ps->mangled_screen = 1;
1249 * This is silly, we just need this function so that we can tell scrolltool
1250 * that some commands are recognized. We use scrolltool because we do not
1251 * want to rewrite output_cert_info.
1254 manage_certificate_info_tool(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1256 int rv;
1257 switch(cmd){
1258 case MC_DELETE:
1259 case MC_UNDELETE:
1260 case MC_PRIVATE:
1261 case MC_PUBLIC:
1262 case MC_TRUST: rv = 1; break;
1263 default: rv = 0; break;
1265 return rv;
1270 manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1272 int rv = 0;
1273 X509 *cert = NULL;
1274 WhichCerts ctype = (*cl)->d.s.ctype;
1276 switch(cmd){
1277 case MC_ADD: /* create a self signed certificate and import it */
1278 if(ctype == Password){
1279 PERSONAL_CERT *pc;
1280 char pathdir[MAXPATH+1], filename[MAXPATH+1];
1281 struct stat sbuf;
1282 int st;
1283 smime_path(DF_SMIMETMPDIR, pathdir, sizeof(pathdir));
1284 if(((st = our_stat(pathdir, &sbuf)) == 0
1285 && (sbuf.st_mode & S_IFMT) == S_IFDIR)
1286 || (st != 0
1287 && can_access(pathdir, ACCESS_EXISTS) != 0
1288 && our_mkpath(pathdir, 0700) == 0)){
1289 pc = ALPINE_self_signed_certificate(NULL, 0, pathdir, MASTERNAME);
1290 snprintf(filename, sizeof(filename), "%s/%s.key",
1291 pathdir, MASTERNAME);
1292 filename[sizeof(filename)-1] = '\0';
1293 rv = import_certificate(ctype, pc, filename);
1294 if(rv == 1){
1295 ps->keyemptypwd = 0;
1296 if(our_stat(pathdir, &sbuf) == 0){
1297 if(unlink(filename) < 0)
1298 q_status_message1(SM_ORDER, 0, 2,
1299 _("Could not remove private key %s.key"), MASTERNAME);
1300 filename[strlen(filename)-4] = '\0';
1301 strcat(filename, ".crt");
1302 if(unlink(filename) < 0)
1303 q_status_message1(SM_ORDER, 0, 2,
1304 _("Could not remove public certificate %s.crt"), MASTERNAME);
1305 if(rmdir(pathdir) < 0)
1306 q_status_message1(SM_ORDER, 0, 2,
1307 _("Could not remove temporary directory %s"), pathdir);
1311 rv = 10; /* forces redraw */
1313 break;
1315 case MC_CHOICE:
1316 if(PATHCERTDIR(ctype) == NULL)
1317 return 0;
1319 if((cert = get_cert_for((*cl)->d.s.address, ctype, 0)) == NULL){
1320 q_status_message(SM_ORDER, 1, 3, _("Problem Reading Certificate"));
1321 rv = 0;
1323 else{
1324 display_certificate_information(ps, cert, (*cl)->d.s.address, ctype, (*cl)->varmem);
1325 rv = 10 + (*cl)->varmem;
1327 break;
1329 case MC_DELETE:
1330 if(ctype == Password){
1331 EVP_PKEY *key = NULL;
1332 PERSONAL_CERT *pc = (PERSONAL_CERT *) ps->pwdcert;
1333 RSA *rsa = NULL;
1334 const EVP_CIPHER *enc = NULL;
1335 BIO *out = NULL;
1336 BIO *in = NULL;
1337 char filename[MAXPATH+1];
1338 char passwd[MAILTMPLEN];
1339 char prompt[MAILTMPLEN];
1341 if (pc != NULL && pc->key != NULL){
1342 strncpy(prompt, _("Enter password to unlock key: "), sizeof(prompt));
1343 prompt[sizeof(prompt)-1] = '\0';
1344 passwd[0] = '\0';
1346 rv = alpine_get_password(prompt, passwd, sizeof(passwd));
1348 if(rv == 1)
1349 q_status_message(SM_ORDER, 1, 3, _("Password deletion cancelled"));
1350 else if(rv == 0){
1351 snprintf(filename, sizeof(filename), "%s/%s.key", ps->pwdcertdir, pc->name);
1352 filename[sizeof(filename)-1] = '\0';
1353 if((in = BIO_new_file(filename, "r")) != NULL){
1354 key = PEM_read_bio_PrivateKey(in, NULL, NULL, passwd);
1355 if(key != NULL){
1356 if((rsa = EVP_PKEY_get1_RSA(key)) != NULL
1357 && (out = BIO_new(BIO_s_file())) != NULL
1358 && BIO_write_filename(out, filename) > 0
1359 && PEM_write_bio_RSAPrivateKey(out, rsa, enc, NULL, 0, NULL, passwd) > 0){
1360 q_status_message(SM_ORDER, 1, 3, _("Password Removed from private key"));
1361 ps->keyemptypwd = 1;
1363 else
1364 rv = 1;
1366 else{
1367 rv = 1;
1368 q_status_message(SM_ORDER, 1, 3, _("Failed to unlock private key"));
1370 BIO_free(in);
1372 else
1373 rv = 1;
1375 if(rv == 1)
1376 q_status_message(SM_ORDER, 1, 3, _("Failed to remove password from private key"));
1377 rv += 10; /* forces redraw */
1378 if(out != NULL)
1379 BIO_free_all(out);
1380 if(rsa != NULL)
1381 RSA_free(rsa);
1382 if(key != NULL)
1383 EVP_PKEY_free(key);
1386 else {
1387 if ((*cl)->d.s.deleted != 0)
1388 q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
1389 else{
1390 (*cl)->d.s.deleted = 1;
1391 rv = 10 + (*cl)->varmem; /* forces redraw */
1392 mark_cert_deleted(ctype, (*cl)->varmem, 1);
1393 q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
1396 break;
1398 case MC_UNDELETE:
1399 if ((*cl)->d.s.deleted == 0)
1400 q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
1401 else{
1402 (*cl)->d.s.deleted = 0;
1403 mark_cert_deleted(ctype, (*cl)->varmem, 0);
1404 rv = 10 + (*cl)->varmem; /* forces redraw */
1405 q_status_message(SM_ORDER, 1, 3, _("Certificate marked UNdeleted"));
1407 break;
1409 case MC_EXPUNGE:
1410 { CertList *cl;
1412 for(cl = DATACERT(ctype); cl != NULL && DELETEDCERT(cl) == 0; cl = cl->next);
1413 if(cl != NULL && DELETEDCERT(cl) != 0){
1414 smime_expunge_cert(ctype);
1415 rv = 10; /* forces redraw */
1417 else{
1418 q_status_message(SM_ORDER, 3, 3, _("No certificates marked deleted"));
1419 rv = 0;
1421 break;
1423 case MC_IMPORT:
1424 rv = import_certificate(ctype, NULL, NULL);
1425 if(rv < 0){
1426 switch(rv){
1427 default:
1428 case -1:
1429 cmd_cancelled("Import certificate");
1430 break;
1432 case -2:
1433 q_status_message1(SM_ORDER, 0, 2, _("Can't import certificate outside of %s"),
1434 ps_global->VAR_OPER_DIR);
1435 break;
1438 rv = 10; /* forces redraw */
1439 break;
1441 case MC_EXIT:
1442 rv = config_exit_cmd(flags);
1443 break;
1445 default:
1446 rv = -1;
1447 break;
1450 X509_free(cert);
1451 return rv;
1454 void
1455 smime_setup_size(char **s, size_t buflen, size_t n)
1457 char *t = *s;
1458 *t++ = ' ';
1459 *t++ = '%';
1460 *t++ = '-';
1461 snprintf(t, buflen-3, "%zu.%zu", n, n);
1462 t += strlen(t);
1463 *t++ = 's';
1464 *t = '\0';
1465 *s = t;
1468 #ifdef PASSFILE
1469 void
1470 manage_password_file_certificates(struct pine *ps)
1472 OPT_SCREEN_S screen;
1473 int readonly_warning = 0, rv = 10, fline, state = 0;
1475 dprint((9, "manage_password_file_certificates"));
1476 ps->next_screen = SCREEN_FUN_NULL;
1477 ps->keyemptypwd = 0; /* just in case */
1479 do {
1480 CONF_S *ctmp = NULL, *first_line = NULL;
1482 fline = rv >= 10 ? rv - 10 : 0;
1484 smime_manage_password_file_certs_init(ps, &ctmp, &first_line, fline, &state);
1486 if(ctmp == NULL){
1487 ps->mangled_screen = 1;
1488 q_status_message(SM_ORDER, 1, 3, _("Failed to initialize password management screen (no key)"));
1489 return;
1492 memset(&screen, 0, sizeof(screen));
1493 screen.deferred_ro_warning = readonly_warning;
1495 rv = conf_scroll_screen(ps, &screen, first_line,
1496 _("MANAGE PASSWORD FILE CERTS"),
1497 /* TRANSLATORS: Print something1 using something2.
1498 configuration is something1 */
1499 _("configuration"), 0, NULL);
1500 } while (rv != 0);
1502 ps->mangled_screen = 1;
1503 ps->keyemptypwd = 0; /* reset this so it will not confuse other routines */
1504 smime_reinit();
1507 /* state: 0 = first time,
1508 * 1 = second or another time
1510 void
1511 smime_manage_password_file_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, int fline, int *state)
1513 char tmp[200];
1514 char *ext;
1515 CertList *cl;
1516 int i;
1517 void *pwdcert = NULL; /* this is our current password file */
1518 X509_LOOKUP *lookup = NULL;
1519 X509_STORE *store = NULL;
1520 char filename[MAXPATH+1];
1521 BIO *in = NULL;
1522 EVP_PKEY *key = NULL;
1523 PERSONAL_CERT *pc;
1525 if(*state == 0){ /* first time around? */
1526 setup_pwdcert(&pwdcert);
1527 if(pwdcert == NULL) return;
1528 if(ps->pwdcert == NULL)
1529 ps->pwdcert = pwdcert;
1530 else
1531 free_personal_certs((PERSONAL_CERT **) &pwdcert);
1532 (*state)++;
1535 pc = (PERSONAL_CERT *) ps_global->pwdcert;
1536 snprintf(filename, sizeof(filename), "%s/%s.key", ps->pwdcertdir, pc->name);
1537 filename[sizeof(filename)-1] = '\0';
1538 if((in = BIO_new_file(filename, "r")) != NULL
1539 && (key = PEM_read_bio_PrivateKey(in, NULL, NULL, "")) != NULL)
1540 ps->keyemptypwd = 1;
1541 if(in != NULL)
1542 BIO_free(in);
1543 if(key != NULL)
1544 EVP_PKEY_free(key);
1546 ps->pwdcertlist = cl = smime_X509_to_cert_info(X509_dup(pc->cert), pc->name);
1548 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1549 tmp[i] = '-';
1550 tmp[i] = '\0';
1552 new_confline(ctmp);
1553 (*ctmp)->flags |= CF_NOSELECT;
1554 (*ctmp)->value = cpystr(tmp);
1556 new_confline(ctmp);
1557 (*ctmp)->flags |= CF_NOSELECT;
1558 (*ctmp)->value = cpystr(_("Manage Certificates and Keys Used to Encrypt your Password File"));
1560 new_confline(ctmp);
1561 (*ctmp)->flags |= CF_NOSELECT;
1562 (*ctmp)->value = cpystr(tmp);
1564 new_confline(ctmp);
1565 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1567 if(cl){
1568 int s, e, df, dt, md5; /* sizes of certain fields */
1569 int nf; /* number of fields */
1570 char u[MAILTMPLEN], *t;
1572 e = MIN(strlen(cl->name), ps->ttyo->screen_cols/3); /* do not use too much screen */
1573 nf = 5; /* there are 5 fields */
1574 s = 3; /* status has fixed size */
1575 df = dt = 10; /* date from and date to have fixed size */
1576 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1578 t = u;
1579 smime_setup_size(&t, sizeof(u), s);
1580 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1581 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1582 *t++ = ' '; /* leave an extra space between dates */
1583 *t = '\0'; /* make valgrind happy */
1584 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1585 *t++ = ' '; /* and another space between date and md5 sum */
1586 *t = '\0'; /* make valgrind happy again */
1587 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1588 *t = '\0'; /* tie off */
1590 new_confline(ctmp);
1591 (*ctmp)->flags |= CF_NOSELECT;
1592 (*ctmp)->value = cpystr(_("New Public Certificate and Key:"));
1594 new_confline(ctmp);
1595 (*ctmp)->d.s.ctype = Password;
1596 (*ctmp)->help = h_config_smime_password_file_certificates;
1597 (*ctmp)->tool = manage_certs_tool;
1598 (*ctmp)->keymenu = &config_smime_add_new_key_keymenu;
1599 s += 2;
1600 for(i = 0; i < s; i++) tmp[i] = ' ';
1601 tmp[i] = '\0';
1602 strncpy(tmp+s, _("Press \"RETURN\" to add new personal key"), sizeof(tmp)-s-1);
1603 for(i = strlen(tmp); i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp) - 1); i++)
1604 tmp[i] = ' ';
1605 tmp[i] = '\0';
1606 (*ctmp)->value = cpystr(tmp);
1607 *first_line = *ctmp;
1609 new_confline(ctmp);
1610 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1612 new_confline(ctmp);
1613 (*ctmp)->flags |= CF_NOSELECT;
1614 (*ctmp)->value = cpystr(_("Current Public Certificate and Key:"));
1616 new_confline(ctmp);
1617 (*ctmp)->d.s.ctype = Password;
1618 (*ctmp)->d.s.deleted = 0;
1619 (*ctmp)->help = h_config_smime_password_file_certificates;
1620 (*ctmp)->tool = manage_certs_tool;
1621 (*ctmp)->keymenu = ps->keyemptypwd == 0
1622 ? &config_smime_manage_view_cert_keymenu
1623 : &config_smime_manage_view_cert_keymenu_no_delete;
1624 (*ctmp)->varmem = 0;
1625 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1626 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1627 snprintf(tmp, sizeof(tmp), u,
1628 (*ctmp)->d.s.deleted ? "D" : " ",
1629 cl->name,
1630 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1631 (*ctmp)->value = cpystr(tmp);
1634 #endif /* PASSFILE */
1637 void
1638 smime_manage_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, WhichCerts ctype, int fline)
1640 char tmp[200];
1641 char *ext;
1642 CertList *data;
1643 int i;
1645 smime_init();
1647 data = DATACERT(ctype);
1648 ext = EXTCERT(ctype);
1650 if(data == NULL || RENEWCERT(data))
1651 renew_cert_data(&data, ctype);
1653 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1654 tmp[i] = '-';
1655 tmp[i] = '\0';
1657 new_confline(ctmp);
1658 (*ctmp)->flags |= CF_NOSELECT;
1659 (*ctmp)->value = cpystr(tmp);
1661 (*ctmp)->keymenu = &config_text_keymenu;
1663 new_confline(ctmp);
1664 (*ctmp)->flags |= CF_NOSELECT;
1665 sprintf(tmp, _("List of %s certificates"), ctype == Public ? _("public")
1666 : (ctype == Private ? _("private")
1667 : (ctype == CACert ? _("certificate authority") : "unknown (?)")));
1668 (*ctmp)->value = cpystr(tmp);
1670 for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
1671 tmp[i] = '-';
1672 tmp[i] = '\0';
1674 new_confline(ctmp);
1675 (*ctmp)->flags |= CF_NOSELECT;
1676 (*ctmp)->value = cpystr(tmp);
1678 new_confline(ctmp);
1679 (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
1681 if(data){
1682 CertList *cl; int i;
1683 int s, e, df, dt, md5; /* sizes of certain fields */
1684 int nf; /* number of fields */
1685 char u[MAILTMPLEN], *t;
1687 for(cl = data, e = 0; cl; cl = cl->next)
1688 if(cl->name && strlen(cl->name) > e)
1689 e = strlen(cl->name);
1691 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1692 e -= 4; /* remove extension length */
1693 e = MIN(e, ps->ttyo->screen_cols/3); /* do not use too much screen */
1694 nf = 5; /* there are 5 fields */
1695 s = 3; /* status has fixed size */
1696 df = dt = 10; /* date from and date to have fixed size */
1697 md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1);
1699 t = u;
1700 smime_setup_size(&t, sizeof(u), s);
1701 smime_setup_size(&t, sizeof(u) - strlen(t), e);
1702 smime_setup_size(&t, sizeof(u) - strlen(t), df);
1703 *t++ = ' '; /* leave an extra space between dates */
1704 *t = '\0'; /* make valgrind happy */
1705 smime_setup_size(&t, sizeof(u) - strlen(t), dt);
1706 *t++ = ' '; /* and another space between date and md5 sum */
1707 *t = '\0'; /* make valgrind happy again */
1708 smime_setup_size(&t, sizeof(u) - strlen(t), md5);
1709 *t = '\0'; /* tie off */
1711 for(cl = data, i = 0; cl; cl = cl->next)
1712 if(cl->name){
1713 char *s, *t;
1715 new_confline(ctmp);
1716 (*ctmp)->d.s.ctype = ctype;
1717 (*ctmp)->d.s.deleted = get_cert_deleted(ctype, i);
1718 (*ctmp)->tool = manage_certs_tool;
1719 (*ctmp)->keymenu = &config_smime_manage_certs_work_keymenu;
1720 (*ctmp)->varmem = i++;
1721 (*ctmp)->help = ctype == Public ? h_config_smime_manage_public_menu
1722 : (ctype == Private ? h_config_smime_manage_private_menu
1723 : h_config_smime_manage_cacerts_menu);
1724 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1725 cl->name[strlen(cl->name) - 4] = '\0'; /* FIX FIX FIX */
1726 strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address));
1727 (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0';
1728 snprintf(tmp, sizeof(tmp), u,
1729 (*ctmp)->d.s.deleted ? "D" : " ",
1730 ctype == CACert ? cl->cn : cl->name,
1731 DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl));
1732 if(ctype != Private && SMHOLDERTYPE(ctype) == Directory)
1733 cl->name[strlen(cl->name)] = '.';
1734 (*ctmp)->value = cpystr(tmp);
1735 if(i == fline+1 && first_line && !*first_line)
1736 *first_line = *ctmp;
1739 else {
1740 new_confline(ctmp);
1741 (*ctmp)->d.s.ctype = ctype;
1742 (*ctmp)->tool = manage_certs_tool;
1743 (*ctmp)->keymenu = &config_smime_add_certs_keymenu;
1744 (*ctmp)->value = cpystr(_(" \tNo certificates found, press \"RETURN\" to add one."));
1745 if(first_line && !*first_line)
1746 *first_line = *ctmp;
1750 void
1751 manage_certificates(struct pine *ps, WhichCerts ctype)
1753 OPT_SCREEN_S screen;
1754 int readonly_warning = 0, rv = 10, fline;
1756 dprint((9, "manage_certificates(ps, %s)", ctype == Public ? _("Public") : (ctype == Private ? _("Private") : (ctype == CACert ? _("certificate authority") : _("unknown")))));
1757 ps->next_screen = SCREEN_FUN_NULL;
1759 do {
1760 CONF_S *ctmp = NULL, *first_line = NULL;
1762 fline = rv >= 10 ? rv - 10 : 0;
1764 smime_init();
1766 smime_manage_certs_init(ps, &ctmp, &first_line, ctype, fline);
1768 if(ctmp == NULL){
1769 ps->mangled_screen = 1;
1770 smime_reinit();
1771 return;
1774 memset(&screen, 0, sizeof(screen));
1775 screen.deferred_ro_warning = readonly_warning;
1776 rv = conf_scroll_screen(ps, &screen, first_line,
1777 _("MANAGE CERTIFICATES"),
1778 /* TRANSLATORS: Print something1 using something2.
1779 configuration is something1 */
1780 _("configuration"), 0, NULL);
1781 } while (rv != 0);
1783 ps->mangled_screen = 1;
1784 smime_reinit();
1788 smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
1790 int rv = 0;
1792 switch(cmd){
1793 case MC_CHOICE:
1794 switch((*cl)->varmem){
1795 case 1:
1796 rv = copy_publiccert_dir_to_container();
1797 if(rv == 0)
1798 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1799 else{
1800 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1801 rv = 0;
1804 break;
1806 case 2:
1807 rv = copy_publiccert_container_to_dir();
1808 if(rv == 0)
1809 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to directory, delete Container config to use"));
1810 else{
1811 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1812 rv = 0;
1815 break;
1817 case 3:
1818 rv = copy_privatecert_dir_to_container();
1819 if(rv == 0)
1820 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to container"));
1821 else{
1822 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1823 rv = 0;
1826 break;
1828 case 4:
1829 rv = copy_privatecert_container_to_dir();
1830 if(rv == 0)
1831 q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to directory, delete Container config to use"));
1832 else{
1833 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1834 rv = 0;
1837 break;
1839 case 5:
1840 rv = copy_cacert_dir_to_container();
1841 if(rv == 0)
1842 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to container"));
1843 else{
1844 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1845 rv = 0;
1848 break;
1850 case 6:
1851 rv = copy_cacert_container_to_dir();
1852 if(rv == 0)
1853 q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to directory, delete Container config to use"));
1854 else{
1855 q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
1856 rv = 0;
1859 break;
1861 #ifdef APPLEKEYCHAIN
1862 case 7:
1863 rv = copy_publiccert_container_to_keychain();
1864 if(rv == 0)
1865 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to keychain"));
1866 else{
1867 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1868 rv = 0;
1871 break;
1873 case 8:
1874 rv = copy_publiccert_keychain_to_container();
1875 if(rv == 0)
1876 q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
1877 else{
1878 q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
1879 rv = 0;
1882 break;
1883 #endif /* APPLEKEYCHAIN */
1885 case 9: manage_certificates(ps, Public) ; break;
1886 case 10: manage_certificates(ps, Private); break;
1887 case 11: manage_certificates(ps, CACert) ; break;
1889 #ifdef PASSFILE
1890 case 12: manage_password_file_certificates(ps); break;
1891 #endif /* PASSFILE */
1893 default:
1894 rv = -1;
1895 break;
1898 break;
1900 case MC_EXIT:
1901 rv = config_exit_cmd(flags);
1902 break;
1904 case MC_IMPORT:
1905 rv = import_certificate((*cl)->d.s.ctype, NULL, NULL);
1906 break;
1908 default:
1909 rv = -1;
1910 break;
1913 return rv;
1918 * Compare saved user_val with current user_val to see if it changed.
1919 * If any have changed, change it back and take the appropriate action.
1921 void
1922 revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave)
1924 struct variable *vreal;
1925 SAVED_CONFIG_S *v;
1926 int i, n;
1927 int changed = 0;
1928 char *pval, **apval, **lval, ***alval;
1930 v = vsave;
1931 for(vreal = ps->vars; vreal->name; vreal++,v++){
1932 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
1933 continue;
1935 if(vreal->is_list){
1936 lval = LVAL(vreal, ew);
1937 alval = ALVAL(vreal, ew);
1939 if((v->saved_user_val.l && !lval)
1940 || (!v->saved_user_val.l && lval))
1941 changed++;
1942 else if(!v->saved_user_val.l && !lval)
1943 ;/* no change, nothing to do */
1944 else
1945 for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
1946 if((v->saved_user_val.l[i]
1947 && (!lval[i]
1948 || strcmp(v->saved_user_val.l[i], lval[i])))
1950 (!v->saved_user_val.l[i] && lval[i])){
1951 changed++;
1952 break;
1955 if(changed){
1956 char **list;
1958 if(alval){
1959 if(*alval)
1960 free_list_array(alval);
1962 /* copy back the original one */
1963 if(v->saved_user_val.l){
1964 list = v->saved_user_val.l;
1965 n = 0;
1966 /* count how many */
1967 while(list[n])
1968 n++;
1970 *alval = (char **)fs_get((n+1) * sizeof(char *));
1972 for(i = 0; i < n; i++)
1973 (*alval)[i] = cpystr(v->saved_user_val.l[i]);
1975 (*alval)[n] = NULL;
1980 else{
1981 pval = PVAL(vreal, ew);
1982 apval = APVAL(vreal, ew);
1984 if((v->saved_user_val.p &&
1985 (!pval || strcmp(v->saved_user_val.p, pval))) ||
1986 (!v->saved_user_val.p && pval)){
1987 /* It changed, fix it */
1988 changed++;
1989 if(apval){
1990 /* free the changed value */
1991 if(*apval)
1992 fs_give((void **)apval);
1994 if(v->saved_user_val.p)
1995 *apval = cpystr(v->saved_user_val.p);
2000 if(changed){
2001 if(vreal == &ps->vars[V_FEATURE_LIST])
2002 set_feature_list_current_val(vreal);
2003 else
2004 set_current_val(vreal, TRUE, FALSE);
2006 fix_side_effects(ps, vreal, 1);
2012 SAVED_CONFIG_S *
2013 save_smime_config_vars(struct pine *ps)
2015 struct variable *vreal;
2016 SAVED_CONFIG_S *vsave, *v;
2018 vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
2019 memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
2020 for(v = vsave, vreal = ps->vars; vreal->name; vreal++,v++){
2021 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
2022 continue;
2024 if(vreal->is_list){
2025 int n, i;
2026 char **list;
2028 if(LVAL(vreal, ew)){
2029 /* count how many */
2030 n = 0;
2031 list = LVAL(vreal, ew);
2032 while(list[n])
2033 n++;
2035 v->saved_user_val.l = (char **)fs_get((n+1)*sizeof(char *));
2036 memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
2037 for(i = 0; i < n; i++)
2038 v->saved_user_val.l[i] = cpystr(list[i]);
2040 v->saved_user_val.l[n] = NULL;
2043 else{
2044 if(PVAL(vreal, ew))
2045 v->saved_user_val.p = cpystr(PVAL(vreal, ew));
2049 return(vsave);
2053 void
2054 free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep)
2056 struct variable *vreal;
2057 SAVED_CONFIG_S *v;
2059 if(vsavep && *vsavep){
2060 for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
2061 if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
2062 continue;
2064 if(vreal->is_list){ /* free saved_user_val.l */
2065 if(v && v->saved_user_val.l)
2066 free_list_array(&v->saved_user_val.l);
2068 else if(v && v->saved_user_val.p)
2069 fs_give((void **)&v->saved_user_val.p);
2072 fs_give((void **)vsavep);
2076 #endif /* SMIME */