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