#!/usr/local/bin/perl
# cert_form.cgi
# Show a form for requesting a CSR, or installing a cert

require './virtual-server-lib.pl';
&ReadParse();
$d = &get_domain($in{'dom'});
$d || &error($text{'edit_egone'});
$tmpl = &get_template($d->{'template'});
&can_edit_domain($d) && &can_edit_ssl() || &error($text{'edit_ecannot'});
&foreign_require("webmin");
&ui_print_header(&domain_in($d), $text{'cert_title'}, "");
@already = &get_all_domain_service_ssl_certs($d);

# If this domain shares a cert file with another, link to it's page
if ($d->{'ssl_same'}) {
	$same = &get_domain($d->{'ssl_same'});
	print &text('cert_same', &show_domain_name($same)),"\n";
	if (&can_edit_domain($same)) {
		print &text('cert_samelink', "cert_form.cgi?dom=$same->{'id'}");
		}
	print "<p>\n";
	print $text{'cert_breakdesc'},"<p>\n";
	print &ui_form_start("break_cert.cgi");
	print &ui_hidden("dom", $d->{'id'});
	print &ui_form_end([ [ undef, $text{'cert_break'} ] ]);
	&ui_print_footer(&domain_footer_link($d),
			 "", $text{'index_return'});
	return;
	}

# Get ACME providers, if there are any
my @provs;
if (defined(&list_acme_providers)) {
	@provs = grep { &can_acme_provider($_) ||
			$d->{'letsencrypt_id'} eq $_->{'id'} }
		      &list_acme_providers();
	%known = map { $_->{'id'}, $_ } &list_known_acme_providers();
	foreach my $p (@provs) {
		if (!$p->{'desc'} && $p->{'type'}) {
			$p->{'desc'} = $known{$p->{'type'}}->{'desc'};
			}
		}
	}

# Show tabs
$prog = "cert_form.cgi?dom=$in{'dom'}&mode=";
@tabs = ( [ "current", $text{'cert_tabcurrent'}, $prog."current" ],
	  [ "csr", $text{'cert_tabcsr'}, $prog."csr" ],
	  [ "self", $text{'cert_tabself'}, $prog."self" ],
	  -r $d->{'ssl_newkey'} ?
		( [ "savecsr", $text{'cert_tabsavecsr'}, $prog."savecsr" ] ) :
		( ),
	  [ "new", $text{'cert_tabnew'}, $prog."new" ],
	  &can_edit_letsencrypt() &&
	  (@provs || !defined(&list_acme_providers)) &&
	  (&domain_has_website($d) || $d->{'dns'}) ?
		( [ "lets", $text{'cert_tabacme'}, $prog."lets" ] ) :
		( ),
	);
print &ui_tabs_start(\@tabs, "mode", $in{'mode'} || "current", 1);

# Details of current cert
print &ui_tabs_start_tab("mode", "current");

if (&domain_has_ssl_cert($d)) {
	print "<p>$text{'cert_desc2'}</p>\n";
	if (!&domain_has_ssl($d)) {
		print &ui_alert_box($text{'cert_hasnossl'}, 'warn');
		}

	print &ui_table_start($text{'cert_header2'}, undef, 4);

	# Create links to Filemin if installed
	my $filemin_prefix;
	my %filemin_acls;
	if (&foreign_available("filemin")) {
		&foreign_require("filemin");
		$filemin_prefix = &get_webprefix_safe().
			"/filemin/index.cgi?path=";
		%filemin_acls = &get_module_acl(undef, 'filemin');
		}

	# Cert files
	my @certs = ( ['cert_incert', 'ssl_cert', 'cert'],
		      ['cert_inkey', 'ssl_key', 'key'],
		      ['cert_inca', 'ssl_chain', undef] );
	my @cert_files;
	foreach (@certs) {
		my ($label_key, $field, $dtype) = @$_;
		my $val = $d->{$field} or next;		
		my $file = &ui_tag('tt', $val);
		
		# File name for file manager if available
		if ($filemin_prefix) {
			my $dir = $val;
			$dir =~ s{/[^/]*$}{};
			my @cert_allowed = &filemin::get_allowed_paths($dir);
			my $rel;
			for my $root (@cert_allowed) {
				if (&is_under_directory($root, $dir)) {
					$rel = $dir;
					if ($filemin_acls{'work_as_user'} eq
					    $d->{'user'}) {
						$rel =~ s/^\Q$root\E//;
						}
					last;
					}
				}
			if (defined $rel) {
				my $p = &urlize($rel);
				$file = &ui_link("$filemin_prefix$p", $file,
						 'text-link');
				}
			}
		
		my $line = &ui_tag('strong',
			$text{$label_key}).&ui_tag('br').$file;
		if ($dtype) {
			my @dlinks = (
				&ui_link("download_${dtype}.cgi/$dtype.pem?dom".
					 "=$in{'dom'}", $text{'cert_pem'}),
				&ui_link("download_${dtype}.cgi/$dtype.p12?dom".
					 "=$in{'dom'}", $text{'cert_pkcs12'}));
			$line .= &ui_tag('br').&ui_links_row(\@dlinks);
			}
		push(@cert_files, $line);
		}
	if (@cert_files) {
		print &ui_table_row(
			$text{'cert_files'},
			&ui_details({
				html => 1,
				class => 'inline fit',
				title => &text('cert_filescnt',
					scalar(@cert_files)),
				content => join("<br>", @cert_files) }));
		}

	# Cert hash type
	$type = &get_ssl_key_type($d->{'ssl_key'}, $d->{'ssl_pass'});
	if ($type) {
		print &ui_table_row($text{'cert_hash'},
			$text{'cert_type_'.$type} || uc($type));
		}
	
	# Other cert attributes
	$info = &cert_info($d);
	$chaininfo = &get_domain_ca_cert_info($d);
	foreach $i (@cert_attributes) {
		next if ($i eq 'modulus' || $i eq 'exponent');
		$v = $info->{$i};
		if (ref($v)) {
			my @vlist = @$v;
			my @opart = splice(@vlist, 0, 5);
			my $ofirst = join(', ', @opart);
			my $orest  = join(', <br>', @vlist);
			$others = $orest
				? &ui_details({
					html => 1,
					class => 'inline fit',
					title => $ofirst,
					content => $orest }) 
				: $ofirst;
			print &ui_table_row($text{'cert_'.$i}, $others, 3);
			}
		elsif ($v) {
			print &ui_table_row($text{'cert_'.$i}, $v);
			}

		# Warn if the CA is wrong
		if ($i eq 'type' && $chaininfo) {
			my $cainfo = $chaininfo;
			if (&cert_issuer_ca_mismatch($info, $cainfo)) {
				print &ui_table_row('',
				    &ui_text_color(
				      "&nbsp;* ".&text('validate_esslcamatch',
					&cert_principal_name(
					    $cainfo->{'o'}, $cainfo->{'cn'}),
					&cert_principal_name(
					    $info->{'issuer_o'}, $info->{'issuer_cn'})),
				      "danger"), 3);
				}
			}
		}

	# Other domains using same cert, such as via wildcards or UCC
	@others = grep { &domain_has_ssl_cert($_) }
		       &get_domain_by("ssl_same", $d->{'id'});
	if (@others) {
		my @links;
		foreach my $d (@others) {
			my $l = &can_config_domain($d) ? "edit_domain.cgi"
						       : "view_domain.cgi";
			push(@links, "<a href='${l}?dom=$d->{'id'}'>".
				     &show_domain_name($d)."</a>");
			}
		my $lnum = scalar(@links);
		my $ldesc = $lnum == 1 ?
			$text{'cert_also_desc1'} :
			&text('cert_also_desc2', $lnum);
		my $others = &ui_details({
				html => 1,
				class => 'inline',
				title => $ldesc,
				content => join('<br>', @links) });
		print &ui_table_row($text{'cert_also_title'}, $others, 3);
		}

	# Current usage
	if (@already) {
		my @msgall;
		my @msgsglob;
		my %msgsdom;
		my %msgsip;
		foreach my $svc (@already) {
			my $m = &text('cert_service_'.$svc->{'id'});
			push(@msgall, $m);
			if ($svc->{'ip'}) {
				$msgsip{$svc->{'ip'}} ||= [];
				push(@{$msgsip{$svc->{'ip'}}}, $m);
				}
			elsif ($svc->{'dom'}) {
				$msgsdom{$svc->{'dom'}} ||= [];
				push(@{$msgsdom{$svc->{'dom'}}}, $m);
				}
			else {
				push(@msgsglob, $m);
				}
			}
		# Always show all seen services as title
		my $already = join(", ", &unique(@msgall));
		# The rest show in details with indentation for readability
		my $pad   = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
		my $label = sub {
			my ($lbl, $val) = @_;
			return "<b>$lbl</b><br><span>$val</span><br>";
			};
		my $spanwrap = sub {
			my ($val, $pad) = @_;
			$pad ||= 1;
			return "<span style='padding-left:".(32/$pad).
				"px;display: inline-block;'>$val</span>";
			};
		my $already_details = '';
		# Globally
		if (@msgsglob) {
			$already_details .= $label->(
				$text{'cert_svcsglob'},
				$spanwrap->(join(", ", @msgsglob), 2)
				);
			}
		# By domain
		if (%msgsdom) {
			my @doms = map {
			    $pad . "<b>" . &show_domain_name($_) . "</b><br>" .
			    $spanwrap->(join(", ", @{ $msgsdom{$_} }))
			} sort keys %msgsdom;
			$already_details .= $label->(
				$text{'cert_svcsbydom'},
				join("<br>", @doms)
				);
			}
		# By IP
		if (%msgsip) {
			my @ips = map {
				$pad . "<b>$_</b><br>" .
				$spanwrap->(join(", ", @{ $msgsip{$_} }))
			} sort keys %msgsip;
			$already_details .= $label->(
				$text{'cert_svcsbyip'},
				join("<br>", @ips)
				);
			}
		# Show the details with all the services nicely formatted
		# and indented
		$already = &ui_details({
			html => 1,
			class => 'inline fit',
			title => $already,
			content => $already_details
			});
		print &ui_table_row($text{'cert_svcs'}, $already, 3);
		}

	# Can copy as global
	my @gmissing;
	foreach my $st (&list_service_ssl_cert_types()) {
		($a) = grep { !$_->{'d'} && $_->{'id'} eq $st->{'id'}} @already;
		push(@gmissing, $st) if (!$a);
		}

	# Expiry status, if we have it
	my $expiry = &parse_notafter_date($info->{'notafter'});
	if ($expiry) {
		$now = time();
		my $remaining = $expiry - $now;
		$future = int($remaining / (24*60*60));
		my $ipcert = grep {
			&check_ipaddress($_) || &check_ip6address($_)
			} grep { $_ } ($info->{'cn'}, @{$info->{'alt'} || []});
		# IP certificates are short-lived, so warn only on the last day.
		my $warn_days = $ipcert ? 2 : 7;
		if ($remaining <= 0) {
			$emsg = "<font color=red>".
				&text('cert_expired', int(-$remaining/(24*60*60))).
				"</font>";
			}
		elsif ($future < $warn_days) {
			$emsg = "<font color=orange>".
				&text('cert_expiring', $future || 1)."</font>";
			}
		else {
			$emsg = &text('cert_future', $future);
			}
		print &ui_table_row($text{'cert_etime'}, $emsg);
		}

	print &ui_table_row($text{'cert_def'},
		(@gmissing && &can_webmin_cert()) ? $text{'no'} : $text{'yes'}, 3);

	# CA cert details
	if ($chaininfo) {
		my $ui_table_hr;
		my $info = $chaininfo;
		my @ca_cert_attributes = ('cn', 'notafter',
		      grep { $_ ne 'cn' && $_ ne 'notafter' } @cert_attributes);
		foreach $i (@ca_cert_attributes) {
			next if ($i eq 'modulus' || $i eq 'exponent');
			if ($info->{$i} && !ref($info->{$i})) {
				next if ($i eq 'type');
				print &ui_table_hr() if (!$ui_table_hr++);
				print &ui_table_row($text{'cert_c'.$i} ||
					    $text{'cert_ca'.$i} ||
					    $text{'cert_'.$i}, $info->{$i});
				}
			}
		}

	print &ui_table_end();

	my $ui_hr;
	print &ui_buttons_start();

	if (!&domain_has_ssl($d) && !@already && !$d->{'ssl_same'}) {
		print &ui_hr() if (!$ui_hr++);
		# Show button to remove SSL cert
		print &ui_buttons_row("remove_cert.cgi",
				      $text{'cert_remove'},
				      $text{'cert_removedesc'},
				      &ui_hidden("dom", $in{'dom'}));
		}

	# Show button to copy to per-service, if any are missing
	my @smissing;
	my @sall;
	foreach my $st (&list_service_ssl_cert_types()) {
		next if (!$st->{'dom'} && !$st->{'virt'});
		next if (!$st->{'dom'} && !$d->{'virt'});
		($a) = grep { $_->{'d'} && $_->{'id'} eq $st->{'id'} } @already;
		push(@sall, $st);
		push(@smissing, $st) if (!$a);
		}
	if (@smissing && &can_webmin_cert()) {
		print &ui_hr() if (!$ui_hr++);
		print &ui_buttons_row(
			"peripcerts.cgi",
			$text{'cert_copyall'},
			&text('cert_copyalldesc',
			    &vui_make_and(map { $_->{'desc'} } @smissing)),
			&ui_hidden("dom", $in{'dom'}).
			&ui_hidden("enable", 1));
		}
	else {
		# Show button to uninstall all per-service
		print &ui_hr() if (!$ui_hr++);
		print &ui_buttons_row(
			"peripcerts.cgi",
			$text{'cert_removeall'},
			&text('cert_removealldesc',
			    &vui_make_and(map { $_->{'desc'} } @sall)),
			&ui_hidden("dom", $in{'dom'}));
		}

	# Show button to copy to global
	if (@gmissing && &can_webmin_cert()) {
		print &ui_hr() if (!$ui_hr++);
		print &ui_buttons_row(
			"copy_cert_all.cgi",
			$text{'cert_copyall2'},
			&text('cert_copyall2desc',
			    &vui_make_and(map { $_->{'desc'} } @gmissing)),
			&ui_hidden("dom", $in{'dom'}));
		}

	# Show button to copy to the default location. Only the certificate
	# and key paths should expose this action; CA and generated combined
	# files may be moved as helpers, but should not make it appear alone.
	my $needs_default_path = 0;
	foreach my $type ("key", "cert") {
		my $oldfile = &get_website_ssl_file($d, $type);
		my $deffile = &default_certificate_file($d, $type);
		if ($oldfile && $oldfile ne $deffile) {
			$needs_default_path = 1;
			last;
			}
		}
	if (!$d->{'ssl_same'} && $needs_default_path) {
		my $defcert_dir = &default_certificate_file($d, "cert");
		$defcert_dir =~ s|/[^/]+$||;
		print &ui_hr() if (!$ui_hr++);
		print &ui_buttons_row(
			"default_cert.cgi",
			$text{'cert_defaultpath'},
			&text('cert_defaultpathdesc',
			  "<tt>$defcert_dir</tt>"),
			&ui_hidden("dom", $in{'dom'}));
		}

	print &ui_buttons_end();
	}
else {
	# No cert yet! Perhaps a domain without SSL that has no cert yet
	print "<p>",$text{'cert_noneyet'},"</p>\n";
	}

print &ui_tabs_end_tab();

##########################

# CSR generation form
print &ui_tabs_start_tab("mode", "csr");
print "$text{'cert_desc1'}<br>\n";
print "$text{'cert_desc4'}<p>\n";

# Show warning if there is a CSR outstanding
if ($d->{'ssl_csr'} && -r $d->{'ssl_csr'}) {
	print &ui_alert_box(&text('cert_csrwarn',
		"<tt>".&home_relative_path($d, $d->{'ssl_csr'})."</tt>",
		"<tt>".&home_relative_path($d, $d->{'ssl_newkey'})."</tt>"), 'warn');
	}

print &ui_form_start("csr.cgi");
print &ui_hidden("dom", $in{'dom'});
print &ui_table_start($text{'cert_header1'}, undef, 2);
&print_cert_fields(0);
print &ui_table_end();
print &ui_form_end([ [ undef, $text{'cert_csrok'} ] ]);
print &ui_tabs_end_tab();

##########################

# Self-signed key generation form
print &ui_tabs_start_tab("mode", "self");
print "$text{'cert_desc6'}<p>\n";

# Show warning if there is an existing key
if ($d->{'ssl_key'} && -r $d->{'ssl_key'} && !&self_signed_cert($d)) {
	print &ui_alert_box(&text('cert_keywarn',
		"<tt>".&home_relative_path($d, $d->{'ssl_cert'})."</tt>",
		"<tt>".&home_relative_path($d, $d->{'ssl_key'})."</tt>"), 'warn');
	}

print &ui_form_start("csr.cgi");
print &ui_hidden("dom", $in{'dom'});
print &ui_hidden("self", 1);
print &ui_table_start($text{'cert_header6'}, undef, 2);
&print_cert_fields(1);
print &ui_table_end();
print &ui_form_end([ [ undef, $text{'cert_self'} ] ]);
print &ui_tabs_end_tab();

##########################

# Apply signed cert form
print &ui_tabs_start_tab("mode", "savecsr");
print "$text{'cert_desc7'}<p>\n";

print &ui_form_start("newkey.cgi", "form-data");
print &ui_hidden("dom", $in{'dom'});
print &ui_table_start($text{'cert_header7'}, undef, 2);

# Cert
print &ui_table_row($text{'cert_cert'},
	&ui_radio_table("cert_mode", 0,
		[ [ 0, $text{'cert_cert0'},
		    &ui_textarea("cert", undef, 8, 70) ],
		  [ 1, $text{'cert_cert1'},
		    &ui_upload("certupload") ],
		  [ 2, $text{'cert_cert2'},
		    &ui_textbox("certfile", undef, 70)." ".
		    &file_chooser_button("certfile") ] ]));

# Use saved key from when CSR was generated
print &ui_hidden("newkey_mode", 4);
print &ui_hidden("newca_mode", 3);

print &ui_table_end();
print &ui_form_end([ [ "ok", $text{'cert_newok'} ] ]);
print &ui_tabs_end_tab();

##########################

# New key, cert and CA form
print &ui_tabs_start_tab("mode", "new");
print "$text{'cert_desc3'} $text{'cert_desc3a'}<p>\n";

print &ui_form_start("newkey.cgi", "form-data");
print &ui_hidden("dom", $in{'dom'});
print &ui_table_start($text{'cert_header3'}, undef, 2);

# Cert
my $gotcert = $d->{'ssl_cert'} && -r $d->{'ssl_cert'};
print &ui_table_row($text{'cert_cert'},
	&ui_radio_table("cert_mode", $gotcert ? 3 : 0,
		[ $gotcert ? ( [ 3, $text{'cert_newcertkeep'} ] ) : ( ),
		  [ 0, $text{'cert_cert0'},
		    &ui_textarea("cert", undef, 8, 70) ],
		  [ 1, $text{'cert_cert1'},
		    &ui_upload("certupload") ],
		  [ 2, $text{'cert_cert2'},
		    &ui_filebox("certfile", $d->{'ssl_cert'}, 70) ] ]));

# Key
my $gotkey = $d->{'ssl_key'} && -r $d->{'ssl_key'};
print &ui_table_row($text{'cert_newkey'},
	&ui_radio_table("newkey_mode",
		$gotkey ? 3 : 0,
		[ $gotkey ? ( [ 3, $text{'cert_newkeykeep'} ] ) : ( ),
		  [ 0, $text{'cert_cert0'},
		    &ui_textarea("newkey", undef, 8, 70) ],
		  [ 1, $text{'cert_cert1'},
		    &ui_upload("newkeyupload") ],
		  [ 2, $text{'cert_cert2'},
		    &ui_filebox("newkeyfile", $d->{'ssl_key'}, 70) ] ]));

# Passphrase on key
print &ui_table_row($text{'cert_pass'},
		    &ui_opt_textbox("pass", undef, 20, $text{'cert_nopass'}));

# CA cert
my $gotca = $d->{'ssl_chain'} && -r $d->{'ssl_chain'};
print &ui_table_row($text{'cert_newca'},
	&ui_radio_table("newca_mode",
		$gotca ? 3 : 4,
		[ $gotca ? ( [ 3, $text{'cert_newcakeep'} ] ) : ( ),
		  [ 4, $text{'cert_chain0'} ],
		  [ 0, $text{'cert_cert0'},
		    &ui_textarea("newca", undef, 8, 70) ],
		  [ 1, $text{'cert_cert1'},
		    &ui_upload("newcaupload") ],
		  [ 2, $text{'cert_cert2'},
		    &ui_filebox("newcafile", $d->{'ssl_chain'}, 70) ] ]));

print &ui_table_end();
print &ui_form_end([ [ "ok", $text{'cert_newok'} ] ]);
print &ui_tabs_end_tab();

# Let's encrypt tab
if (&can_edit_letsencrypt() && (&domain_has_website($d) || $d->{'dns'})) {
	&foreign_require("webmin");
	$err = &webmin::check_letsencrypt();
	print &ui_tabs_start_tab("mode", "lets");
	if ($err) {
		print $text{'cert_desc9'}.&ui_tag('p');
		print &ui_alert_box(&text('cert_elets', $err), 'warn',
			undef, undef, "");
		if (&master_admin() &&
		    defined(&webmin::get_letsencrypt_install_message)) {
			my $msg = &webmin::get_letsencrypt_install_message(
				"/$module_name/cert_form.cgi?dom=$d->{'id'}".
				"&mode=$in{'mode'}", $text{'cert_title'});
			print $msg,"<p>\n";
			}
		}
	else {
		$phd = &public_html_dir($d);
		my $catexts = &text('cert_acmedesc', "<tt>$phd</tt>");
		if  (defined(&can_acme_providers) && &can_acme_providers()) {
			$catexts .= &ui_tag('br').&text('cert_acmelink',
				'pro/edit_newacmes.cgi');
			}
		print &ui_details({
			class => 'inline',
			html => 1,
			title => $text{'cert_desc9'},
			content => $catexts });

		print &ui_form_start("letsencrypt.cgi");
		print &ui_hidden("dom", $in{'dom'});
		print &ui_table_start(undef, undef, 2);

		# Domain names to request cert for
		@defnames = &get_hostnames_for_ssl($d);
		$dis1 = &js_disable_inputs([ "dname" ], [ ], "onClick");
		$dis0 = &js_disable_inputs([ ], [ "dname" ], "onClick");
		$wildcb = "";
		&foreign_require("webmin");
		if ($webmin::letsencrypt_cmd && $d->{'dns'}) {
			$wildcb = "<br>".&ui_checkbox(
				"dwild", 1, $text{'cert_dwild'},
				$d->{'letsencrypt_dwild'});
			}
		print &ui_table_row($text{'cert_dnamefor'},
		    &ui_radio_table("dname_def", 
		      $d->{'letsencrypt_dname'} ? 0 : 1,
		      [ [ 1, $text{'cert_dnamedef'},
			  join("<br>\n", map { "<tt>$_</tt>" } @defnames),
			  $dis1 ],
		        [ 0, $text{'cert_dnamesel'},
			  &ui_textarea("dname",
			    join("\n", split(/\s+/, $d->{'letsencrypt_dname'})),
			     5, 60, undef, $d->{'letsencrypt_dname'} ? 0 : 1).
			  $wildcb, $dis0 ] ]));

		# SSL certificate provider
		if (defined(&list_acme_providers)) {
			print &ui_table_row($text{'cert_acmes'},
			&ui_select("acme", $d->{'letsencrypt_id'},
				[ map { [ $_->{'id'},
					  $_->{'desc'} || $_->{'issuer'} ] }
				      @provs ]));
			}
		else {
			print &ui_table_row($text{'cert_acmes'},
				$text{'acme_letsencrypt'});
			}

		# Setup automatic renewal?
		print &ui_table_row($text{'cert_letsrenew2'},
			&ui_yesno_radio("renew",
					$d->{'letsencrypt_renew'} ? 1 : 0));

		# Renewal email option
		my $mmsg = "";
		if (&master_admin() && $gconfig{'webmin_email_to'}) {
			$mmsg = "<br>".&ui_checkbox("email_master", 1,
					$text{'cert_letsemail_master'},
					$d->{'letsencrypt_email_master'} //
					  $tmpl->{'ssl_email_master'});
			}
		my  $email = $d->{'letsencrypt_email'} //
			     $tmpl->{'ssl_email'} // 0;
		print &ui_table_row($text{'cert_letsemail'},
			&ui_radio("email", $email,
				  [ [ 0, $text{'yes'} ],
				    [ 1, $text{'cert_letsemailerr'} ],
				    [ 2, $text{'no'} ] ]).$mmsg);

		# Test connectivity first?
		if (defined(&check_domain_connectivity)) {
			my $conn = $d->{'letsencrypt_connectivity'} //
				   $tmpl->{'ssl_connectivity'} // 1;
			print &ui_table_row($text{'cert_connectivity'},
				&ui_radio("connectivity", $conn,
				  [ [ 2, $text{'cert_connectivity2'} ],
				    [ 1, $text{'cert_connectivity1'} ],
				    [ 0, $text{'cert_connectivity0'} ] ]));
			}

		# Hostname filter mode
		my $filter = $d->{'letsencrypt_subset'} ? 2 :
			     !$d->{'letsencrypt_nodnscheck'} ? 1 : 0;
		print &ui_table_row($text{'cert_hostfilter'},
			&ui_radio("hostfilter", $filter,
				  [ [ 0, $text{'cert_hostfilter0'}."<br>" ],
				    [ 1, $text{'cert_hostfilter1'}."<br>" ],
				    [ 2, $text{'cert_hostfilter2'} ] ]));

		# Certificate type, if supported
		if (&letsencrypt_supports_ec()) {
			print &ui_table_row($text{'cert_hash'},
				&ui_select("ctype", $d->{'letsencrypt_ctype'},
					[ [ "rsa", $text{'cert_type_rsa'} ],
					  [ "ecdsa", $text{'cert_type_ec'} ] ]));
			}

		# Recent renewal details
		if ($d->{'letsencrypt_last'}) {
			$ago = (time() - $d->{'letsencrypt_last'}) /
			       (30*24*60*60);
			print &ui_table_row($text{'cert_letsage'},
				&text('cert_letsmonths', sprintf("%.2f",$ago)));
			}
		if ($d->{'letsencrypt_last_success'}) {
			print &ui_table_row($text{'cert_lets_success'},
				&make_date($d->{'letsencrypt_last_success'}));
			}
		if ($d->{'letsencrypt_last_failure'} &&
		    $d->{'letsencrypt_last_failure'} >
		      $d->{'letsencrypt_last_success'}) {
			print &ui_table_row($text{'cert_lets_failure'},
				"<font color=red>".
				&make_date($d->{'letsencrypt_last_failure'}).
				"</font>");

			if ($d->{'letsencrypt_last_err'}) {
				my $err = $d->{'letsencrypt_last_err'};
				$err =~ s/\t/\n/g;
				print &ui_table_row($text{'cert_lets_freason'},
					"<font color=red>".$err."</font>");
				}
			}

		print &ui_table_end();
		print &ui_form_end([ [ undef, $text{'cert_letsok'} ],
				     [ 'only', $text{'cert_letsonly'} ] ]);
		}
	print &ui_tabs_end_tab();
	}

print &ui_tabs_end(1);

# Make sure the left menu is showing this domain
if (defined(&theme_select_domain)) {
	&theme_select_domain($d);
	}

&ui_print_footer(&domain_footer_link($d),
		 "", $text{'index_return'});

# print_cert_fields(show-days)
sub print_cert_fields
{
my ($showdays) = @_;

print &ui_table_row($webmin::text{'ssl_cn'},
		    &ui_textbox("commonName", "www.$d->{'dom'}", 30));

$alts = join("\n", map { "www.".$_->{'dom'} } @others);
print &ui_table_row($text{'cert_alt'},
		    &ui_textarea("subjectAltName", $alts, 5, 30));

print &ui_table_row($webmin::text{'ca_email'},
		    &ui_textbox("emailAddress", $d->{'emailto_addr'}, 30));

print &ui_table_row($webmin::text{'ca_ou'},
		    &ui_textbox("organizationalUnitName", undef, 30));

print &ui_table_row($webmin::text{'ca_o'},
		    &ui_textbox("organizationName", $d->{'owner'}, 30));

print &ui_table_row($webmin::text{'ca_city'} || $text{'cert_city'},
		    &ui_textbox("cityName", undef, 30));

print &ui_table_row($webmin::text{'ca_sp'},
		    &ui_textbox("stateOrProvinceName", undef, 15));

print &ui_table_row($webmin::text{'ca_c'},
		    &ui_textbox("countryName", undef, 2));

$key_size = $tmpl->{'ssl_key_size'};
$key_size = undef if ($key_size == $webmin::default_key_size);
print &ui_table_row($webmin::text{'ssl_size'},
		    &ui_opt_textbox("size", $key_size, 6,
			    "$text{'default'} ($webmin::default_key_size)").
			" ".$text{'ssl_bits'});

if ($showdays) {
	print &ui_table_row($webmin::text{'ssl_days'},
			    &ui_textbox("days", 1825, 8));
	}

print &ui_table_row($text{'cert_hash'},
		    &ui_select("hash", $tmpl->{'ssl_cert_type'},
			       [ [ "sha1", "SHA1" ],
				 [ "sha2", "SHA2" ],
				 [ "ecdsa", $text{'cert_type_ec'} ] ]));
}
