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