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