]> git.proxmox.com Git - pmg-api.git/blame - PMG/Config.pm
add pmg-smtp-filter to service list
[pmg-api.git] / PMG / Config.pm
CommitLineData
7e0e6dbe
DM
1package PMG::Config::Base;
2
3use strict;
4use warnings;
5use Data::Dumper;
6
7use PVE::Tools;
8use PVE::JSONSchema qw(get_standard_option);
9use PVE::SectionConfig;
10
11use base qw(PVE::SectionConfig);
12
13my $defaultData = {
14 propertyList => {
15 type => { description => "Section type." },
ef6f5dd1 16 section => {
7e0e6dbe
DM
17 description => "Secion ID.",
18 type => 'string', format => 'pve-configid',
19 },
20 },
21};
22
23sub private {
24 return $defaultData;
25}
26
27sub format_section_header {
28 my ($class, $type, $sectionId) = @_;
29
30 if ($type eq 'ldap') {
31 $sectionId =~ s/^ldap_//;
32 return "$type: $sectionId\n";
33 } else {
34 return "section: $type\n";
35 }
36}
37
38
39sub parse_section_header {
40 my ($class, $line) = @_;
41
42 if ($line =~ m/^(ldap|section):\s*(\S+)\s*$/) {
43 my ($raw_type, $raw_id) = (lc($1), $2);
44 my $type = $raw_type eq 'section' ? $raw_id : $raw_type;
45 my $section_id = "${raw_type}_${raw_id}";
46 my $errmsg = undef; # set if you want to skip whole section
47 eval { PVE::JSONSchema::pve_verify_configid($raw_id); };
48 $errmsg = $@ if $@;
49 my $config = {}; # to return additional attributes
50 return ($type, $section_id, $errmsg, $config);
51 }
52 return undef;
53}
54
ac5d1312 55package PMG::Config::Admin;
7e0e6dbe
DM
56
57use strict;
58use warnings;
59
60use base qw(PMG::Config::Base);
61
62sub type {
ac5d1312 63 return 'admin';
7e0e6dbe
DM
64}
65
66sub properties {
67 return {
68 dailyreport => {
69 description => "Send daily reports.",
70 type => 'boolean',
71 default => 1,
72 },
f62194b2
DM
73 demo => {
74 description => "Demo mode - do not start SMTP filter.",
75 type => 'boolean',
76 default => 0,
77 },
78 email => {
79 description => "Administrator E-Mail address.",
80 type => 'string', format => 'email',
81 default => 'admin@domain.tld',
ac5d1312
DM
82 },
83 proxyport => {
84 description => "HTTP proxy port.",
85 type => 'integer',
86 minimum => 1,
87 default => 8080,
88 },
89 proxyserver => {
90 description => "HTTP proxy server address.",
91 type => 'string',
92 },
93 proxyuser => {
94 description => "HTTP proxy user name.",
95 type => 'string',
96 },
97 proxypassword => {
98 description => "HTTP proxy password.",
99 type => 'string',
100 },
7e0e6dbe
DM
101 };
102}
103
104sub options {
105 return {
106 dailyreport => { optional => 1 },
f62194b2 107 demo => { optional => 1 },
ac5d1312
DM
108 proxyport => { optional => 1 },
109 proxyserver => { optional => 1 },
110 proxyuser => { optional => 1 },
111 proxypassword => { optional => 1 },
7e0e6dbe
DM
112 };
113}
114
115package PMG::Config::Spam;
116
117use strict;
118use warnings;
119
120use base qw(PMG::Config::Base);
121
122sub type {
123 return 'spam';
124}
125
126sub properties {
127 return {
1ccc8e95
DM
128 languages => {
129 description => "This option is used to specify which languages are considered OK for incoming mail.",
130 type => 'string',
131 pattern => '(all|([a-z][a-z])+( ([a-z][a-z])+)*)',
132 default => 'all',
133 },
134 use_bayes => {
135 description => "Whether to use the naive-Bayesian-style classifier.",
136 type => 'boolean',
137 default => 1,
138 },
582cfacf
DM
139 use_awl => {
140 description => "Use the Auto-Whitelist plugin.",
141 type => 'boolean',
142 default => 1,
143 },
144 use_razor => {
145 description => "Whether to use Razor2, if it is available.",
146 type => 'boolean',
147 default => 1,
148 },
03ac6d8f
DM
149 use_ocr => {
150 description => "Enable OCR to scan pictures.",
151 type => 'boolean',
152 default => 0,
153 },
1ccc8e95
DM
154 wl_bounce_relays => {
155 description => "Whitelist legitimate bounce relays.",
156 type => 'string',
157 },
7e0e6dbe
DM
158 bounce_score => {
159 description => "Additional score for bounce mails.",
160 type => 'integer',
161 minimum => 0,
162 maximum => 1000,
163 default => 0,
164 },
f62194b2
DM
165 rbl_checks => {
166 description => "Enable real time blacklists (RBL) checks.",
167 type => 'boolean',
168 default => 1,
169 },
170 maxspamsize => {
171 description => "Maximum size of spam messages in bytes.",
172 type => 'integer',
4d76e24e 173 minimum => 64,
f62194b2
DM
174 default => 200*1024,
175 },
7e0e6dbe
DM
176 };
177}
178
179sub options {
180 return {
582cfacf
DM
181 use_awl => { optional => 1 },
182 use_razor => { optional => 1 },
03ac6d8f 183 use_ocr => { optional => 1 },
1ccc8e95
DM
184 wl_bounce_relays => { optional => 1 },
185 languages => { optional => 1 },
186 use_bayes => { optional => 1 },
7e0e6dbe 187 bounce_score => { optional => 1 },
f62194b2
DM
188 rbl_checks => { optional => 1 },
189 maxspamsize => { optional => 1 },
190 };
191}
192
193package PMG::Config::ClamAV;
194
195use strict;
196use warnings;
197
198use base qw(PMG::Config::Base);
199
200sub type {
201 return 'clamav';
202}
203
204sub properties {
205 return {
ac5d1312
DM
206 dbmirror => {
207 description => "ClamAV database mirror server.",
208 type => 'string',
209 default => 'database.clamav.net',
210 },
211 archiveblockencrypted => {
212 description => "Wether to block encrypted archives. Mark encrypted archives as viruses.",
213 type => 'boolean',
214 default => 0,
215 },
216 archivemaxrec => {
217 description => "Nested archives are scanned recursively, e.g. if a ZIP archive contains a TAR file, all files within it will also be scanned. This options specifies how deeply the process should be continued. Warning: setting this limit too high may result in severe damage to the system.",
1baec5ab 218 type => 'integer',
ac5d1312
DM
219 minimum => 1,
220 default => 5,
221 },
f62194b2 222 archivemaxfiles => {
ac5d1312 223 description => "Number of files to be scanned within an archive, a document, or any other kind of container. Warning: disabling this limit or setting it too high may result in severe damage to the system.",
f62194b2
DM
224 type => 'integer',
225 minimum => 0,
226 default => 1000,
227 },
ac5d1312
DM
228 archivemaxsize => {
229 description => "Files larger than this limit won't be scanned.",
230 type => 'integer',
231 minimum => 1000000,
232 default => 25000000,
233 },
234 maxscansize => {
235 description => "Sets the maximum amount of data to be scanned for each input file.",
236 type => 'integer',
237 minimum => 1000000,
238 default => 100000000,
239 },
240 maxcccount => {
241 description => "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
242 type => 'integer',
243 minimum => 0,
244 default => 0,
245 },
f62194b2
DM
246 };
247}
248
249sub options {
250 return {
ac5d1312
DM
251 archiveblockencrypted => { optional => 1 },
252 archivemaxrec => { optional => 1 },
f62194b2 253 archivemaxfiles => { optional => 1 },
ac5d1312
DM
254 archivemaxsize => { optional => 1 },
255 maxscansize => { optional => 1 },
256 dbmirror => { optional => 1 },
257 maxcccount => { optional => 1 },
7e0e6dbe
DM
258 };
259}
260
261package PMG::Config::LDAP;
262
263use strict;
264use warnings;
265
266use base qw(PMG::Config::Base);
267
268sub type {
269 return 'ldap';
270}
271
272sub properties {
273 return {
274 mode => {
275 description => "LDAP protocol mode ('ldap' or 'ldaps').",
276 type => 'string',
277 enum => ['ldap', 'ldaps'],
278 default => 'ldap',
279 },
280 };
281}
282
283sub options {
284 return {
285 mode => { optional => 1 },
286 };
287}
f62194b2 288
d9dc3c08
DM
289package PMG::Config::Mail;
290
291use strict;
292use warnings;
293
f62194b2
DM
294use PVE::ProcFSTools;
295
d9dc3c08
DM
296use base qw(PMG::Config::Base);
297
298sub type {
299 return 'mail';
300}
301
f62194b2
DM
302my $physicalmem = 0;
303sub physical_memory {
304
305 return $physicalmem if $physicalmem;
306
307 my $info = PVE::ProcFSTools::read_meminfo();
308 my $total = int($info->{memtotal} / (1024*1024));
309
310 return $total;
311}
312
313sub get_max_filters {
314 # estimate optimal number of filter servers
315
316 my $max_servers = 5;
317 my $servermem = 120;
318 my $memory = physical_memory();
319 my $add_servers = int(($memory - 512)/$servermem);
320 $max_servers += $add_servers if $add_servers > 0;
321 $max_servers = 40 if $max_servers > 40;
322
323 return $max_servers - 2;
324}
325
f609bf7f
DM
326sub get_max_smtpd {
327 # estimate optimal number of smtpd daemons
328
329 my $max_servers = 25;
330 my $servermem = 20;
331 my $memory = physical_memory();
332 my $add_servers = int(($memory - 512)/$servermem);
333 $max_servers += $add_servers if $add_servers > 0;
334 $max_servers = 100 if $max_servers > 100;
335 return $max_servers;
336}
337
338
d9dc3c08
DM
339sub properties {
340 return {
f609bf7f
DM
341 relay => {
342 description => "The default mail delivery transport (incoming mails).",
343 type => 'string',
344 },
345 relayport => {
346 description => "SMTP port number for relay host.",
347 type => 'integer',
348 minimum => 1,
349 maximum => 65535,
350 default => 25,
351 },
352 relaynomx => {
353 description => "Disable MX lookups for default relay.",
354 type => 'boolean',
355 default => 0,
356 },
357 smarthost => {
358 description => "When set, all outgoing mails are deliverd to the specified smarthost.",
359 type => 'string',
360 },
d9dc3c08
DM
361 banner => {
362 description => "ESMTP banner.",
363 type => 'string',
364 maxLength => 1024,
365 default => 'ESMTP Proxmox',
366 },
f62194b2
DM
367 max_filters => {
368 description => "Maximum number of filter processes.",
369 type => 'integer',
370 minimum => 3,
371 maximum => 40,
372 default => get_max_filters(),
373 },
f609bf7f
DM
374 max_smtpd_in => {
375 description => "Maximum number of SMTP daemon processes (in).",
376 type => 'integer',
377 minimum => 3,
378 maximum => 100,
379 default => get_max_smtpd(),
380 },
381 max_smtpd_out => {
382 description => "Maximum number of SMTP daemon processes (out).",
383 type => 'integer',
384 minimum => 3,
385 maximum => 100,
386 default => get_max_smtpd(),
387 },
388 conn_count_limit => {
389 description => "How many simultaneous connections any client is allowed to make to this service. To disable this feature, specify a limit of 0.",
390 type => 'integer',
391 minimum => 0,
392 default => 50,
393 },
394 conn_rate_limit => {
395 description => "The maximal number of connection attempts any client is allowed to make to this service per minute. To disable this feature, specify a limit of 0.",
396 type => 'integer',
397 minimum => 0,
398 default => 0,
399 },
400 message_rate_limit => {
401 description => "The maximal number of message delivery requests that any client is allowed to make to this service per minute.To disable this feature, specify a limit of 0.",
402 type => 'integer',
403 minimum => 0,
404 default => 0,
405 },
f62194b2
DM
406 hide_received => {
407 description => "Hide received header in outgoing mails.",
408 type => 'boolean',
ac5d1312
DM
409 default => 0,
410 },
f609bf7f 411 maxsize => {
ac5d1312
DM
412 description => "Maximum email size. Larger mails are rejected.",
413 type => 'integer',
414 minimum => 1024,
415 default => 1024*1024*10,
f62194b2 416 },
f609bf7f
DM
417 dwarning => {
418 description => "SMTP delay warning time (in hours).",
419 type => 'integer',
420 minimum => 0,
421 default => 4,
422 },
423 use_rbl => {
4d76e24e 424 description => "Use Realtime Blacklists.",
f609bf7f
DM
425 type => 'boolean',
426 default => 1,
427 },
428 tls => {
4d76e24e 429 description => "Use TLS.",
f609bf7f
DM
430 type => 'boolean',
431 default => 0,
432 },
433 spf => {
4d76e24e 434 description => "Use Sender Policy Framework.",
f609bf7f
DM
435 type => 'boolean',
436 default => 1,
437 },
438 greylist => {
4d76e24e 439 description => "Use Greylisting.",
f609bf7f
DM
440 type => 'boolean',
441 default => 1,
442 },
443 helotests => {
4d76e24e 444 description => "Use SMTP HELO tests.",
f609bf7f
DM
445 type => 'boolean',
446 default => 0,
447 },
448 rejectunknown => {
4d76e24e 449 description => "Reject unknown clients.",
f609bf7f
DM
450 type => 'boolean',
451 default => 0,
452 },
453 rejectunknownsender => {
4d76e24e 454 description => "Reject unknown senders.",
f609bf7f
DM
455 type => 'boolean',
456 default => 0,
457 },
458 verifyreceivers => {
459 description => "Enable receiver verification. The value (if greater than 0) spefifies the numerical reply code when the Postfix SMTP server rejects a recipient address (450 or 550).",
460 type => 'integer',
461 minimum => 0,
462 maximum => 599,
463 default => 0,
464 },
465 dnsbl_sites => {
466 description => "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).",
467 type => 'string',
468 },
d9dc3c08
DM
469 };
470}
471
472sub options {
473 return {
f609bf7f
DM
474 relay => { optional => 1 },
475 relayport => { optional => 1 },
476 relaynomx => { optional => 1 },
477 dwarning => { optional => 1 },
478 max_smtpd_in => { optional => 1 },
479 max_smtpd_out => { optional => 1 },
480 greylist => { optional => 1 },
481 helotests => { optional => 1 },
482 use_rbl => { optional => 1 },
483 tls => { optional => 1 },
484 spf => { optional => 1 },
485 maxsize => { optional => 1 },
d9dc3c08 486 banner => { optional => 1 },
f62194b2
DM
487 max_filters => { optional => 1 },
488 hide_received => { optional => 1 },
f609bf7f
DM
489 rejectunknown => { optional => 1 },
490 rejectunknownsender => { optional => 1 },
491 conn_count_limit => { optional => 1 },
492 conn_rate_limit => { optional => 1 },
493 message_rate_limit => { optional => 1 },
494 verifyreceivers => { optional => 1 },
495 dnsbl_sites => { optional => 1 },
d9dc3c08
DM
496 };
497}
7e0e6dbe
DM
498package PMG::Config;
499
500use strict;
501use warnings;
9123cab5 502use IO::File;
7e0e6dbe 503use Data::Dumper;
4ccdc564 504use Template;
7e0e6dbe 505
9123cab5 506use PVE::SafeSyslog;
7e0e6dbe
DM
507use PVE::Tools;
508use PVE::INotify;
509
ac5d1312 510PMG::Config::Admin->register();
d9dc3c08 511PMG::Config::Mail->register();
7e0e6dbe
DM
512PMG::Config::Spam->register();
513PMG::Config::LDAP->register();
f62194b2 514PMG::Config::ClamAV->register();
7e0e6dbe
DM
515
516# initialize all plugins
517PMG::Config::Base->init();
518
f62194b2
DM
519
520sub new {
521 my ($type) = @_;
522
523 my $class = ref($type) || $type;
524
525 my $cfg = PVE::INotify::read_file("pmg.conf");
526
527 return bless $cfg, $class;
528}
529
be6e2db9
DM
530sub write {
531 my ($self) = @_;
532
533 PVE::INotify::write_file("pmg.conf", $self);
534}
535
f21d933c
DM
536my $lockfile = "/var/lock/pmgconfig.lck";
537
538sub lock_config {
539 my ($code, $errmsg) = @_;
540
541 my $p = PVE::Tools::lock_file($lockfile, undef, $code);
542 if (my $err = $@) {
543 $errmsg ? die "$errmsg: $err" : die $err;
544 }
545}
546
062f0498
DM
547# set section values
548# this does not work for ldap entries
549sub set {
550 my ($self, $section, $key, $value) = @_;
551
552 my $pdata = PMG::Config::Base->private();
553
554 die "internal error" if $section eq 'ldap';
555
556 my $plugin = $pdata->{plugins}->{$section};
557 die "no such section '$section'" if !$plugin;
558
559 my $configid = "section_$section";
560 if (defined($value)) {
561 my $tmp = PMG::Config::Base->check_value($section, $key, $value, $section, 0);
562 print Dumper($self->{ids});
563 $self->{ids}->{$configid} = { type => $section } if !defined($self->{ids}->{$configid});
564 $self->{ids}->{$configid}->{$key} = PMG::Config::Base->decode_value($section, $key, $tmp);
565 } else {
566 if (defined($self->{ids}->{$configid})) {
567 delete $self->{ids}->{$configid}->{$key};
568 }
569 }
570
571 return undef;
572}
573
f62194b2
DM
574# get section value or default
575# this does not work for ldap entries
576sub get {
577 my ($self, $section, $key) = @_;
578
579 my $pdata = PMG::Config::Base->private();
580 return undef if !defined($pdata->{options}->{$section});
581 return undef if !defined($pdata->{options}->{$section}->{$key});
582 my $pdesc = $pdata->{propertyList}->{$key};
583 return undef if !defined($pdesc);
584
585 my $configid = "section_$section";
586 if (defined($self->{ids}->{$configid}) &&
587 defined(my $value = $self->{ids}->{$configid}->{$key})) {
588 return $value;
1ccc8e95 589 }
f62194b2
DM
590
591 return $pdesc->{default};
592}
593
1ccc8e95
DM
594# get a whole section with default value
595# this does not work for ldap entries
596sub get_section {
597 my ($self, $section) = @_;
598
599 my $pdata = PMG::Config::Base->private();
600 return undef if !defined($pdata->{options}->{$section});
601
602 my $res = {};
603
604 foreach my $key (keys %{$pdata->{options}->{$section}}) {
605
606 my $pdesc = $pdata->{propertyList}->{$key};
607
608 my $configid = "section_$section";
609 if (defined($self->{ids}->{$configid}) &&
610 defined(my $value = $self->{ids}->{$configid}->{$key})) {
611 $res->{$key} = $value;
612 next;
613 }
614 $res->{$key} = $pdesc->{default};
615 }
616
617 return $res;
618}
619
be16be07
DM
620# get a whole config with default values
621# this does not work for ldap entries
622sub get_config {
623 my ($self) = @_;
624
9dab5fe5
DM
625 my $pdata = PMG::Config::Base->private();
626
be16be07
DM
627 my $res = {};
628
9dab5fe5
DM
629 foreach my $type (keys %{$pdata->{plugins}}) {
630 next if $type eq 'ldap';
631 my $plugin = $pdata->{plugins}->{$type};
632 $res->{$type} = $self->get_section($type);
be16be07
DM
633 }
634
635 return $res;
636}
637
7e0e6dbe
DM
638sub read_pmg_conf {
639 my ($filename, $fh) = @_;
f62194b2 640
7e0e6dbe 641 local $/ = undef; # slurp mode
f62194b2 642
7e0e6dbe
DM
643 my $raw = <$fh>;
644
645 return PMG::Config::Base->parse_config($filename, $raw);
646}
647
648sub write_pmg_conf {
649 my ($filename, $fh, $cfg) = @_;
650
651 my $raw = PMG::Config::Base->write_config($filename, $cfg);
652
653 PVE::Tools::safe_print($filename, $fh, $raw);
654}
655
f62194b2
DM
656PVE::INotify::register_file('pmg.conf', "/etc/proxmox/pmg.conf",
657 \&read_pmg_conf,
7e0e6dbe
DM
658 \&write_pmg_conf);
659
f609bf7f
DM
660# parsers/writers for other files
661
662my $domainsfilename = "/etc/proxmox/domains";
663
664sub read_pmg_domains {
665 my ($filename, $fh) = @_;
666
667 my $domains = [];
668
669 if (defined($fh)) {
670 while (defined(my $line = <$fh>)) {
671 if ($line =~ m/^\s*(\S+)\s*$/) {
672 my $domain = $1;
673 push @$domains, $domain;
674 }
675 }
676 }
677
678 return $domains;
679}
680
681sub write_pmg_domains {
682 my ($filename, $fh, $domain) = @_;
683
684 foreach my $domain (sort @$domain) {
685 PVE::Tools::safe_print($filename, $fh, "$domain\n");
686 }
687}
688
689PVE::INotify::register_file('domains', $domainsfilename,
690 \&read_pmg_domains,
691 \&write_pmg_domains,
692 undef, always_call_parser => 1);
693
3546daf0
DM
694my $transport_map_filename = "/etc/postfix/transport";
695
696sub read_transport_map {
697 my ($filename, $fh) = @_;
698
699 return [] if !defined($fh);
700
701 my $res = {};
702
703 while (defined(my $line = <$fh>)) {
704 chomp $line;
705 next if $line =~ m/^\s*$/;
706 next if $line =~ m/^\s*\#/;
707
708 if ($line =~ m/^(\S+)\s+smtp:([^\s:]+):(\d+)\s*$/) {
709 my $domain = $1;
710 my $host = $2;
711 my $port =$3;
712 my $nomx;
713
714 if ($host =~ m/^\[(.*)\]$/) {
715 $host = $1;
716 $nomx = 1;
717 }
718
719 my $key = "$host:$port";
720
721 $res->{$key}->{nomx} = $nomx;
722 $res->{$key}->{host} = $host;
723 $res->{$key}->{port} = $port;
724 $res->{$key}->{transport} = $key;
725
726 push @{$res->{$key}->{domains}}, $domain;
727 }
728 }
729
730 my $ta = [];
731
732 foreach my $t (sort keys %$res) {
733 push @$ta, $res->{$t};
734 }
735
736 return $ta;
737}
738
739sub write_ransport_map {
740 my ($filename, $fh, $tmap) = @_;
741
742 return if !$tmap;
743
744 foreach my $t (sort { $a->{transport} cmp $b->{transport} } @$tmap) {
745 my $domains = $t->{domains};
746
747 foreach my $d (sort @$domains) {
748 if ($t->{nomx}) {
749 PVE::Tools::safe_print($filename, $fh, "$d smtp:[$t->{host}]:$t->{port}\n");
750 } else {
751 PVE::Tools::safe_print($filename, $fh, "$d smtp:$t->{host}:$t->{port}\n");
752 }
753 }
754 }
755}
756
757PVE::INotify::register_file('transport', $transport_map_filename,
758 \&read_transport_map,
759 \&write_ransport_map,
760 undef, always_call_parser => 1);
7e0e6dbe 761
4ccdc564
DM
762# config file generation using templates
763
07b3face
DM
764sub get_template_vars {
765 my ($self) = @_;
4ccdc564
DM
766
767 my $vars = { pmg => $self->get_config() };
768
f609bf7f
DM
769 my $nodename = PVE::INotify::nodename();
770 my $int_ip = PMG::Cluster::remote_node_ip($nodename);
771 my $int_net_cidr = PMG::Utils::find_local_network_for_ip($int_ip);
772
773 $vars->{ipconfig}->{int_ip} = $int_ip;
774 # $vars->{ipconfig}->{int_net_cidr} = $int_net_cidr;
775 $vars->{ipconfig}->{int_port} = 26;
776 $vars->{ipconfig}->{ext_port} = 25;
777
778 my $transportnets = []; # fixme
779 $vars->{postfix}->{transportnets} = join(' ', @$transportnets);
780
781 my $mynetworks = [ '127.0.0.0/8', '[::1]/128' ];
782 push @$mynetworks, @$transportnets;
783 push @$mynetworks, $int_net_cidr;
784
785 # add default relay to mynetworks
786 if (my $relay = $self->get('mail', 'relay')) {
787 if (Net::IP::ip_is_ipv4($relay)) {
788 push @$mynetworks, "$relay/32";
789 } elsif (Net::IP::ip_is_ipv6($relay)) {
790 push @$mynetworks, "[$relay]/128";
791 } else {
792 warn "unable to detect IP version of relay '$relay'";
793 }
794 }
795
796 $vars->{postfix}->{mynetworks} = join(' ', @$mynetworks);
797
798 my $usepolicy = 0;
799 $usepolicy = 1 if $self->get('mail', 'greylist') ||
800 $self->get('mail', 'spf') || $self->get('mail', 'use_rbl');
801 $vars->{postfix}->{usepolicy} = $usepolicy;
802
803 my $resolv = PVE::INotify::read_file('resolvconf');
804 $vars->{dns}->{hostname} = $nodename;
805 $vars->{dns}->{domain} = $resolv->{search};
806
07b3face
DM
807 return $vars;
808}
809
810# rewrite file from template
811# return true if file has changed
812sub rewrite_config_file {
813 my ($self, $tmplname, $dstfn) = @_;
814
815 my $demo = $self->get('admin', 'demo');
816
817 my $srcfn = ($tmplname =~ m|^.?/|) ?
818 $tmplname : "/var/lib/pmg/templates/$tmplname";
819
820 if ($demo) {
821 my $demosrc = "$srcfn.demo";
822 $srcfn = $demosrc if -f $demosrc;
823 }
824
825 my ($perm, $uid, $gui);
826
827 my $srcfd = IO::File->new ($srcfn, "r")
828 || die "cant read template '$srcfn' - $!: ERROR";
829
830 if ($dstfn eq '/etc/fetchmailrc') {
831 (undef, undef, $uid, $gid) = getpwnam('fetchmail');
832 $perm = 0600;
833 } elsif ($dstfn eq '/etc/clamav/freshclam.conf') {
834 # needed if file contains a HTTPProxyPasswort
835
836 $uid = getpwnam('clamav');
837 $gid = getgrnam('adm');
838 $perm = 0600;
839 }
840
841 my $template = Template->new({});
842
843 my $vars = $self->get_template_vars();
844
845 my $output = '':
846
847 $template->process($srcfd, $vars, \$output) ||
4ccdc564
DM
848 die $template->error();
849
850 $srcfd->close();
07b3face
DM
851
852 my $old = PVE::Tools::file_get_contents($dstfn, 128*1024) if -f $dstfn;
853
854 return 0 if defined($old) && ($old eq $output); # no change
855
856 PVE::Tools::file_set_contents($dstfn, $output, $perm);
857
858 if (defined($uid) && defined($gid)) {
859 chown($uid, $gid, $dstfn);
860 }
861
862 return 1;
4ccdc564
DM
863}
864
9123cab5
DM
865# rewrite spam configuration
866sub rewrite_config_spam {
867 my ($self) = @_;
868
869 my $use_awl = $self->get('spam', 'use_awl');
870 my $use_bayes = $self->get('spam', 'use_bayes');
871 my $use_razor = $self->get('spam', 'use_razor');
872
17424665
DM
873 my $changes = 0;
874
9123cab5 875 # delete AW and bayes databases if those features are disabled
17424665
DM
876 if (!$use_awl) {
877 $changes = 1 if unlink '/root/.spamassassin/auto-whitelist';
878 }
879
9123cab5 880 if (!$use_bayes) {
17424665
DM
881 $changes = 1 if unlink '/root/.spamassassin/bayes_journal';
882 $changes = 1 if unlink '/root/.spamassassin/bayes_seen';
883 $changes = 1 if unlink '/root/.spamassassin/bayes_toks';
9123cab5
DM
884 }
885
886 # make sure we have a custom.cf file (else cluster sync fails)
887 IO::File->new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
888
17424665
DM
889 $changes = 1 if $self->rewrite_config_file(
890 'local.cf.in', '/etc/mail/spamassassin/local.cf');
891
892 $changes = 1 if $self->rewrite_config_file(
893 'init.pre.in', '/etc/mail/spamassassin/init.pre');
894
895 $changes = 1 if $self->rewrite_config_file(
896 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
897
898 $changes = 1 if $self->rewrite_config_file(
899 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
9123cab5
DM
900
901 if ($use_razor) {
902 mkdir "/root/.razor";
17424665
DM
903
904 $changes = 1 if $self->rewrite_config_file(
905 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
906
9123cab5
DM
907 if (! -e '/root/.razor/identity') {
908 eval {
909 my $timeout = 30;
17424665
DM
910 PVE::Tools::run_command(['razor-admin', '-discover'], timeout => $timeout);
911 PVE::Tools::run_command(['razor-admin', '-register'], timeout => $timeout);
9123cab5
DM
912 };
913 my $err = $@;
914 syslog('info', msgquote ("registering razor failed: $err")) if $err;
915 }
916 }
17424665
DM
917
918 return $changes;
9123cab5
DM
919}
920
ac5d1312
DM
921# rewrite ClamAV configuration
922sub rewrite_config_clam {
923 my ($self) = @_;
924
17424665
DM
925 return $self->rewrite_config_file(
926 'clamd.conf.in', '/etc/clamav/clamd.conf');
927}
928
929sub rewrite_config_freshclam {
930 my ($self) = @_;
931
932 return $self->rewrite_config_file(
933 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
ac5d1312
DM
934}
935
86737f12
DM
936sub rewrite_config_postgres {
937 my ($self) = @_;
938
939 my $pgconfdir = "/etc/postgresql/9.6/main";
940
17424665
DM
941 my $changes = 0;
942
943 $changes = 1 if $self->rewrite_config_file(
944 'pg_hba.conf.in', "$pgconfdir/pg_hba.conf");
945
946 $changes = 1 if $self->rewrite_config_file(
947 'postgresql.conf.in', "$pgconfdir/postgresql.conf");
948
949 return $changes;
86737f12
DM
950}
951
952# rewrite /root/.forward
953sub rewrite_dot_forward {
954 my ($self) = @_;
955
956 my $fname = '/root/.forward';
957
958 my $email = $self->get('administration', 'email');
959 open(TMP, ">$fname");
960 if ($email && $email =~ m/\s*(\S+)\s*/) {
961 print (TMP "$1\n");
962 } else {
963 # empty .forward does not forward mails (see man local)
964 }
965 close (TMP);
17424665
DM
966
967 return undef;
86737f12
DM
968}
969
f609bf7f
DM
970# rewrite /etc/postfix/*
971sub rewrite_config_postfix {
972 my ($self) = @_;
973
3546daf0 974 # make sure we have required files (else postfix start fails)
f609bf7f 975 IO::File->new($domainsfilename, 'a', 0644);
3546daf0 976 IO::File->new($transport_map_filename, 'a', 0644);
f609bf7f 977
17424665
DM
978 my $changes = 0;
979
f609bf7f
DM
980 if ($self->get('mail', 'tls')) {
981 eval {
982 my $resolv = PVE::INotify::read_file('resolvconf');
983 my $domain = $resolv->{search};
984
985 my $company = $domain; # what else ?
986 my $cn = "*.$domain";
987 PMG::Utils::gen_proxmox_tls_cert(0, $company, $cn);
988 };
989 syslog ('info', msgquote ("generating certificate failed: $@")) if $@;
990 }
991
17424665
DM
992 $changes = 1 if $self->rewrite_config_file(
993 'main.cf.in', '/etc/postfix/main.cf');
994
995 $changes = 1 if $self->rewrite_config_file(
996 'master.cf.in', '/etc/postfix/master.cf');
997
f609bf7f
DM
998 #rewrite_config_transports ($class);
999 #rewrite_config_whitelist ($class);
1000 #rewrite_config_tls_policy ($class);
1001
1002 # make sure aliases.db is up to date
1003 system('/usr/bin/newaliases');
17424665
DM
1004
1005 return $changes;
f609bf7f
DM
1006}
1007
f983300f
DM
1008sub rewrite_config {
1009 my ($self) = @_;
1010
17424665 1011 $self->rewrite_config_postfix();
86737f12
DM
1012 $self->rewrite_dot_forward();
1013 $self->rewrite_config_postgres();
f983300f
DM
1014 $self->rewrite_config_spam();
1015 $self->rewrite_config_clam();
17424665
DM
1016 $self->rewrite_config_freshclam();
1017
f983300f
DM
1018}
1019
7e0e6dbe 10201;