#! PERL_COMMAND # This is a Perl script that reads an Exim run-time configuration file for # Exim 3. It makes what changes it can for Exim 4, and also output commentary # on what it has done, and on things it cannot do. # It is assumed that the input is a valid Exim 3 configuration file. use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; use Getopt::Long; use File::Basename; GetOptions( 'version' => sub { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $^V\n"; exit 0; }, ); # These are lists of main options which are abolished in Exim 4. # The first contains options that are used to construct new options. @skipped_options = ( "auth_hosts", "auth_over_tls_hosts", "errors_address", "headers_check_syntax", "headers_checks_fail", "headers_sender_verify", "headers_sender_verify_errmsg", "host_accept_relay", "host_auth_accept_relay", "host_reject_recipients", "local_domains", "local_domains_include_host", "local_domains_include_host_literals", "log_all_parents", "log_arguments", "log_incoming_port", "log_interface", "log_level", "log_received_sender", "log_received_recipients", "log_rewrites", "log_sender_on_delivery", "log_smtp_confirmation", "log_smtp_connections", "log_smtp_syntax_errors", "log_subject", "log_queue_run_level", "rbl_domains", "rbl_hosts", "rbl_reject_recipients", "receiver_verify", "receiver_verify_addresses", "receiver_verify_hosts", "receiver_verify_senders", "recipients_reject_except", "recipients_reject_except_senders", "relay_domains", "relay_domains_include_local_mx", "sender_address_relay", "sender_address_relay_hosts", "sender_reject_recipients", "sender_verify", "sender_verify_hosts_callback", "sender_verify_callback_domains", "sender_verify_callback_timeout", "sender_verify_hosts", "smtp_etrn_hosts", "smtp_expn_hosts", "smtp_verify", "tls_host_accept_relay", "tls_hosts", "tls_log_cipher", "tls_log_peerdn", "tls_verify_ciphers" ); # The second contains options that are completely abolished and have # no equivalent. @abolished_options = ( "always_bcc", "debug_level", "helo_strict_syntax", "kill_ip_options", "log_ip_options", "log_refused_recipients", "message_size_limit_count_recipients", "rbl_log_headers", "rbl_log_rcpt_count", "receiver_try_verify", "refuse_ip_options", "relay_match_host_or_sender", "sender_try_verify", "sender_verify_batch", "sender_verify_fixup", "sender_verify_reject", "sender_verify_max_retry_rate", ); # This is a list of options that are not otherwise handled, but which # contain domain or host lists that have to be processed so that any # regular expressions are marked "not for expansion". @list_options = ( "dns_again_means_nonexist", "hold_domains", "hosts_treat_as_local", "percent_hack_domains", "queue_smtp_domains", "helo_accept_junk_hosts", "host_lookup", "ignore_fromline_hosts", "rfc1413_hosts", "sender_unqualified_hosts", "smtp_reserve_hosts", "tls_advertise_hosts", "tls_verify_hosts", ); ################################################## # Output problem rubric once # ################################################## sub rubric { return if $rubric_output; $rubric_output = 1; print STDERR "\n" . "** The following comments describe problems that have been encountered\n" . " while converting an Exim 3 runtime file for Exim 4. More detail can\n" . " be found in the file doc/Exim4.upgrade.\n"; } ################################################## # Analyse one line # ################################################## sub checkline{ my($line) = $_[0]; return "comment" if $line =~ /^\s*(#|$)/; return "end" if $line =~ /^\s*end\s*$/i; # Macros are recognized only in the first section of the file. return "macro" if $prefix eq "" && $line =~ /^\s*[A-Z]/; # In retry and rewrite sections, the type is always "other" return "other" if $prefix eq "=retry" || $prefix eq "=rewrite"; # Pick out the name at the start and the rest of the line (into global # variables) and return whether the start of a driver or not. ($hide,$name,$rest) = $line =~ /^\s*(hide\s+|)([a-z0-9_]+)\s*(.*?)\s*$/; # If $rest begins with a colon, this is a driver name return "driver" if $rest =~ /^:/; # If $rest begins with an = the value of the option is given explicitly; # remove the = from the start. Turn "yes"/"no" into "true"/"false". if ($rest =~ /^=/) { $rest =~ s/^=\s*//; $rest = "true" if $rest eq "yes"; $rest = "false" if $rest eq "no"; } # Otherwise we have a boolean option. Set up a "true"/"false" value. else { if ($name =~ /^not?_/) # Recognize "no_" or "not_" { $rest = "false"; $name =~ s/^not?_//; } else { $rest = "true"; } } return "option"; } ################################################## # Negate a list of things # ################################################## # Can be tricky, because there may be comment lines in the list. # Also, lists may have different delimiters. sub negate { my($list) = $_[0]; my($delim) = ":"; my($leadin) = ""; return $list if ! defined $list; ($list) = $list =~ /^"?(.*?)"?\s*$/s; # Remove surrounding quotes $list =~ s/\\\s*\n\s*//g; # Remove continuation markers if ($list =~ /^(\s*<(\S)\s*)(.*)/s) { $leadin = $1; $delim = $2; $list = $3; } $list =~ s/^\s+//; $list =~ s/\Q$delim$delim/>%%%%%%%%%%%%%%%%%%%%%%%%); $clen = scalar @c; # Remove the standard comment that appears at the end of the default if ($clen > 0 && $c[$clen-1] =~ /^#\s*End of Exim configuration file\s*/i) { pop @c; $clen--; } # The first pass over the input fishes out all the options settings in the # main, transport, director, and router sections, and places their values in # associative arrays. It also notes the starting position of all the sections. $prefix = ""; %main = (); $hash = \%main; for ($i = 0; $i < $clen; $i++) { # Change references to +allow_unknown and +warn_unknown into +include_unknown if ($c[$i] =~ /\+(?:allow|warn)_unknown/) { if (!$unk_output) { &rubric(); print STDERR "\n" . "** You have used '+allow_unknown' or '+warn_unknown' in a configuration\n" . " option. This has been converted to '+include_unknown', but the action\n" . " is different in Exim 4, so you should review all the relevant options.\n"; $unk_output = 1; } $c[$i] =~ s/\+(?:allow|warn)_unknown/+include_unknown/g; } # Any reference to $errmsg_recipient is changed to $bounce_recipient if ($c[$i] =~ /\$errmsg_recipient/) { if (!$errmsg_output) { &rubric(); print STDERR "\n" . "** References to \$errmsg_recipient have been changed to \$bounce_recipient\n"; $errmsg_output = 1; } $c[$i] =~ s/\$errmsg_recipient/\$bounce_recipient/g; } # Analyse the type of line $type = &checkline($c[$i]); next if $type eq "comment"; # Output a warning if $key is used if ($c[$i] =~ /\$key/ && !$key_output) { &rubric(); print STDERR "\n" . "** You have used '\$key' in a configuration option. This variable does not\n" . " exist in Exim 4. Instead, the value you need for your lookup will be\n" . " in one of the other variables such as '\$domain' or '\$host'. You will\n" . " need to edit the new configuration to sort this out.\n"; $key_output = 1; } # Save macro definitions so we can output them first; must handle # continuations. if ($type eq "macro") { $macro_output .= "$c[$i++]\n" while $c[$i] =~ /\\\s*$|^\s*#/; $macro_output .= "$c[$i]\n"; } # Handle end of section elsif ($type eq "end") { if ($prefix eq "=rewrite") { $prefix = "a."; $auth_start = $i + 1; last; } elsif ($prefix eq "=retry") { $prefix = "=rewrite"; $rewrite_start = $i + 1; } elsif ($prefix eq "r.") { $prefix = "=retry"; $retry_start = $i + 1; } elsif ($prefix eq "d.") { $prefix = "r."; $router_start = $i + 1; } elsif ($prefix eq "t.") { $prefix = "d."; $director_start = $i + 1; } elsif ($prefix eq "") { $prefix = "t."; $transport_start = $i + 1; } } # Handle start of a new director, router or transport driver elsif ($type eq "driver" && $prefix !~ /^=/) { $hash = {}; if (defined $driverlist{"$prefix$name"}) { die "*** There are two drivers with the name \"$name\"\n"; } $driverlist{"$prefix$name"} = $hash; $first_director = $name if !defined $first_director && $prefix eq "d."; } # Handle definition of an option; we must pull in any continuation # strings, and save the value in the current hash. Note if the option # is hidden. elsif ($type eq "option") { my($nextline) = ""; while ($i < $clen - 1 && ($rest =~ /\\\s*$/s || $nextline =~ /^\s*#/)) { $nextline = $c[++$i]; $rest .= "\n$nextline"; } $$hash{$name} = $rest; $$hash{"$name-hide"} = 1 if $hide ne ""; } } # Generate the new configuration. Start with a warning rubric. print STDOUT "#!!# This file is output from the convert4r4 script, which tries\n"; print STDOUT "#!!# to convert Exim 3 configurations into Exim 4 configurations.\n"; print STDOUT "#!!# However, it is not perfect, especially with non-simple\n"; print STDOUT "#!!# configurations. You must check it before running it.\n"; print STDOUT "\n\n"; # Output the macro definitions if ($macro_output ne "") { print STDOUT "#!!# All macro definitions have been gathered here to ensure\n"; print STDOUT "#!!# they precede any references to them.\n\n"; print STDOUT "$macro_output\n"; } # Output some default pointers to ACLs for RCPT and DATA time. If no Exim 3 # options that apply are set, non-restricting ACLs are generated. print STDOUT "#!!# These options specify the Access Control Lists (ACLs) that\n"; print STDOUT "#!!# are used for incoming SMTP messages - after the RCPT and DATA\n"; print STDOUT "#!!# commands, respectively.\n\n"; print STDOUT "acl_smtp_rcpt = check_recipient\n"; print STDOUT "acl_smtp_data = check_message\n\n"; if (defined $main{"auth_over_tls_hosts"}) { print STDOUT "#!!# This option specifies the Access Control List (ACL) that\n"; print STDOUT "#!!# is used after an AUTH command.\n\n"; print STDOUT "acl_smtp_auth = check_auth\n\n"; } if (&bool("smtp_verify") || defined $main{"smtp_etrn_hosts"} || defined $main{"smtp_expn_hosts"}) { print STDOUT "#!!# These options specify the Access Control Lists (ACLs) that\n"; print STDOUT "#!!# are used to control the ETRN, EXPN, and VRFY commands.\n"; print STDOUT "#!!# Where no ACL is defined, the command is locked out.\n\n"; print STDOUT "acl_smtp_etrn = check_etrn\n" if defined $main{"smtp_etrn_hosts"}; print STDOUT "acl_smtp_expn = check_expn\n" if defined $main{"smtp_expn_hosts"}; print STDOUT "acl_smtp_vrfy = check_vrfy\n" if &bool("smtp_verify"); print STDOUT "\n"; } # If local_domains was set, get its value; otherwise set to "@". Add into it # appropriate magic for local_domains_include_host[_literals]. $local_domains = (defined $main{"local_domains"})? $main{"local_domains"} : "@"; $ldsep = ":"; if ($local_domains =~ /^\s*<(.)\s*(.*)/s) { $ldsep = $1; $local_domains = $2; } $local_domains = "\@[] $ldsep " . $local_domains if defined $main{"local_domains_include_host_literals"} && $main{"local_domains_include_host_literals"} eq "true"; $local_domains = "\@ $ldsep " . $local_domains if defined $main{"local_domains_include_host"} && $main{"local_domains_include_host"} eq "true"; $local_domains = "<$ldsep " . $local_domains if $ldsep ne ":"; # Output a domain list setting for these domains, provided something is defined if ($local_domains !~ /^\s*$/) { print STDOUT "#!!# This setting defines a named domain list called\n"; print STDOUT "#!!# local_domains, created from the old options that\n"; print STDOUT "#!!# referred to local domains. It will be referenced\n"; print STDOUT "#!!# later on by the syntax \"+local_domains\".\n"; print STDOUT "#!!# Other domain and host lists may follow.\n\n"; printf STDOUT ("domainlist local_domains = %s\n\n", &no_expand_regex($local_domains)); } $relay_domains = (defined $main{"relay_domains"})? $main{"relay_domains"} : ""; $ldsep = ":"; if ($relay_domains =~ /^\s*<(.)\s*(.*)/s) { $ldsep = $1; } if (defined $main{"relay_domains_include_local_mx"}) { $relay_domains .= ($relay_domains =~ /^\s*$/)? "\@mx_any" : " $ldsep \@mx_any"; } printf STDOUT ("domainlist relay_domains = %s\n", &no_expand_regex($relay_domains)) if $relay_domains !~ /^\s*$/; # If ignore_errmsg_errors is set, we are going to force 0s as the value # for ignore_errmsg_errors_after, so arrange to skip any other value. push @skipped_options, "ignore_errmsg_errors_after" if &bool("ignore_errmsg_errors"); # If rbl_domains is set, split it up and generate six lists: # rbl_warn_domains, rbl_warn_domains_skiprelay # rbl_reject_domains, rbl_reject_domains_skiprelay # rbl_accept_domains, rbl_accept_domains_skiprelay if (defined $main{"rbl_domains"}) { my($s) = &unquote($main{"rbl_domains"}); $s =~ s/\s*\\\s*\n\s*/ /g; my(@list) = split /\s*:\s*/, $s; foreach $d (@list) { my(@sublist) = split /\//, $d; my($name) = shift @sublist; my($warn) = 0; if (defined $main{"rbl_reject_recipients"}) { $warn = $main{"rbl_reject_recipients"} ne "true"; } foreach $o (@sublist) { $warn = 1 if $o eq "warn"; $warn = 0 if $o eq "reject"; $warn = 2 if $o eq "accept"; $skiprelay = 1 if $o eq "skiprelay"; } if ($skiprelay) { if ($warn == 0) { $rbl_reject_skiprelay .= ((defined $rbl_reject_skiprelay)? ":":"").$name; } elsif ($warn == 1) { $rbl_warn_skiprelay .= ((defined $rbl_warn_skiprelay)? ":":"").$name; } elsif ($warn == 2) { $rbl_accept_skiprelay .= ((defined $rbl_accept_skiprelay)? ":":"").$name; } } else { if ($warn == 0) { $rbl_reject_domains .= ((defined $rbl_reject_domains)? ":":"").$name; } elsif ($warn == 1) { $rbl_warn_domains .= ((defined $rbl_warn_domains)? ":":"").$name; } elsif ($warn == 2) { $rbl_accept_domains .= ((defined $rbl_accept_domains)? ":":"").$name; } } } } # Output host list settings printf STDOUT ("hostlist auth_hosts = %s\n", &no_expand_regex($main{"auth_hosts"})) if defined $main{"auth_hosts"}; printf STDOUT ("hostlist rbl_hosts = %s\n", &no_expand_regex($main{"rbl_hosts"})) if defined $main{"rbl_hosts"}; printf STDOUT ("hostlist relay_hosts = %s\n", &no_expand_regex($main{"host_accept_relay"})) if defined $main{"host_accept_relay"}; printf STDOUT ("hostlist auth_relay_hosts = %s\n", &no_expand_regex($main{"host_auth_accept_relay"})) if defined $main{"host_auth_accept_relay"}; printf STDOUT ("hostlist auth_over_tls_hosts = %s\n", &no_expand_regex($main{"auth_over_tls_hosts"})) if defined $main{"auth_over_tls_hosts"}; printf STDOUT ("hostlist tls_hosts = %s\n", &no_expand_regex($main{"tls_hosts"})) if defined $main{"tls_hosts"}; printf STDOUT ("hostlist tls_relay_hosts = %s\n", &no_expand_regex($main{"tls_host_accept_relay"})) if defined $main{"tls_host_accept_relay"}; print STDOUT "\n"; # Convert various logging options $log_selector = ""; $sep = " \\\n "; if (defined $main{"log_level"}) { my($level) = $main{"log_level"}; $log_selector .= "$sep -retry_defer$sep -skip_delivery" if $level < 5; $log_selector .= "$sep -lost_incoming_connection$sep -smtp_syntax_error" . "$sep -delay_delivery" if $level < 4; $log_selector .= "$sep -size_reject" if $level < 2; } $log_selector .= "$sep -queue_run" if defined $main{"log_queue_run_level"} && defined $main{"log_level"} && $main{"log_queue_run_level"} > $main{"log_level"}; $log_selector .= "$sep +address_rewrite" if &bool("log_rewrites"); $log_selector .= "$sep +all_parents" if &bool("log_all_parents"); $log_selector .= "$sep +arguments" if &bool("log_arguments"); $log_selector .= "$sep +incoming_port" if &bool("log_incoming_port"); $log_selector .= "$sep +incoming_interface" if &bool("log_interface"); $log_selector .= "$sep +received_sender" if &bool("log_received_sender"); $log_selector .= "$sep +received_recipients" if &bool("log_received_recipients"); $log_selector .= "$sep +sender_on_delivery" if &bool("log_sender_on_delivery"); $log_selector .= "$sep +smtp_confirmation" if &bool("log_smtp_confirmation"); $log_selector .= "$sep +smtp_connection" if &bool("log_smtp_connections"); $log_selector .= "$sep +smtp_syntax_error" if &bool("log_smtp_syntax_errors"); $log_selector .= "$sep +subject" if &bool("log_subject"); $log_selector .= "$sep +tls_cipher" if &bool("tls_log_cipher"); $log_selector .= "$sep +tls_peerdn" if &bool("tls_log_peerdn"); if ($log_selector ne "") { print STDOUT "#!!# All previous logging options are combined into a single\n" . "#!!# option in Exim 4. This setting is an approximation to\n" . "#!!# the previous state - some logging has changed.\n\n"; print STDOUT "log_selector = $log_selector\n\n"; } # If deliver_load_max is set, replace it with queue_only_load (taking the # lower value if both set) and also set deliver_queue_load_max if it is # not already set. When scanning for output, deliver_load_max is skipped. if (defined $main{"deliver_load_max"}) { &rubric(); print STDERR "\n" . "** deliver_load_max is abolished in Exim 4.\n"; if (defined $main{"queue_only_load"}) { $queue_only_load_was_present = 1; if ($main{"queue_only_load"} < $main{"deliver_load_max"}) { print STDERR " As queue_only_load was set lower, deliver_load_max is just removed.\n"; } else { print STDERR " As queue_only_load was set higher, it's value has been replaced by\n" . " the value of deliver_load_max.\n"; $main{"queue_only_load"} = $main{"deliver_load_max"}; } } else { print STDERR " queue_only_load has been set to the load value.\n"; $main{"queue_only_load"} = $main{"deliver_load_max"}; } if (!defined $main{"deliver_queue_load_max"}) { print STDERR " deliver_queue_load_max has been set to the value of queue_only_load.\n"; $main{"deliver_queue_load_max"} = $main{"queue_only_load"}; } else { $deliver_queue_load_max_was_present = 1; } } # Now we scan through the various parts of the file again, making changes # as necessary. # -------- The main configuration -------- $prefix = ""; MainLine: for ($i = 0; $i < $clen; $i++) { my($nextline) = ""; $type = &checkline($c[$i]); last if $type eq "end"; if ($type eq "macro") { $i++ while $c[$i] =~ /\\\s*$|^\s*#/; next; } if ($type eq "comment") { print STDOUT "$c[$i]\n"; next; } # Collect any continuation lines for an option setting while ($rest =~ /\\\s*$/s || $nextline =~ /^\s*#/) { $nextline = $c[++$i]; $rest .= "\n$nextline"; } $rest =~ s/^=\s*//; # Deal with main options that are skipped (they are used in other # options in other places). for $skipped (@skipped_options) { next MainLine if $name eq $skipped; } # Deal with main options that are totally abolished for $abolished (@abolished_options) { if ($name eq $abolished) { &rubric(); print STDERR "\n" . "** The $name option no longer exists, and has no equivalent\n" . " in Exim 4.\n"; next MainLine; } } # There is a special case for rbl_warn_header if ($name eq "rbl_warn_header") { &rubric(); print STDERR "\n" . "** The $name option no longer exists. In Exim 4 you can achieve the\n" . " effect by adding a suitable \"message\" statement in the ACL.\n"; } # There is a special case for sender_reject and host_reject elsif ($name eq "sender_reject" || $name eq "host_reject") { &rubric(); print STDERR "\n" . "** The $name option no longer exists. Its data has been used in\n" . " an Access Control List as if it were in ${name}_recipients.\n"; } # And a special message for prohibition_message elsif ($name eq "prohibition_message") { &rubric(); print STDERR "\n" . "** The prohibition_message option no longer exists. The facility is\n" . " provided in a different way in Exim 4, via the \"message\" keyword\n" . " in Access Control Lists. It isn't possible to do an automatic conversion,\n" . " so the value of prohibition_message has been ignored. You will have to\n" . " modify the ACLs if you want to reinstate the feature.\n"; } # auth_always_advertise gets converted to auth_advertise_hosts elsif ($name eq "auth_always_advertise") { print STDOUT "#!!# auth_always_advertise converted to auth_advertise_hosts\n"; if (&bool("auth_always_advertise")) { print STDOUT "auth_advertise_hosts = *\n"; } else { $sep = ""; print STDOUT "auth_advertise_hosts ="; if (defined $main{"auth_hosts"}) { print STDOUT "$sep +auth_hosts"; $sep = " :"; } if (defined $main{"host_accept_relay"}) { print STDOUT "$sep !+relay_hosts"; $sep = " :"; } if (defined $main{"host_auth_accept_relay"}) { print STDOUT "$sep +auth_relay_hosts"; } print STDOUT "\n"; } } # Deal with main options that have to be rewritten elsif ($name eq "accept_timeout") { print STDOUT "#!!# accept_timeout renamed receive_timeout\n"; print STDOUT "receive_timeout = $rest\n"; } elsif ($name eq "collapse_source_routes") { print STDOUT "#!!# collapse_source_routes removed\n"; print STDOUT "#!!# It has been a no-op since 3.10.\n"; } elsif ($name eq "daemon_smtp_service") { print STDOUT "#!!# daemon_smtp_service renamed daemon_smtp_port\n"; print STDOUT "daemon_smtp_port = $rest\n"; } elsif ($name eq "dns_check_names" || $name eq "dns_check_names_pattern") { if (!$done_dns_check_names) { if (&bool("dns_check_names")) { if (defined $main{"dns_check_names_pattern"}) { &outopt(\%main, "dns_check_names_pattern", 0); } } else { print STDOUT "#!!# dns_check_names has been abolished\n"; print STDOUT "#!!# setting dns_check_pattern empty to turn off check\n"; print STDOUT "dns_check_names_pattern =\n"; } $done_dns_check_names = 1; } } elsif ($name eq "deliver_load_max") { print STDOUT "deliver_queue_load_max = $main{'deliver_queue_load_max'}\n" if !$deliver_queue_load_max_was_present; print STDOUT "queue_only_load = $main{'queue_only_load'}\n" if !$queue_only_load_was_present; } elsif ($name eq "errmsg_file") { print STDOUT "#!!# errmsg_file renamed bounce_message_file\n"; print STDOUT "bounce_message_file = $rest\n"; } elsif ($name eq "errmsg_text") { print STDOUT "#!!# errmsg_text renamed bounce_message_text\n"; print STDOUT "bounce_message_text = $rest\n"; } elsif ($name eq "forbid_domain_literals") { print STDOUT "#!!# forbid_domain_literals replaced by allow_domain_literals\n"; print STDOUT "allow_domain_literals = ", &bool("forbid_domain_literals")? "false" : "true", "\n"; } elsif ($name eq "freeze_tell_mailmaster") { print STDOUT "#!!# freeze_tell_mailmaster replaced by freeze_tell\n"; if (&bool("freeze_tell_mailmaster")) { print STDOUT "freeze_tell = ", ((defined $main{"errors_address"})? $main{"errors_address"} : "postmaster"), "\n"; } else { print STDOUT "#!!# freeze_tell is unset by default\n"; } } elsif ($name eq "helo_verify") { print STDOUT "#!!# helo_verify renamed helo_verify_hosts\n"; printf STDOUT ("helo_verify_hosts = %s\n", &no_expand_regex($rest)); } elsif ($name eq "ignore_errmsg_errors") { print STDOUT "ignore_bounce_errors_after = 0s\n"; } elsif ($name eq "ignore_errmsg_errors_after") { print STDOUT "#!!# ignore_errmsg_errors_after renamed ignore_bounce_errors_after\n"; print STDOUT "ignore_bounce_errors_after = $rest\n"; } elsif ($name eq "ipv4_address_lookup" || $name eq "dns_ipv4_lookup") { print STDOUT "#!!# $name changed to dns_ipv4_lookup\n" if $name eq "ipv4_address_lookup"; print STDOUT "#!!# dns_ipv4_lookup is now a domain list\n"; if (&bool($name)) { print STDOUT "dns_ipv4_lookup = *\n"; } else { print STDOUT "#!!# default for dns_ipv4_lookup is unset\n"; } } elsif ($name eq "locally_caseless") { print STDOUT "#!!# locally_caseless removed\n"; print STDOUT "#!!# caseful_local_part will be added to ex-directors\n"; $add_caseful_local_part = 1; } elsif ($name eq "message_filter_directory2_transport") { print STDOUT "#!!# message_filter_directory2_transport removed\n"; } elsif ($name =~ /^message_filter(.*)/) { print STDOUT "#!!# $name renamed system_filter$1\n"; print STDOUT "system_filter$1 = $rest\n"; } elsif ($name eq "queue_remote_domains") { print STDOUT "#!!# queue_remote_domains renamed queue_domains\n"; printf STDOUT ("queue_domains = %s\n", &no_expand_regex($rest)); } elsif ($name eq "receiver_unqualified_hosts") { print STDOUT "#!!# receiver_unqualified_hosts renamed recipient_unqualified_hosts\n"; printf STDOUT ("recipient_unqualified_hosts = %s\n", &no_expand_regex($rest)); } elsif ($name eq "remote_sort") { print STDOUT "#!!# remote_sort renamed remote_sort_domains\n"; printf STDOUT ("remote_sort_domains = %s\n", &no_expand_regex($rest)); } elsif ($name eq "security") { if ($rest eq "unprivileged") { print STDOUT "#!!# security=unprivileged changed to deliver_drop_privilege\n"; print STDOUT "deliver_drop_privilege\n"; } else { &rubric(); print STDERR "\n" . "** The 'security' option no longer exists.\n"; } } elsif ($name eq "timestamps_utc") { print STDOUT "#!!# timestamps_utc changed to use timezone\n"; print STDOUT "timezone = utc\n"; } elsif ($name eq "untrusted_set_sender") { print STDOUT "#!!# untrusted_set_sender is now a list of what can be set\n"; print STDOUT "#!!# The default is an empty list.\n"; if (&bool("untrusted_set_sender")) { print STDOUT "untrusted_set_sender = *\n"; } } elsif ($name eq "warnmsg_file") { print STDOUT "#!!# warnmsg_file renamed warn_message_file\n"; print STDOUT "warn_message_file = $rest\n"; } # Remaining options just get copied unless they are one of those that's # a list where any regular expressions have to be escaped. else { my($no_expand) = 0; foreach $o (@list_options) { if ($name eq $o) { $no_expand = 1; last; } } &outopt(\%main, $name, $no_expand); } } # -------- The ACL configuration -------- print STDOUT "\n"; print STDOUT "#!!#######################################################!!#\n"; print STDOUT "#!!# This new section of the configuration contains ACLs #!!#\n"; print STDOUT "#!!# (Access Control Lists) derived from the Exim 3 #!!#\n"; print STDOUT "#!!# policy control options. #!!#\n"; print STDOUT "#!!#######################################################!!#\n"; print STDOUT "\n"; print STDOUT "#!!# These ACLs are crudely constructed from Exim 3 options.\n"; print STDOUT "#!!# They are almost certainly not optimal. You should study\n"; print STDOUT "#!!# them and rewrite as necessary.\n"; print STDOUT "\nbegin acl\n\n"; # Output an ACL for use after the RCPT command. This combines all the previous # policy checking options. print STDOUT "#!!# ACL that is used after the RCPT command\n"; print STDOUT "check_recipient:\n"; print STDOUT " # Exim 3 had no checking on -bs messages, so for compatibility\n"; print STDOUT " # we accept if the source is local SMTP (i.e. not over TCP/IP).\n"; print STDOUT " # We do this by testing for an empty sending host field.\n"; print STDOUT " accept hosts = :\n"; if (defined $main{"tls_verify_ciphers"}) { print STDOUT " deny "; print STDOUT "hosts = $main{'tls_verify_hosts'}\n " if defined $main{"tls_verify_hosts"}; print STDOUT " encrypted = *\n "; print STDOUT "!encrypted = $main{'tls_verify_ciphers'}\n"; } print STDOUT " deny hosts = +auth_hosts\n" . " message = authentication required\n" . " !authenticated = *\n" if defined $main{"auth_hosts"}; print STDOUT " deny hosts = +tls_hosts\n" . " message = encryption required\n" . " !encrypted = *\n" if defined $main{"tls_hosts"}; printf STDOUT (" accept recipients = %s\n", &acl_quote(&sort_address_list($main{"recipients_reject_except"}, "recipients_reject_except"))) if defined $main{"recipients_reject_except"}; printf STDOUT (" accept senders = %s\n", &acl_quote(&sort_address_list($main{"recipients_reject_except_senders"}, "recipients_reject_except_senders"))) if defined $main{"recipients_reject_except_senders"}; printf STDOUT (" deny hosts = %s\n", &acl_quote($main{"host_reject"})) if defined $main{"host_reject"}; printf STDOUT (" deny hosts = %s\n", &acl_quote($main{"host_reject_recipients"})) if defined $main{"host_reject_recipients"}; if (defined $main{"rbl_domains"}) { my($msg) = "message = host is listed in \$dnslist_domain\n "; my($hlist) = (defined $main{"rbl_hosts"})? "hosts = +rbl_hosts\n " : ""; print STDOUT " accept ${hlist}dnslists = $rbl_accept_domains\n" if defined $rbl_accept_domains; print STDOUT " deny ${hlist}${msg}dnslists = $rbl_reject_domains\n" if defined $rbl_reject_domains; print STDOUT " warn ${hlist}" . "message = X-Warning: \$sender_host_address is listed at \$dnslist_domain\n" . " dnslists = $rbl_warn_domains\n" if defined $rbl_warn_domains; if (defined $main{"host_accept_relay"}) { $hlist .= "hosts = !+relay_hosts\n "; print STDOUT " accept ${hlist}dnslists = $rbl_accept_skiprelay\n" if defined $rbl_accept_skiprelay; print STDOUT " deny ${hlist}${msg}dnslists = $rbl_reject_skiprelay\n" if defined $rbl_reject_skiprelay; print STDOUT " warn ${hlist}" . "message = X-Warning: \$sender_host_address is listed at \$dnslist_domain\n" . " dnslists = $rbl_warn_skiprelay\n" if defined $rbl_warn_skiprelay; } } printf STDOUT (" deny senders = %s\n", &acl_quote(&sort_address_list($main{"sender_reject"}, "sender_reject"))) if defined $main{"sender_reject"}; printf STDOUT (" deny senders = %s\n", &acl_quote(&sort_address_list($main{"sender_reject_recipients"}, "sender_reject_recipients"))) if defined $main{"sender_reject_recipients"}; if (&bool("sender_verify")) { if (defined $main{"sender_verify_hosts_callback"} && defined $main{"sender_verify_callback_domains"}) { printf STDOUT (" deny hosts = %s\n", &acl_quote($main{"sender_verify_hosts_callback"})); printf STDOUT (" sender_domains = %s\n", &acl_quote($main{"sender_verify_callback_domains"})); print STDOUT " !verify = sender/callout"; print STDOUT "=$main{\"sender_verify_callback_timeout\"}" if defined $main{"sender_verify_callback_timeout"}; print STDOUT "\n"; } if (defined $main{"sender_verify_hosts"}) { printf STDOUT (" deny hosts = %s\n", &acl_quote($main{"sender_verify_hosts"})); print STDOUT " !verify = sender\n"; } else { print STDOUT " require verify = sender\n"; } } if (&bool("receiver_verify")) { print STDOUT " deny message = unrouteable address\n"; printf STDOUT (" recipients = %s\n", &acl_quote(&sort_address_list($main{"receiver_verify_addresses"}, "receiver_verify_addresses"))) if defined $main{"receiver_verify_addresses"}; printf STDOUT (" hosts = %s\n", &acl_quote($main{"receiver_verify_hosts"})) if defined $main{"receiver_verify_hosts"}; printf STDOUT (" senders = %s\n", &acl_quote(&sort_address_list($main{"receiver_verify_senders"}, "receiver_verify_senders"))) if defined $main{"receiver_verify_senders"}; print STDOUT " !verify = recipient\n"; } print STDOUT " accept domains = +local_domains\n" if $local_domains !~ /^\s*$/; print STDOUT " accept domains = +relay_domains\n" if $relay_domains !~ /^\s*$/; if (defined $main{"host_accept_relay"}) { if (defined $main{"sender_address_relay"}) { if (defined $main{"sender_address_relay_hosts"}) { printf STDOUT (" accept hosts = %s\n", &acl_quote($main{"sender_address_relay_hosts"})); print STDOUT " endpass\n"; print STDOUT " message = invalid sender\n"; printf STDOUT (" senders = %s\n", &acl_quote(&sort_address_list($main{"sender_address_relay"}, "sender_address_relay"))); print STDOUT " accept hosts = +relay_hosts\n"; } else { print STDOUT " accept hosts = +relay_hosts\n"; print STDOUT " endpass\n"; print STDOUT " message = invalid sender\n"; printf STDOUT (" senders = %s\n", &acl_quote(&sort_address_list($main{"sender_address_relay"}, "sender_address_relay"))); } } else { print STDOUT " accept hosts = +relay_hosts\n"; } } print STDOUT " accept hosts = +auth_relay_hosts\n" . " endpass\n" . " message = authentication required\n" . " authenticated = *\n" if defined $main{"host_auth_accept_relay"}; print STDOUT " accept hosts = +tls_relay_hosts\n" . " endpass\n" . " message = encryption required\n" . " encrypted = *\n" if defined $main{"tls_host_accept_relay"}; print STDOUT " deny message = relay not permitted\n\n"; # Output an ACL for use after the DATA command. This is concerned with # header checking. print STDOUT "#!!# ACL that is used after the DATA command\n"; print STDOUT "check_message:\n"; # Default for headers_checks_fail is true if (!defined $main{"headers_checks_fail"} || $main{"headers_checks_fail"} eq "true") { print STDOUT " require verify = header_syntax\n" if &bool("headers_check_syntax"); print STDOUT " require verify = header_sender\n" if &bool("headers_sender_verify"); print STDOUT " accept senders = !:\n require verify = header_sender\n" if &bool("headers_sender_verify_errmsg"); } else { print STDOUT " warn !verify = header_syntax\n" if &bool("headers_check_syntax"); print STDOUT " warn !verify = header_sender\n" if &bool("headers_sender_verify"); print STDOUT " accept senders = !:\n warn !verify = header_sender\n" if &bool("headers_sender_verify_errmsg"); } print STDOUT " accept\n\n"; # Output an ACL for AUTH if required if (defined $main{"auth_over_tls_hosts"}) { print STDOUT "#!!# ACL that is used after the AUTH command\n" . "check_auth:\n" . " accept hosts = +auth_over_tls_hosts\n" . " endpass\n" . " message = STARTTLS required before AUTH\n" . " encrypted = *\n" . " accept\n"; } # Output ACLs for ETRN, EXPN, and VRFY if required if (defined $main{"smtp_etrn_hosts"}) { print STDOUT "#!!# ACL that is used after the ETRN command\n" . "check_etrn:\n"; print STDOUT " deny hosts = +auth_hosts\n" . " message = authentication required\n" . " !authenticated = *\n" if defined $main{"auth_hosts"}; print STDOUT " accept hosts = $main{\"smtp_etrn_hosts\"}\n\n"; } if (defined $main{"smtp_expn_hosts"}) { print STDOUT "#!!# ACL that is used after the EXPN command\n" . "check_expn:\n"; print STDOUT " deny hosts = +auth_hosts\n" . " message = authentication required\n" . " !authenticated = *\n" if defined $main{"auth_hosts"}; print STDOUT " accept hosts = $main{\"smtp_expn_hosts\"}\n\n"; } if (&bool("smtp_verify")) { print STDOUT "#!!# ACL that is used after the VRFY command\n" . "check_vrfy:\n"; print STDOUT " deny hosts = +auth_hosts\n" . " message = authentication required\n" . " !authenticated = *\n" if defined $main{"auth_hosts"}; print STDOUT " accept\n\n"; } # -------- The authenticators -------- $started = 0; for ($i = $auth_start; $i < $clen; $i++) { if (!$started) { if ($c[$i] !~ /^\s*(#|$)/) { print STDOUT "\nbegin authenticators\n\n"; $started = 1; } } print STDOUT "$c[$i]\n"; } # -------- Rewrite section -------- $started = 0; for ($i = $rewrite_start; $i < $clen && $i < $auth_start - 1; $i++) { if (!$started) { if ($c[$i] !~ /^\s*(#|$)/) { print STDOUT "\nbegin rewrite\n\n"; $started = 1; } } &print_no_expand($c[$i]); } # -------- The routers configuration -------- # The new routers configuration is created out of the old directors and routers # configuration. We put the old routers first, adding a "domains" option to # any that don't have one, to make them select the domains that do not match # the original local_domains. The routers get modified as necessary, and the # final one has "no_more" set, unless it has conditions. In that case we have # to add an extra router to be sure of failing all non-local addresses that # fall through. We do this also if there are no routers at all. The old # directors follow, modified as required. $prefix = "r."; undef @comments; print STDOUT "\n"; print STDOUT "#!!#######################################################!!#\n"; print STDOUT "#!!# Here follow routers created from the old routers, #!!#\n"; print STDOUT "#!!# for handling non-local domains. #!!#\n"; print STDOUT "#!!#######################################################!!#\n"; print STDOUT "\nbegin routers\n\n"; for ($i = $router_start; $i < $clen; $i++) { $type = &checkline($c[$i]); last if $type eq "end"; if ($type eq "comment") { push(@comments, "$c[$i]\n"); next; } # When we hit the start of a driver, modify its options as necessary, # and then output it from the stored option settings, having first output # and previous comments. if ($type eq "driver") { print STDOUT shift @comments while scalar(@comments) > 0; $hash = $driverlist{"$prefix$name"}; $driver = $$hash{"driver"}; print STDOUT "$name:\n"; $add_no_more = ! defined $$hash{"domains"} && ! defined $$hash{"local_parts"} && ! defined $$hash{"senders"} && ! defined $$hash{"condition"} && ! defined $$hash{"require_files"} && (!defined $$hash{"verify_only"} || $$hash{"verify_only"} eq "false") && (!defined $$hash{"verify"} || $$hash{"verify"} eq "true"); # Create a "domains" setting if there isn't one, unless local domains # was explicitly empty. $$hash{"domains"} = "! +local_domains" if !defined $$hash{"domains"} && $local_domains !~ /^\s*$/; # If the router had a local_parts setting, add caseful_local_part $$hash{"caseful_local_part"} = "true" if defined $$hash{"local_parts"}; # If the router has "self=local" set, change it to "self=pass", and # set pass_router to the router that was the first director. Change the # obsolete self settings of "fail_hard" and "fail_soft" to "fail" and # "pass". if (defined $$hash{"self"}) { if ($$hash{"self"} eq "local") { $$hash{"self"} = "pass"; $$hash{"pass_router"} = $first_director; } elsif ($$hash{"self"} eq "fail_hard") { $$hash{"self"} = "fail"; } elsif ($$hash{"self"} eq "fail_soft") { $$hash{"self"} = "pass"; } } # If the router had a require_files setting, check it for user names # and colons that are part of expansion items if (defined $$hash{"require_files"}) { &check_require($$hash{"require_files"}, "'$name' router"); if (($$hash{"require_files"} =~ s/(\$\{\w+):/$1::/g) > 0 || ($$hash{"require_files"} =~ s/ldap:/ldap::/g) > 0) { &rubric(); print STDERR "\n" . "*** A setting of require_files in the $name router contains\n" . " a colon in what appears to be an expansion item. In Exim 3, the\n" . " whole string was expanded before splitting the list, but in Exim 4\n" . " each item is expanded separately, so colons that are not list\n" . " item separators have to be doubled. One or more such colons in this\n" . " list have been doubled as a precaution. Please check the result.\n"; } } # If the router had a "senders" setting, munge the address list $$hash{"senders"} = &sort_address_list($$hash{"senders"}, "senders") if defined $$hash{"senders"}; # ---- Changes to domainlist router ---- if ($driver eq "domainlist") { &abolished($hash, "A domainlist router", "modemask", "owners", "owngroups", "qualify_single", "search_parents"); # The name has changed $$hash{"driver"} = "manualroute"; # Turn "route_file", "route_query" and "route_queries" into lookups for # route_data. if (defined $$hash{"route_file"}) { $$hash{"route_data"} = "\${lookup\{\$domain\}$$hash{'search_type'}" . "\{$$hash{'route_file'}\}\}"; } elsif (defined $$hash{"route_query"}) { $$hash{"route_data"} = "\${lookup $$hash{'search_type'}" . "\{" . &unquote($$hash{'route_query'}) . "\}\}"; } elsif (defined $$hash{"route_queries"}) { $endkets = 0; $$hash{"route_data"} = ""; $route_queries = $$hash{'route_queries'}; $route_queries =~ s/^"(.*)"$/$1/s; $route_queries =~ s/::/++colons++/g; @qq = split(/:/, $route_queries); foreach $q (@qq) { $q =~ s/\+\+colons\+\+/:/g; $q =~ s/^\s+//; $q =~ s/\s+$//; if ($endkets > 0) { $$hash{"route_data"} .= "\\\n {"; $endkets++; } $$hash{"route_data"} .= "\${lookup $$hash{'search_type'} \{$q\}\{\$value\}"; $endkets++; } $$hash{"route_data"} .= "}" x $endkets; } delete $$hash{"route_file"}; delete $$hash{"route_query"}; delete $$hash{"route_queries"}; delete $$hash{"search_type"}; # But we can't allow both route_data and route_list if (defined $$hash{"route_data"} && defined $$hash{"route_list"}) { &rubric(); print STDERR "\n" . "** An Exim 3 'domainlist' router called '$name' contained a 'route_list'\n" . " option as well as a setting of 'route_file', 'route_query', or\n" . " 'route_queries'. The latter has been turned into a 'route_data' setting,\n". " but in Exim 4 you can't have both 'route_data' and 'route_list'. You'll\n" . " have to rewrite this router; in the meantime, 'route_list' has been\n" . " omitted.\n"; print STDOUT "#!!# route_list option removed\n"; delete $$hash{"route_list"}; } # Change bydns_a into bydns in a route_list; also bydns_mx, but that # works differently. if (defined $$hash{"route_list"}) { $$hash{"route_list"} =~ s/bydns_a/bydns/g; if ($$hash{"route_list"} =~ /bydns_mx/) { $$hash{"route_list"} =~ s/bydns_mx/bydns/g; &rubric(); print STDERR "\n" . "*** An Exim 3 'domainlist' router called '$name' contained a 'route_list'\n" . " option which used 'bydns_mx'. This feature no longer exists in Exim 4.\n" . " It has been changed to 'bydns', but it won't have the same effect,\n" . " because it will look for A rather than MX records. Use the 'dnslookup'\n" . " router to do MX lookups - if you want to override the hosts found from\n" . " MX records, you should route to a special 'smtp' transport which has\n" . " both 'hosts' and 'hosts_override' set.\n"; } } # Arrange to not expand regex $$hash{"route_list"} = &no_expand_regex($$hash{"route_list"}, ";") if (defined $$hash{"route_list"}) } # ---- Changes to iplookup router ---- elsif ($driver eq "iplookup") { &renamed($hash, "service", "port"); } # ---- Changes to lookuphost router ---- elsif ($driver eq "lookuphost") { $$hash{"driver"} = "dnslookup"; if (defined $$hash{"gethostbyname"}) { &rubric(); print STDERR "\n" . "** An Exim 3 'lookuphost' router called '$name' used the 'gethostbyname'\n" . " option, which no longer exists. You will have to rewrite it.\n"; print STDOUT "#!!# gethostbyname option removed\n"; delete $$hash{"gethostbyname"}; } $$hash{"mx_domains"} = &no_expand_regex($$hash{"mx_domains"}) if defined $$hash{"mx_domains"}; } # ---- Changes to the queryprogram router ---- elsif ($driver eq "queryprogram") { &rubric(); print STDERR "\n" . "** The configuration contains a 'queryprogram' router. Please note that\n" . " the specification for the text that is returned by the program run\n" . " by this router has changed in Exim 4. You will need to modify your\n" . " program.\n"; if (!defined $$hash{'command_user'}) { &rubric(); print STDERR "\n" . "** The 'queryprogram' router called '$name' does not have a setting for\n" . " the 'command_user' option. This is mandatory in Exim 4. A setting of\n" . " 'nobody' has been created.\n"; $$hash{"command_user"} = "nobody"; } } # ------------------------------------- # Output the router's option settings &outdriver($hash); next; } # Skip past any continuation lines for an option setting while ($c[$i] =~ /\\\s*$/s && $i < $clen - 1) { $i++; $i++ while ($c[$i] =~ /^\s*#/); } } # Add "no_more" to the final driver from the old routers, provided it had no # conditions. Otherwise, or if there were no routers, make up one to fail all # non-local domains. if ($add_no_more) { print STDOUT " no_more\n"; print STDOUT shift @comments while scalar(@comments) > 0; } else { print STDOUT shift @comments while scalar(@comments) > 0; print STDOUT "\n#!!# This new router is put here to fail all domains that\n"; print STDOUT "#!!# were not in local_domains in the Exim 3 configuration.\n\n"; print STDOUT "fail_remote_domains:\n"; print STDOUT " driver = redirect\n"; print STDOUT " domains = ! +local_domains\n"; print STDOUT " allow_fail\n"; print STDOUT " data = :fail: unrouteable mail domain \"\$domain\"\n\n"; } # Now copy the directors, making appropriate changes print STDOUT "\n"; print STDOUT "#!!#######################################################!!#\n"; print STDOUT "#!!# Here follow routers created from the old directors, #!!#\n"; print STDOUT "#!!# for handling local domains. #!!#\n"; print STDOUT "#!!#######################################################!!#\n"; $prefix = "d."; for ($i = $director_start; $i < $clen; $i++) { $type = &checkline($c[$i]); last if $type eq "end"; if ($type eq "comment") { print STDOUT "$c[$i]\n"; next; } undef $second_router; if ($type eq "driver") { $hash = $driverlist{"$prefix$name"}; $driver = $$hash{"driver"}; print STDOUT "$name:\n"; $$hash{"caseful_local_part"} = "true" if $add_caseful_local_part; if (defined $$hash{"local_parts"} && (defined $$hash{"prefix"} || defined $hash{"suffix"})) { &rubric(); print STDERR "\n" . "** The Exim 3 configuration contains a director called '$name' which has\n" . " 'local_parts' set, together with either or both of 'prefix' and 'suffix'\n". " This combination has a different effect in Exim 4, where the affix\n" . " is removed *before* 'local_parts' is tested. You will probably need\n" . " to make changes to this driver.\n"; } &renamed($hash, "prefix", "local_part_prefix"); &renamed($hash, "prefix_optional", "local_part_prefix_optional"); &renamed($hash, "suffix", "local_part_suffix"); &renamed($hash, "suffix_optional", "local_part_suffix_optional"); &renamed($hash, "new_director", "redirect_router"); &handle_current_and_home_directory($hash, $driver, $name); # If the director had a require_files setting, check it for user names # and colons that are part of expansion items if (defined $$hash{"require_files"}) { &check_require($$hash{"require_files"}, "'$name' director"); if (($$hash{"require_files"} =~ s/(\$\{\w+):/$1::/g) > 0 || ($$hash{"require_files"} =~ s/ldap:/ldap::/g) > 0) { &rubric(); print STDERR "\n" . "*** A setting of require_files in the $name director contains\n" . " a colon in what appears to be an expansion item. In Exim 3, the\n" . " whole string was expanded before splitting the list, but in Exim 4\n" . " each item is expanded separately, so colons that are not list\n" . " item separators have to be doubled. One or more such colons in this\n" . " list have been doubled as a precaution. Please check the result.\n"; } } # If the director had a "senders" setting, munge the address list $$hash{"senders"} = &sort_address_list($$hash{"senders"}, "senders") if defined $$hash{"senders"}; # ---- Changes to aliasfile director ---- if ($driver eq "aliasfile") { &abolished($hash, "An aliasfile director", "directory2_transport", "freeze_missing_include", "modemask", "owners", "owngroups"); $$hash{"driver"} = "redirect"; $key = "\$local_part"; $key = "\$local_part\@\$domain" if defined $$hash{"include_domain"} && $$hash{"include_domain"} eq "true"; delete $$hash{"include_domain"}; if (defined $$hash{"forbid_special"} && $$hash{"forbid_special"} eq "true") { $$hash{"forbid_blackhole"} = "true"; } else { $$hash{"allow_defer"} = "true"; $$hash{"allow_fail"} = "true"; } delete $$hash{"forbid_special"}; # Deal with "file", "query", or "queries" if (defined $$hash{"file"}) { $$hash{"data"} = "\$\{lookup\{$key\}$$hash{'search_type'}\{$$hash{'file'}\}\}"; if (defined $$hash{"optional"} && $$hash{"optional"} eq "true") { $$hash{"data"} = "\$\{if exists\{$$hash{'file'}\}\{$$hash{'data'}\}\}"; } delete $$hash{"optional"}; } elsif (defined $$hash{"query"}) { &abolished($hash, "An aliasfile director", "optional"); $$hash{"data"} = "\${lookup $$hash{'search_type'} " . "\{" . &unquote($$hash{'query'}) . "\}\}"; } else # Must be queries { &abolished($hash, "An aliasfile director", "optional"); $endkets = 0; $$hash{"data"} = ""; $queries = $$hash{'queries'}; $queries =~ s/^"(.*)"$/$1/s; $queries =~ s/::/++colons++/g; @qq = split(/:/, $queries); foreach $q (@qq) { $q =~ s/\+\+colons\+\+/:/g; $q =~ s/^\s+//; $q =~ s/\s+$//; if ($endkets > 0) { $$hash{"data"} .= "\\\n {"; $endkets++; } $$hash{"data"} .= "\${lookup $$hash{'search_type'} \{$q\}\{\$value\}"; $endkets++; } $$hash{"data"} .= "}" x $endkets; } $$hash{"data"} = "\${expand:$$hash{'data'}\}" if (defined $$hash{"expand"} && $$hash{"expand"} eq "true"); delete $$hash{"expand"}; delete $$hash{"file"}; delete $$hash{"query"}; delete $$hash{"queries"}; delete $$hash{"search_type"}; # Turn aliasfile + transport into accept + condition if (defined $$hash{'transport'}) { &rubric(); if (!defined $$hash{'condition'}) { print STDERR "\n" . "** The Exim 3 configuration contains an aliasfile director called '$name',\n". " which has 'transport' set. This has been turned into an 'accept' router\n". " with a 'condition' setting, but should be carefully checked.\n"; $$hash{'driver'} = "accept"; $$hash{'condition'} = "\$\{if eq \{\}\{$$hash{'data'}\}\{no\}\{yes\}\}"; delete $$hash{'data'}; delete $$hash{'allow_defer'}; delete $$hash{'allow_fail'}; } else { print STDERR "\n" . "** The Exim 3 configuration contains an aliasfile director called '$name',\n". " which has 'transport' set. This cannot be turned into an 'accept' router\n". " with a 'condition' setting, because there is already a 'condition'\n" . " setting. It has been left as 'redirect' with a transport, which is\n" . " invalid - you must sort this one out.\n"; } } } # ---- Changes to forwardfile director ---- elsif ($driver eq "forwardfile") { &abolished($hash, "A forwardfile director", "check_group", "directory2_transport", "freeze_missing_include", "match_directory", "seteuid"); &renamed($hash, "filter", "allow_filter"); $$hash{"driver"} = "redirect"; $$hash{"check_local_user"} = "true" if !defined $$hash{"check_local_user"}; if (defined $$hash{"forbid_pipe"} && $$hash{"forbid_pipe"} eq "true") { print STDOUT "#!!# forbid_filter_run added because forbid_pipe is set\n"; $$hash{"forbid_filter_run"} = "true"; } if (defined $$hash{'allow_system_actions'} && $$hash{'allow_system_actions'} eq 'true') { $$hash{'allow_freeze'} = "true"; } delete $$hash{'allow_system_actions'}; # If file_directory is defined, use it to qualify relative paths; if not, # and check_local_user is defined, use $home. Remove file_directory from # the output. $dir = ""; if (defined $$hash{"file_directory"}) { $dir = $$hash{"file_directory"} . "/"; delete $$hash{"file_directory"}; } elsif ($$hash{"check_local_user"} eq "true") { $dir = "\$home/"; } # If it begins with an upper case letter, guess that this is really # a macro. if (defined $$hash{"file"} && $$hash{"file"} !~ /^[\/A-Z]/) { $$hash{"file"} = $dir . $$hash{"file"}; } } # ---- Changes to localuser director ---- elsif ($driver eq "localuser") { &abolished($hash, "A localuser director", "match_directory"); $$hash{"driver"} = "accept"; $$hash{"check_local_user"} = "true"; } # ---- Changes to smartuser director ---- elsif ($driver eq "smartuser") { &abolished($hash, "A smartuser director", "panic_expansion_fail"); $transport = $$hash{"transport"}; $new_address = $$hash{"new_address"}; if (defined $transport && defined $new_address) { &rubric(); print STDERR "\n" . "** The Exim 3 configuration contains a smartuser director called '$name',\n". " which has both 'transport' and 'new_address' set. This has been turned\n". " into two routers for Exim 4. However, if the new address contains a\n" . " reference to \$local_part, this won't work correctly. In any case, you\n". " may be able to make it tidier by rewriting.\n"; $$hash{"driver"} = "redirect"; $$hash{"data"} = $new_address; $$hash{"redirect_router"} = "${name}_part2"; $second_router = "\n". "#!!# This router is invented to go with the previous one because\n". "#!!# in Exim 4 you can't have a change of address and a transport\n". "#!!# setting in the same router as you could in Exim 3.\n\n" . "${name}_part2:\n". " driver = accept\n". " condition = \$\{if eq\{\$local_part@\$domain\}" . "\{$new_address\}\{yes\}\{no\}\}\n". " transport = $$hash{'transport'}\n"; delete $$hash{"new_address"}; delete $$hash{"transport"}; } elsif (defined $new_address) { $$hash{"driver"} = "redirect"; $$hash{"data"} = $new_address; $$hash{"allow_defer"} = "true"; $$hash{"allow_fail"} = "true"; delete $$hash{"new_address"}; } else # Includes the case of neither set (verify_only) { $$hash{"driver"} = "accept"; if (defined $$hash{"rewrite"}) { &rubric(); print STDERR "\n" . "** The Exim 3 configuration contains a setting of the 'rewrite' option on\n". " a smartuser director called '$name', but this director does not have\n". " a setting of 'new_address', so 'rewrite' has no effect. The director\n". " has been turned into an 'accept' router, and 'rewrite' has been discarded."; delete $$hash{"rewrite"}; } } } # ------------------------------------- # For ex-directors that don't have check_local_user set, add # retry_use_local_part to imitate what Exim 3 would have done. $$hash{"retry_use_local_part"} = "true" if (!defined $$hash{"check_local_user"} || $$hash{"check_local_user"} eq "false") ; # Output the router's option settings &outdriver($hash); # Output an auxiliary router if one is needed print STDOUT $second_router if defined $second_router; next; } # Skip past any continuation lines for an option setting while ($c[$i] =~ /\\\s*$/s) { $i++; $i++ while ($c[$i] =~ /^\s*#/); } } # -------- The transports configuration -------- $started = 0; $prefix = "t."; for ($i = $transport_start; $i < $clen; $i++) { $type = &checkline($c[$i]); last if $type eq "end"; if ($type eq "comment") { print STDOUT "$c[$i]\n"; next; } if (!$started) { print STDOUT "begin transports\n\n"; $started = 1; } if ($type eq "driver") { $hash = $driverlist{"$prefix$name"}; $driver = $$hash{"driver"}; print STDOUT "$name:\n"; # ---- Changes to the appendfile transport ---- if ($driver eq "appendfile") { &renamed($hash, "prefix", "message_prefix"); &renamed($hash, "suffix", "message_suffix"); &abolished($hash, "An appendfile transport", "require_lockfile"); &handle_batch_and_bsmtp($hash); if (defined $$hash{"from_hack"} && $$hash{"from_hack"} eq "false") { print STDOUT "#!!# no_from_hack replaced by check_string\n"; $$hash{"check_string"} = ""; } delete $$hash{"from_hack"}; } # ---- Changes to the lmtp transport ---- elsif ($driver eq "lmtp") { if (defined $$hash{"batch"} && $$hash{"batch"} ne "none") { $$hash{"batch_max"} = "100" if !defined $$hash{"batch_max"}; $$hash{"batch_id"} = "\$domain" if $$hash{"batch"} eq "domain"; } else { $$hash{"batch_max"} = "1" if defined $$hash{"batch_max"}; } delete $$hash{"batch"}; } # ---- Changes to the pipe transport ---- elsif ($driver eq "pipe") { &renamed($hash, "prefix", "message_prefix"); &renamed($hash, "suffix", "message_suffix"); &handle_batch_and_bsmtp($hash); if (defined $$hash{"from_hack"} && $$hash{"from_hack"} eq "false") { print STDOUT "#!!# no_from_hack replaced by check_string\n"; $$hash{"check_string"} = ""; } delete $$hash{"from_hack"}; } # ---- Changes to the smtp transport ---- elsif ($driver eq "smtp") { &abolished($hash, "An smtp transport", "mx_domains"); &renamed($hash, "service", "port"); &renamed($hash, "tls_verify_ciphers", "tls_require_ciphers"); &renamed($hash, "authenticate_hosts", "hosts_try_auth"); if (defined $$hash{"batch_max"}) { print STDOUT "#!!# batch_max renamed connection_max_messages\n"; $$hash{"connection_max_messages"} = $$hash{"batch_max"}; delete $$hash{"batch_max"}; } foreach $o ("hosts_try_auth", "hosts_avoid_tls", "hosts_require_tls", "mx_domains", "serialize_hosts") { $$hash{$o} = &no_expand_regex($$hash{$o}) if defined $$hash{$o}; } } &outdriver($driverlist{"$prefix$name"}); next; } # Skip past any continuation lines for an option setting while ($c[$i] =~ /\\\s*$/s) { $i++; $i++ while ($c[$i] =~ /^\s*#/); } } # -------- The retry configuration -------- $started = 0; for ($i = $retry_start; $i < $clen && $i < $rewrite_start - 1; $i++) { if (!$started) { if ($c[$i] !~ /^\s*(#|$)/) { print STDOUT "\nbegin retry\n\n"; $started = 1; } } &print_no_expand($c[$i]); } print STDOUT "\n# End of Exim 4 configuration\n"; print STDERR "\n*******************************************************\n"; print STDERR "***** Please review the generated file carefully. *****\n"; print STDERR "*******************************************************\n\n"; # End of convert4r4