1#! PERL_COMMAND
2
3# This is a Perl script that reads an Exim run-time configuration file and
4# checks for settings that were valid prior to release 3.00 but which were
5# obsoleted by that release. It writes a new file with suggested changes to
6# the standard output, and commentary about what it has done to stderr.
7
8# It is assumed that the input is a valid Exim configuration file.
9
10use warnings;
11BEGIN { pop @INC if $INC[-1] eq '.' };
12
13use Getopt::Long;
14use File::Basename;
15
16GetOptions(
17    'version' => sub {
18        print basename($0) . ": $0\n",
19            "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n",
20            "perl(runtime): $^V\n";
21            exit 0;
22    },
23);
24
25##################################################
26#             Analyse one line                   #
27##################################################
28
29# This is called for the main and the driver sections, not for retry
30# or rewrite sections (which are unmodified).
31
32sub checkline{
33my($line) = $_[0];
34
35return "comment" if $line =~ /^\s*(#|$)/;
36return "end"     if $line =~ /^\s*end\s*$/;
37
38# Macros are recognized only in the first section of the file.
39
40return "macro" if $prefix eq "" && $line =~ /^\s*[A-Z]/;
41
42# Pick out the name at the start and the rest of the line (into global
43# variables) and return whether the start of a driver or not.
44
45($i1,$name,$i2,$rest) = $line =~ /^(\s*)([a-z0-9_]+)(\s*)(.*?)\s*$/;
46return ($rest =~ /^:/)? "driver" : "option";
47}
48
49
50
51
52##################################################
53#       Add transport setting to a director      #
54##################################################
55
56# This function adds a transport setting to an aliasfile or forwardfile
57# director if a global setting exists and a local one does not. If neither
58# exist, it adds file/pipe/reply, but not the directory ones.
59
60sub add_transport{
61my($option) = @_;
62
63my($key) = "$prefix$driver.${option}_transport";
64if (!exists $o{$key})
65  {
66  if (exists $o{"address_${option}_transport"})
67    {
68    print STDOUT "# >> Option added by convert4r3\n";
69    printf STDOUT "${i1}${option}_transport = %s\n",
70      $o{"address_${option}_transport"};
71    printf STDERR
72      "\n%03d ${option}_transport added to $driver director.\n",
73      ++$count;
74    }
75  else
76    {
77    if ($option eq "pipe" || $option eq "file" || $option eq "reply")
78      {
79      print STDOUT "# >> Option added by convert4r3\n";
80      printf STDOUT "${i1}${option}_transport = address_${option}\n";
81      printf STDERR
82        "\n%03d ${option}_transport added to $driver director.\n",
83        ++$count;
84      }
85    }
86  }
87}
88
89
90
91
92##################################################
93#       Negate a list of things                  #
94##################################################
95
96sub negate {
97my($list) = $_[0];
98
99return $list if ! defined $list;
100
101($list) = $list =~ /^"?(.*?)"?\s*$/s;
102
103# Under Perl 5.005 we can split very nicely at colons, ignoring double
104# colons, like this:
105#
106# @split = split /\s*(?<!:):(?!:)\s*(?:\\\s*)?/s, $list;
107#
108# However, we'd better make this work under Perl 5.004, since there is
109# a lot of that about.
110
111$list =~ s/::/>%%%%</g;
112@split = split /\s*:\s*(?:\\\s*)?/s, $list;
113foreach $item (@split)
114  {
115  $item =~ s/>%%%%</::/g;
116  }
117
118$" = " : \\\n    ! ";
119return "! @split";
120}
121
122
123
124
125
126##################################################
127#          Skip blank lines                      #
128##################################################
129
130# This function is called after we have generated no output for an option;
131# it skips subsequent blank lines if the previous line was blank.
132
133sub skipblanks {
134my($i) = $_[0];
135if ($last_was_blank)
136  {
137  $i++ while $c[$i+1] =~ /^\s*$/;
138  }
139return $i;
140}
141
142
143
144
145
146##################################################
147#       Get base name of data key                #
148##################################################
149
150sub base {
151return "$_[0]" if $_[0] !~ /^(?:d|r|t)\.[^.]+\.(.*)/;
152return $1;
153}
154
155
156
157##################################################
158#     Amalgamate accept/reject/reject_except     #
159##################################################
160
161# This function amalgamates the three previous kinds of
162# option into a single list, using negation for the middle one if
163# the final argument is "+", or for the outer two if the final
164# argument is "-".
165
166sub amalgamate {
167my($accept,$reject,$reject_except,$name);
168my($last_was_negated) = 0;
169my($join) = "";
170
171$accept = $o{$_[0]};
172$reject = $o{$_[1]};
173$reject_except = $o{$_[2]};
174$name = $_[3];
175
176if ($_[4] eq "+")
177  {
178  ($accept) = $accept =~ /^"?(.*?)"?\s*$/s if defined $accept;
179  $reject = &negate($reject) if defined $reject;
180  ($reject_except) = $reject_except =~ /^"?(.*?)"?\s*$/s if defined $reject_except;
181  }
182else
183  {
184  $accept = &negate($accept) if defined $accept;
185  ($reject) = $reject =~ /^"?(.*?)"?\s*$/s if defined $reject;
186  $reject_except = &negate($reject_except) if defined $reject_except;
187  }
188
189print STDOUT "# >> Option rewritten by convert4r3\n";
190print STDOUT "${i1}$name = \"";
191
192if (defined $reject_except)
193  {
194  print STDOUT "$reject_except";
195  $join = " : \\\n    ";
196  $last_was_negated = ($_[4] ne "+");
197  }
198if (defined $reject)
199  {
200  print STDOUT "$join$reject";
201  $join = " : \\\n    ";
202  $last_was_negated = ($_[4] eq "+");
203  }
204if (defined $accept)
205  {
206  print STDOUT "$join$accept";
207  $last_was_negated = ($_[4] ne "+");
208  $join = " : \\\n    ";
209  }
210
211print STDOUT "$join*" if $last_was_negated;
212
213print STDOUT "\"\n";
214
215my($driver_name);
216my($driver_type) = "";
217
218if ($_[0] =~ /^(d|r|t)\.([^.]+)\./ ||
219    $_[1] =~ /^(d|r|t)\.([^.]+)\./ ||
220    $_[2] =~ /^(d|r|t)\.([^.]+)\./)
221  {
222  $driver_type = ($1 eq 'd')? "director" : ($1 eq 'r')? "router" : "transport";
223  $driver_name = $2;
224  }
225
226my($x) = ($driver_type ne "")? " in \"$driver_name\" $driver_type" : "";
227
228my($l0) = &base($_[0]);
229my($l1) = &base($_[1]);
230my($l2) = &base($_[2]);
231
232
233if ($l2 eq "")
234  {
235  if ($l0 eq "")
236    {
237    printf STDERR "\n%03d $l1 converted to $name$x.\n", ++$count;
238    }
239  else
240    {
241    printf STDERR "\n%03d $l0 and $l1\n    amalgamated into $name$x.\n",
242      ++$count;
243    }
244  }
245else
246  {
247  if ($l1 eq "")
248    {
249    printf STDERR "\n%03d $l0 and $l2\n    amalgamated into $name$x.\n",
250      ++$count;
251    }
252  else
253    {
254    printf STDERR "\n%03d $l0, $l1 and $l2\n    amalgamated into " .
255      "$name$x.\n", ++$count;
256    }
257  }
258}
259
260
261
262
263##################################################
264#         Join two lists, if they exist          #
265##################################################
266
267sub pair{
268my($l1) = $o{"$_[0]"};
269my($l2) = $o{"$_[1]"};
270
271return $l2 if (!defined $l1);
272return $l1 if (!defined $l2);
273
274($l1) = $l1 =~ /^"?(.*?)"?\s*$/s;
275($l2) = $l2 =~ /^"?(.*?)"?\s*$/s;
276
277return "$l1 : $l2";
278}
279
280
281
282
283##################################################
284#  Amalgamate accept/reject/reject_except pairs  #
285##################################################
286
287# This is like amalgamate, but it combines pairs of arguments, and
288# doesn't output commentary (easier to write a generic one for the few
289# cases).
290
291sub amalgamatepairs {
292my($accept) = &pair($_[0], $_[1]);
293my($reject) = &pair($_[2], $_[3]);
294my($reject_except) = &pair($_[4], $_[5]);
295my($last_was_negated) = 0;
296my($join) = "";
297
298if ($_[7] eq "+")
299  {
300  ($accept) = $accept =~ /^"?(.*?)"?\s*$/s if defined $accept;
301  $reject = &negate($reject) if defined $reject;
302  ($reject_except) = $reject_except =~ /^"?(.*?)"?\s*$/s if defined $reject_except;
303  }
304else
305  {
306  $accept = &negate($accept) if defined $accept;
307  ($reject) = $reject =~ /^"?(.*?)"?$/s if defined $reject;
308  $reject_except = &negate($reject_except) if defined $reject_except;
309  }
310
311print STDOUT "# >> Option rewritten by convert4r3\n";
312print STDOUT "${i1}$_[6] = \"";
313
314if (defined $reject_except)
315  {
316  print STDOUT "$reject_except";
317  $join = " : \\\n    ";
318  $last_was_negated = ($_[7] ne "+");
319  }
320if (defined $reject)
321  {
322  print STDOUT "$join$reject";
323  $join = " : \\\n    ";
324  $last_was_negated = ($_[7] eq "+");
325  }
326if (defined $accept)
327  {
328  print STDOUT "$join$accept";
329  $last_was_negated = ($_[7] ne "+");
330  $join = " : \\\n    ";
331  }
332
333print STDOUT "$join*" if $last_was_negated;
334print STDOUT "\"\n";
335}
336
337
338
339##################################################
340#      Amalgamate boolean and exception list(s)  #
341##################################################
342
343sub amalgboolandlist {
344my($name,$bool,$e1,$e2) = @_;
345
346print STDOUT "# >> Option rewritten by convert4r3\n";
347if ($bool eq "false")
348  {
349  printf STDOUT "$i1$name =\n";
350  }
351else
352  {
353  printf STDOUT "$i1$name = ";
354  my($n1) = &negate($o{$e1});
355  my($n2) = &negate($o{$e2});
356  if (!defined $n1 && !defined $n2)
357    {
358    print STDOUT "*\n";
359    }
360  elsif (!defined $n1)
361    {
362    print STDOUT "\"$n2 : \\\n    *\"\n";
363    }
364  elsif (!defined $n2)
365    {
366    print STDOUT "\"$n1 : \\\n    *\"\n";
367    }
368  else
369    {
370    print STDOUT "\"$n1 : \\\n    $n2 : \\\n    *\"\n";
371    }
372  }
373}
374
375
376
377##################################################
378#             Convert mask format                #
379##################################################
380
381# This function converts an address and mask in old-fashioned dotted-quad
382# format into an address plus a new format mask.
383
384@byte_list = (0, 128, 192, 224, 240, 248, 252, 254, 255);
385
386sub mask {
387my($address,$mask) = @_;
388my($length) = 0;
389my($i, $j);
390
391my(@bytes) = split /\./, $mask;
392
393for ($i = 0; $i < 4; $i++)
394  {
395  for ($j = 0; $j <= 8; $j++)
396    {
397    if ($bytes[$i] == $byte_list[$j])
398      {
399      $length += $j;
400      if ($j != 8)
401        {
402        for ($i++; $i < 4; $i++)
403          {
404          $j = 9 if ($bytes[$i] != 0);
405          }
406        }
407      last;
408      }
409    }
410
411  if ($j > 8)
412    {
413    print STDERR "*** IP mask $mask cannot be converted to /n format. ***\n";
414    return "$address/$mask";
415    }
416  }
417
418if (!defined $masks{$mask})
419  {
420  printf STDERR "\n%03d IP address mask $mask converted to /$length\n",
421    ++$count, $mask, $length;
422  $masks{$mask} = 1;
423  }
424
425return sprintf "$address/%d", $length;
426}
427
428
429
430
431
432##################################################
433#                  Main program                  #
434##################################################
435
436print STDERR "Exim pre-release 3.00 configuration file converter.\n";
437
438$count = 0;
439$seen_helo_accept_junk = 0;
440$seen_hold_domains = 0;
441$seen_receiver_unqualified = 0;
442$seen_receiver_verify_except = 0;
443$seen_receiver_verify_senders = 0;
444$seen_rfc1413_except = 0;
445$seen_sender_accept = 0;
446$seen_sender_accept_recipients = 0;
447$seen_sender_host_accept = 0;
448$seen_sender_host_accept_recipients = 0;
449$seen_sender_host_accept_relay = 0;
450$seen_sender_unqualified = 0;
451$seen_sender_verify_except_hosts = 0;
452$seen_smtp_etrn = 0;
453$seen_smtp_expn = 0;
454$seen_smtp_reserve = 0;
455$semicomma = 0;
456
457# Read the entire file into an array
458
459chomp(@c = <STDIN>);
460
461# First, go through the input and covert any net masks in the old dotted-quad
462# style into the new /n style.
463
464for ($i = 0; $i < scalar(@c); $i++)
465  {
466  $c[$i] =~
467    s"((?:\d{1,3}\.){3}\d{1,3})/((?:\d{1,3}\.){3}\d{1,3})"&mask($1,$2)"eg;
468  }
469
470# We now make two more passes over the input. In the first pass, we place all
471# the option values into an associative array. Main options are keyed by their
472# names; options for drivers are keyed by a driver type letter, the driver
473# name, and the option name, dot-separated. In the second pass we modify
474# the options if necessary, and write the output file.
475
476for ($pass = 1; $pass < 3; $pass++)
477  {
478  $prefix = "";
479  $driver = "";
480  $last_was_blank = 0;
481
482  for ($i = 0; $i < scalar(@c); $i++)
483    {
484    # Everything after the router section is just copied in pass 2 and
485    # ignored in pass 1.
486
487    if ($prefix eq "end")
488      {
489      print STDOUT "$c[$i]\n" if $pass == 2;
490      next;
491      }
492
493    # Analyze the line
494
495    $type = &checkline($c[$i]);
496
497    # Skip comments in pass 1; copy in pass 2
498
499    if ($type eq "comment")
500      {
501      $last_was_blank = ($c[$i] =~ /^\s*$/)? 1 : 0;
502      print STDOUT "$c[$i]\n" if $pass == 2;
503      next;
504      }
505
506    # Skip/copy macro definitions, but must handle continuations
507
508    if ($type eq "macro")
509      {
510      print STDOUT "$c[$i]\n" if $pass == 2;
511      while ($c[$i] =~ /\\\s*$/)
512        {
513        $i++;
514        print STDOUT "$c[$i]\n" if $pass == 2;
515        }
516      $last_was_blank = 0;
517      next;
518      }
519
520    # Handle end of section
521
522    if ($type eq "end")
523      {
524      $prefix = "end"if $prefix eq "r.";
525      $prefix = "r." if $prefix eq "d.";
526      $prefix = "d." if $prefix eq "t.";
527      $prefix = "t." if $prefix eq "";
528      print STDOUT "$c[$i]\n" if $pass == 2;
529      $last_was_blank = 0;
530      next;
531      }
532
533    # Handle start of a new driver
534
535    if ($type eq "driver")
536      {
537      $driver = $name;
538      print STDOUT "$c[$i]\n" if $pass == 2;
539      $last_was_blank = 0;
540      $seen_domains = 0;
541      $seen_local_parts = 0;
542      $seen_senders = 0;
543      $seen_mx_domains = 0;
544      $seen_serialize = 0;
545      next;
546      }
547
548    # Handle definition of an option
549
550    if ($type eq "option")
551      {
552      # Handle continued strings
553
554      if ($rest =~ /^=\s*".*\\$/)
555        {
556        for (;;)
557          {
558          $rest .= "\n$c[++$i]";
559          last unless $c[$i] =~ /(\\\s*$|^\s*#)/;
560          }
561        }
562
563      # Remove any terminating commas and semicolons in pass 2
564
565      if ($pass == 2 && $rest =~ /[;,]\s*$/)
566        {
567        $rest =~ s/\s*[;,]\s*$//;
568        if (!$semicomma)
569          {
570          printf STDERR
571            "\n%03d Terminating semicolons and commas removed from driver " .
572            "options.\n", ++$count;
573          $semicomma = 1;
574          }
575        }
576
577      # Convert all booleans to "x = true/false" format, but save the
578      # original so that it can be reproduced unchanged for options that
579      # are not of interest.
580
581      $origname = $name;
582      $origrest = $rest;
583
584      if ($name =~ /^not?_(.*)/)
585        {
586        $name = $1;
587        $rest = "= false";
588        }
589      elsif ($rest !~ /^=/)
590        {
591        $rest = "= true";
592        }
593
594      # Set up the associative array key, and get rid of the = on the data
595
596      $key = ($prefix eq "")? "$name" : "$prefix$driver.$name";
597      ($rest) = $rest =~ /^=\s*(.*)/s;
598
599      # Create the associative array of values in pass 1
600
601      if ($pass == 1)
602        {
603        $o{$key} = $rest;
604        }
605
606      # In pass 2, test for interesting options and do the necessary; copy
607      # all the rest.
608
609      else
610        {
611        ##########  Global configuration ##########
612
613        # These global options are abolished
614
615        if ($name eq "address_directory_transport" ||
616            $name eq "address_directory2_transport" ||
617            $name eq "address_file_transport" ||
618            $name eq "address_pipe_transport" ||
619            $name eq "address_reply_transport")
620          {
621          ($n2) = $name =~ /^address_(.*)/;
622          printf STDERR "\n%03d $name option deleted.\n", ++$count;
623          printf STDERR "    $n2 will be added to appropriate directors.\n";
624          $i = &skipblanks($i);
625          next;
626          }
627
628        # This debugging option is abolished
629
630        elsif ($name eq "sender_verify_log_details")
631          {
632          printf STDERR "\n%03d $name option deleted.\n", ++$count;
633          printf STDERR "    (Little used facility abolished.)\n";
634          }
635
636        # This option has been renamed
637
638        elsif ($name eq "check_dns_names")
639          {
640          $origname =~ s/check_dns/dns_check/;
641          print STDOUT "# >> Option rewritten by convert4r3\n";
642          print STDOUT "$i1$origname$i2$origrest\n";
643          printf STDERR "\n%03d check_dns_names renamed as dns_check_names.\n",
644            ++$count;
645          }
646
647        # helo_accept_junk_nets is abolished
648
649        elsif ($name eq "helo_accept_junk_nets" ||
650               $name eq "helo_accept_junk_hosts")
651          {
652          if (!$seen_helo_accept_junk)
653            {
654            &amalgamate("helo_accept_junk_nets", "",
655              "helo_accept_junk_hosts", "helo_accept_junk_hosts", "+");
656            $seen_helo_accept_junk = 1;
657            }
658          else
659            {
660            $i = &skipblanks($i);
661            next;
662            }
663          }
664
665        # helo_verify_except_{hosts,nets} are abolished, and helo_verify
666        # is now a host list instead of a boolean.
667
668        elsif ($name eq "helo_verify")
669          {
670          &amalgboolandlist("helo_verify", $rest, "helo_verify_except_hosts",
671            "helo_verify_except_nets");
672          printf STDERR "\n%03d helo_verify converted to host list.\n",
673            ++$count;
674          }
675        elsif ($name eq "helo_verify_except_hosts" ||
676               $name eq "helo_verify_except_nets")
677          {
678          $i = &skipblanks($i);
679          next;
680          }
681
682        # helo_verify_nets was an old synonym for host_lookup_nets; only
683        # one of them will be encountered. Change to a new name.
684
685        elsif ($name eq "helo_verify_nets" ||
686               $name eq "host_lookup_nets")
687          {
688          print STDOUT "# >> Option rewritten by convert4r3\n";
689          print STDOUT "${i1}host_lookup$i2$origrest\n";
690          printf STDERR "\n%03d $name renamed as host_lookup.\n", ++$count;
691          }
692
693        # hold_domains_except is abolished; add as negated items to
694        # hold_domains.
695
696        elsif ($name eq "hold_domains_except" ||
697               $name eq "hold_domains")
698          {
699          if ($seen_hold_domains)         # If already done with these
700            {                             # omit, and following blanks.
701            $i = &skipblanks($i);
702            next;
703            }
704          $seen_hold_domains = 1;
705
706          if (exists $o{"hold_domains_except"})
707            {
708            &amalgamate("hold_domains", "hold_domains_except", "",
709              "hold_domains", "+");
710            }
711          else
712            {
713            print STDOUT "$i1$origname$i2$origrest\n";
714            }
715          }
716
717        # ignore_fromline_nets is renamed as ignore_fromline_hosts
718
719        elsif ($name eq "ignore_fromline_nets")
720          {
721          $origname =~ s/_nets/_hosts/;
722          print STDOUT "# >> Option rewritten by convert4r3\n";
723          print STDOUT "$i1$origname$i2$origrest\n";
724          printf STDERR
725            "\n%03d ignore_fromline_nets renamed as ignore_fromline_hosts.\n",
726            ++$count;
727          }
728
729        # Output a warning for message filters with no transports set
730
731        elsif ($name eq "message_filter")
732          {
733          print STDOUT "$i1$origname$i2$origrest\n";
734
735          if (!exists $o{"message_filter_directory_transport"} &&
736              !exists $o{"message_filter_directory2_transport"} &&
737              !exists $o{"message_filter_file_transport"} &&
738              !exists $o{"message_filter_pipe_transport"} &&
739              !exists $o{"message_filter_reply_transport"})
740            {
741            printf STDERR
742              "\n%03d message_filter is set, but no message_filter transports "
743              . "are defined.\n"
744              . "    If your filter generates file or pipe deliveries, or "
745              . "auto-replies,\n"
746              . "    you will need to define "
747              . "message_filter_{file,pipe,reply}_transport\n"
748              . "    options, as required.\n", ++$count;
749            }
750          }
751
752        # queue_remote_except is abolished, and queue_remote is replaced by
753        # queue_remote_domains, which is a host list.
754
755        elsif ($name eq "queue_remote")
756          {
757          &amalgboolandlist("queue_remote_domains", $rest,
758            "queue_remote_except", "");
759          printf STDERR
760            "\n%03d queue_remote converted to domain list queue_remote_domains.\n",
761            ++$count;
762          }
763        elsif ($name eq "queue_remote_except")
764          {
765          $i = &skipblanks($i);
766          next;
767          }
768
769        # queue_smtp_except is abolished, and queue_smtp is replaced by
770        # queue_smtp_domains, which is a host list.
771
772        elsif ($name eq "queue_smtp")
773          {
774          &amalgboolandlist("queue_smtp_domains", $rest,
775            "queue_smtp_except", "");
776          printf STDERR
777            "\n%03d queue_smtp converted to domain list queue_smtp_domains.\n",
778            ++$count;
779          }
780        elsif ($name eq "queue_smtp_except")
781          {
782          $i = &skipblanks($i);
783          next;
784          }
785
786        # rbl_except_nets is replaced by rbl_hosts
787
788        elsif ($name eq "rbl_except_nets")
789          {
790          &amalgamate("", "rbl_except_nets", "", "rbl_hosts", "+");
791          }
792
793        # receiver_unqualified_nets is abolished
794
795        elsif ($name eq "receiver_unqualified_nets" ||
796               $name eq "receiver_unqualified_hosts")
797          {
798          if (!$seen_receiver_unqualified)
799            {
800            &amalgamate("receiver_unqualified_nets", "",
801              "receiver_unqualified_hosts", "receiver_unqualified_hosts", "+");
802            $seen_receiver_unqualified = 1;
803            }
804          else
805            {
806            $i = &skipblanks($i);
807            next;
808            }
809          }
810
811        # receiver_verify_except_{hosts,nets} are replaced by
812        # receiver_verify_hosts.
813
814        elsif ($name eq "receiver_verify_except_hosts" ||
815               $name eq "receiver_verify_except_nets")
816          {
817          if (!$seen_receiver_verify_except)
818            {
819            &amalgboolandlist("receiver_verify_hosts", "true",
820              "receiver_verify_except_hosts", "receiver_verify_except_nets");
821            printf STDERR
822              "\n%03d receiver_verify_except_{hosts,nets} converted to " .
823              "receiver_verify_hosts.\n",
824              ++$count;
825            $seen_receiver_verify_except = 1;
826            }
827          else
828            {
829            $i = &skipblanks($i);
830            next;
831            }
832          }
833
834        # receiver_verify_senders_except is abolished
835
836        elsif ($name eq "receiver_verify_senders" ||
837               $name eq "receiver_verify_senders_except")
838          {
839          if (defined $o{"receiver_verify_senders_except"})
840            {
841            if (!$seen_receiver_verify_senders)
842              {
843              &amalgamate("receiver_verify_senders",
844                "receiver_verify_senders_except", "",
845                "receiver_verify_senders", "+");
846              $seen_receiver_verify_senders = 1;
847              }
848            else
849              {
850              $i = &skipblanks($i);
851              next;
852              }
853            }
854          else
855            {
856            print STDOUT "$i1$origname$i2$origrest\n";
857            }
858          }
859
860        # rfc1413_except_{hosts,nets} are replaced by rfc1413_hosts.
861
862        elsif ($name eq "rfc1413_except_hosts" ||
863               $name eq "rfc1413_except_nets")
864          {
865          if (!$seen_rfc1413_except)
866            {
867            &amalgboolandlist("rfc1413_hosts", "true",
868              "rfc1413_except_hosts", "rfc1413_except_nets");
869            printf STDERR
870              "\n%03d rfc1413_except_{hosts,nets} converted to rfc1413_hosts.\n",
871              ++$count;
872            $seen_rfc1413_except = 1;
873            }
874          else
875            {
876            $i = &skipblanks($i);
877            next;
878            }
879          }
880
881        # sender_accept and sender_reject_except are abolished
882
883        elsif ($name eq "sender_accept" ||
884               $name eq "sender_reject")
885          {
886          if (!$seen_sender_accept)
887            {
888            &amalgamate("sender_accept", "sender_reject",
889              "sender_reject_except", "sender_reject", "-");
890            $seen_sender_accept = 1;
891            }
892          else
893            {
894            $i = &skipblanks($i);
895            next;
896            }
897          }
898
899        # sender_accept_recipients is also abolished; sender_reject_except
900        # also used to apply to this, so we include it here as well.
901
902        elsif ($name eq "sender_accept_recipients" ||
903               $name eq "sender_reject_recipients")
904          {
905          if (!$seen_sender_accept_recipients)
906            {
907            &amalgamate("sender_accept_recipients", "sender_reject_recipients",
908              "sender_reject_except", "sender_reject_recipients", "-");
909            $seen_sender_accept_recipients = 1;
910            }
911          else
912            {
913            $i = &skipblanks($i);
914            next;
915            }
916          }
917
918        # sender_reject_except must be removed
919
920        elsif ($name eq "sender_reject_except")
921          {
922          $i = &skipblanks($i);
923          next;
924          }
925
926        # sender_{host,net}_{accept,reject}[_except] all collapse into
927        # host_reject.
928
929        elsif ($name eq "sender_host_accept" ||
930               $name eq "sender_net_accept" ||
931               $name eq "sender_host_reject" ||
932               $name eq "sender_net_reject")
933          {
934          if (!$seen_sender_host_accept)
935            {
936            &amalgamatepairs("sender_host_accept", "sender_net_accept",
937              "sender_host_reject", "sender_net_reject",
938              "sender_host_reject_except", "sender_net_reject_except",
939              "host_reject", "-");
940            printf STDERR "\n%03d sender_{host,net}_{accept,reject} and " .
941              "sender_{host_net}_reject_except\n" .
942              "    amalgamated into host_reject.\n", ++$count;
943            $seen_sender_host_accept = 1;
944            }
945          else
946            {
947            $i = &skipblanks($i);
948            next;
949            }
950          }
951
952        # sender_{host,net}_{accept,reject}_recipients all collapse into
953        # host_reject_recipients.
954
955        elsif ($name eq "sender_host_accept_recipients" ||
956               $name eq "sender_net_accept_recipients" ||
957               $name eq "sender_host_reject_recipients" ||
958               $name eq "sender_net_reject_recipients")
959          {
960          if (!$seen_sender_host_accept_recipients)
961            {
962            &amalgamatepairs("sender_host_accept_recipients",
963              "sender_net_accept_recipients",
964              "sender_host_reject_recipients",
965              "sender_net_reject_recipients",
966              "sender_host_reject_except", "sender_net_reject_except",
967              "host_reject_recipients", "-");
968            printf STDERR "\n%03d sender_{host,net}_{accept,reject}_recipients"
969              . "\n    and sender_{host_net}_reject_except"
970              . "\n    amalgamated into host_reject_recipients.\n", ++$count;
971            $seen_sender_host_accept_recipients = 1;
972            }
973          else
974            {
975            $i = &skipblanks($i);
976            next;
977            }
978          }
979
980        # sender_{host,net}_reject_except must be removed
981
982        elsif ($name eq "sender_host_reject_except" ||
983               $name eq "sender_net_reject_except")
984          {
985          $i = &skipblanks($i);
986          next;
987          }
988
989        # sender_{host,net}_{accept,reject}_relay all collapse into
990        # host_accept_relay.
991
992        elsif ($name eq "sender_host_accept_relay" ||
993               $name eq "sender_net_accept_relay" ||
994               $name eq "sender_host_reject_relay" ||
995               $name eq "sender_net_reject_relay")
996          {
997          if (!$seen_sender_host_accept_relay)
998            {
999            &amalgamatepairs("sender_host_accept_relay",
1000              "sender_net_accept_relay",
1001              "sender_host_reject_relay",
1002              "sender_net_reject_relay",
1003              "sender_host_reject_relay_except",
1004              "sender_net_reject_relay_except",
1005              "host_accept_relay", "+");
1006            printf STDERR "\n%03d sender_{host,net}_{accept,reject}_relay"
1007              . "\n    and sender_{host_net}_reject_relay_except"
1008              . "\n    amalgamated into host_accept_relay.\n", ++$count;
1009            $seen_sender_host_accept_relay = 1;
1010            }
1011          else
1012            {
1013            $i = &skipblanks($i);
1014            next;
1015            }
1016          }
1017
1018        # sender_{host,net}_reject_relay_except must be removed
1019
1020        elsif ($name eq "sender_host_reject_relay_except" ||
1021               $name eq "sender_net_reject_relay_except")
1022          {
1023          $i = &skipblanks($i);
1024          next;
1025          }
1026
1027
1028        # sender_unqualified_nets is abolished
1029
1030        elsif ($name eq "sender_unqualified_nets" ||
1031               $name eq "sender_unqualified_hosts")
1032          {
1033          if (!$seen_sender_unqualified)
1034            {
1035            &amalgamate("sender_unqualified_nets", "",
1036              "sender_unqualified_hosts", "sender_unqualified_hosts", "+");
1037            $seen_sender_unqualified = 1;
1038            }
1039          else
1040            {
1041            $i = &skipblanks($i);
1042            next;
1043            }
1044          }
1045
1046        # sender_verify_except_{hosts,nets} are replaced by sender_verify_hosts.
1047
1048        elsif ($name eq "sender_verify_except_hosts" ||
1049               $name eq "sender_verify_except_nets")
1050          {
1051          if (!$seen_sender_verify_except_hosts)
1052            {
1053            &amalgboolandlist("sender_verify_hosts", "true",
1054              "sender_verify_except_hosts", "sender_verify_except_nets");
1055            printf STDERR
1056              "\n%03d sender_verify_except_{hosts,nets} converted to " .
1057              "sender_verify_hosts.\n",
1058              ++$count;
1059            $seen_sender_verify_except_hosts = 1;
1060            }
1061          else
1062            {
1063            $i = &skipblanks($i);
1064            next;
1065            }
1066          }
1067
1068        # smtp_etrn_nets is abolished
1069
1070        elsif ($name eq "smtp_etrn_nets" ||
1071               $name eq "smtp_etrn_hosts")
1072          {
1073          if (!$seen_smtp_etrn)
1074            {
1075            &amalgamate("smtp_etrn_nets", "",
1076              "smtp_etrn_hosts", "smtp_etrn_hosts", "+");
1077            $seen_smtp_etrn = 1;
1078            }
1079          else
1080            {
1081            $i = &skipblanks($i);
1082            next;
1083            }
1084          }
1085
1086        # smtp_expn_nets is abolished
1087
1088        elsif ($name eq "smtp_expn_nets" ||
1089               $name eq "smtp_expn_hosts")
1090          {
1091          if (!$seen_smtp_expn)
1092            {
1093            &amalgamate("smtp_expn_nets", "",
1094              "smtp_expn_hosts", "smtp_expn_hosts", "+");
1095            $seen_smtp_expn = 1;
1096            }
1097          else
1098            {
1099            $i = &skipblanks($i);
1100            next;
1101            }
1102          }
1103
1104        # This option has been renamed
1105
1106        elsif ($name eq "smtp_log_connections")
1107          {
1108          $origname =~ s/smtp_log/log_smtp/;
1109          print STDOUT "# >> Option rewritten by convert4r3\n";
1110          print STDOUT "$i1$origname$i2$origrest\n";
1111          printf STDERR "\n%03d smtp_log_connections renamed as " .
1112            "log_smtp_connections.\n",
1113            ++$count;
1114          }
1115
1116        # smtp_reserve_nets is abolished
1117
1118        elsif ($name eq "smtp_reserve_nets" ||
1119               $name eq "smtp_reserve_hosts")
1120          {
1121          if (!$seen_smtp_reserve)
1122            {
1123            &amalgamate("smtp_reserve_nets", "",
1124              "smtp_reserve_hosts", "smtp_reserve_hosts", "+");
1125            $seen_smtp_reserve = 1;
1126            }
1127          else
1128            {
1129            $i = &skipblanks($i);
1130            next;
1131            }
1132          }
1133
1134        ###########  Driver configurations  ##########
1135
1136        # For aliasfile and forwardfile directors, add file, pipe, and
1137        # reply transports - copying from the globals if they are set.
1138
1139        elsif ($name eq "driver")
1140          {
1141          $driver_type = $rest;
1142          print STDOUT "$i1$origname$i2$origrest\n";
1143          if ($rest eq "aliasfile" || $rest eq "forwardfile")
1144            {
1145            &add_transport("directory");
1146            &add_transport("directory2");
1147            &add_transport("file");
1148            &add_transport("pipe");
1149            &add_transport("reply") if $rest eq "forwardfile";
1150            }
1151          }
1152
1153        # except_domains is abolished; add as negated items to domains.
1154
1155        elsif ($name eq "except_domains" ||
1156               $name eq "domains")
1157          {
1158          if ($seen_domains)              # If already done with these
1159            {                             # omit, and following blanks.
1160            $i = &skipblanks($i);
1161            next;
1162            }
1163          $seen_domains = 1;
1164
1165          if (exists $o{"$prefix$driver.except_domains"})
1166            {
1167            &amalgamate("$prefix$driver.domains",
1168                         "$prefix$driver.except_domains", "",
1169              "domains", "+");
1170            }
1171          else
1172            {
1173            print STDOUT "$i1$origname$i2$origrest\n";
1174            }
1175          }
1176
1177        # except_local_parts is abolished; add as negated items to
1178        # local_parts.
1179
1180        elsif ($name eq "except_local_parts" ||
1181               $name eq "local_parts")
1182          {
1183          if ($seen_local_parts)              # If already done with these
1184            {                             # omit, and following blanks.
1185            $i = &skipblanks($i);
1186            next;
1187            }
1188          $seen_local_parts = 1;
1189
1190          if (exists $o{"$prefix$driver.except_local_parts"})
1191            {
1192            &amalgamate("$prefix$driver.local_parts",
1193                         "$prefix$driver.except_local_parts", "",
1194              "local_parts", "+");
1195            }
1196          else
1197            {
1198            print STDOUT "$i1$origname$i2$origrest\n";
1199            }
1200          }
1201
1202        # except_senders is abolished; add as negated items to senders
1203
1204        elsif ($name eq "except_senders" ||
1205               $name eq "senders")
1206          {
1207          if ($seen_senders)              # If already done with these
1208            {                             # omit, and following blanks.
1209            $i = &skipblanks($i);
1210            next;
1211            }
1212          $seen_senders = 1;
1213
1214          if (exists $o{"$prefix$driver.except_senders"})
1215            {
1216            &amalgamate("$prefix$driver.senders",
1217                         "$prefix$driver.except_senders", "",
1218              "senders", "+");
1219            }
1220          else
1221            {
1222            print STDOUT "$i1$origname$i2$origrest\n";
1223            }
1224          }
1225
1226        # This option has been renamed
1227
1228        elsif ($name eq "directory" && $driver_type eq "aliasfile")
1229          {
1230          $origname =~ s/directory/home_directory/;
1231          print STDOUT "# >> Option rewritten by convert4r3\n";
1232          print STDOUT "$i1$origname$i2$origrest\n";
1233          printf STDERR "\n%03d directory renamed as " .
1234            "home_directory in \"$driver\" director.\n",
1235            ++$count;
1236          }
1237
1238        # This option has been renamed
1239
1240        elsif ($name eq "directory" && $driver_type eq "forwardfile")
1241          {
1242          $origname =~ s/directory/file_directory/;
1243          print STDOUT "# >> Option rewritten by convert4r3\n";
1244          print STDOUT "$i1$origname$i2$origrest\n";
1245          printf STDERR "\n%03d directory renamed as " .
1246            "file_directory in \"$driver\" director.\n",
1247            ++$count;
1248          }
1249
1250        # This option has been renamed
1251
1252        elsif ($name eq "forbid_filter_log" && $driver_type eq "forwardfile")
1253          {
1254          $origname =~ s/log/logwrite/;
1255          print STDOUT "# >> Option rewritten by convert4r3\n";
1256          print STDOUT "$i1$origname$i2$origrest\n";
1257          printf STDERR "\n%03d forbid_filter_log renamed as " .
1258            "forbid_filter_logwrite in \"$driver\" director.\n",
1259            ++$count;
1260          }
1261
1262        # This option has been renamed
1263
1264        elsif ($name eq "directory" && $driver_type eq "localuser")
1265          {
1266          $origname =~ s/directory/match_directory/;
1267          print STDOUT "# >> Option rewritten by convert4r3\n";
1268          print STDOUT "$i1$origname$i2$origrest\n";
1269          printf STDERR "\n%03d directory renamed as " .
1270            "match_directory in \"$driver\" director.\n",
1271            ++$count;
1272          }
1273
1274        # mx_domains_except (and old synonym non_mx_domains) are abolished
1275        # (both lookuphost router and smtp transport)
1276
1277        elsif ($name eq "mx_domains" ||
1278               $name eq "mx_domains_except" ||
1279               $name eq "non_mx_domains")
1280          {
1281          if ($seen_mx_domains)              # If already done with these
1282            {                             # omit, and following blanks.
1283            $i = &skipblanks($i);
1284            next;
1285            }
1286          $seen_mx_domains = 1;
1287
1288          if (exists $o{"$prefix$driver.mx_domains_except"} ||
1289              exists $o{"$prefix$driver.non_mx_domains"})
1290            {
1291            $o{"$prefix$driver.mx_domains_except"} =
1292              &pair("$prefix$driver.mx_domains_except",
1293                    "$prefix$driver.non_mx_domains");
1294
1295            &amalgamate("$prefix$driver.mx_domains",
1296                        "$prefix$driver.mx_domains_except", "",
1297              "mx_domains", "+");
1298            }
1299          else
1300            {
1301            print STDOUT "$i1$origname$i2$origrest\n";
1302            }
1303          }
1304
1305        # This option has been renamed
1306
1307        elsif ($name eq "directory" && $driver_type eq "pipe")
1308          {
1309          $origname =~ s/directory/home_directory/;
1310          print STDOUT "# >> Option rewritten by convert4r3\n";
1311          print STDOUT "$i1$origname$i2$origrest\n";
1312          printf STDERR "\n%03d directory renamed as " .
1313            "home_directory in \"$driver\" director.\n",
1314            ++$count;
1315          }
1316
1317        # serialize_nets is abolished
1318
1319        elsif ($name eq "serialize_nets" ||
1320               $name eq "serialize_hosts")
1321          {
1322          if (!$seen_serialize)
1323            {
1324            &amalgamate("$prefix$driver.serialize_nets", "",
1325              "$prefix$driver.serialize_hosts", "serialize_hosts", "+");
1326            $seen_serialize = 1;
1327            }
1328          else
1329            {
1330            $i = &skipblanks($i);
1331            next;
1332            }
1333          }
1334
1335
1336        # Option not of interest; reproduce verbatim
1337
1338        else
1339          {
1340          print STDOUT "$i1$origname$i2$origrest\n";
1341          }
1342
1343
1344        $last_was_blank = 0;
1345        }
1346      }
1347    }
1348
1349  }
1350
1351# Debugging: show the associative array
1352# foreach $key (sort keys %o) { print STDERR "$key = $o{$key}\n"; }
1353
1354print STDERR "\nEnd of configuration file conversion.\n";
1355print STDERR "\n*******************************************************\n";
1356print STDERR   "***** Please review the generated file carefully. *****\n";
1357print STDERR   "*******************************************************\n\n";
1358
1359print STDERR "In particular:\n\n";
1360
1361print STDERR "(1) If you use regular expressions in any options that have\n";
1362print STDERR "    been rewritten by this script, they might have been put\n";
1363print STDERR "    inside quotes, when then were not previously quoted. This\n";
1364print STDERR "    means that any backslashes in them must now be escaped.\n\n";
1365
1366print STDERR "(2) If your configuration refers to any external files that\n";
1367print STDERR "    contain lists of network addresses, check that the masks\n";
1368print STDERR "    are specified as single numbers, e.g. /24 and NOT as dotted\n";
1369print STDERR "    quads (e.g. 255.255.255.0) because Exim release 3.00 does\n";
1370print STDERR "    not recognize the dotted quad form.\n\n";
1371
1372print STDERR "(3) If your configuration uses macros for lists of domains or\n";
1373print STDERR "    hosts or addresses, check to see if any of the references\n";
1374print STDERR "    have been negated. If so, you will have to rework things,\n";
1375print STDERR "    because the negation will apply only to the first item in\n";
1376print STDERR "    the macro-generated list.\n\n";
1377
1378print STDERR "(4) If you do not generate deliveries to pipes, files, or\n";
1379print STDERR "    auto-replies in your aliasfile and forwardfile directors,\n";
1380print STDERR "    you can remove the added transport settings.\n\n";
1381
1382# End of convert4r3
1383